Mercurial > lbo > hg > geohub
changeset 34:9966460e2930
Encapsulate GeoJSON and parse_flexible_timestamp
author | Lewin Bormann <lbo@spheniscida.de> |
---|---|
date | Thu, 03 Dec 2020 07:51:38 +0100 |
parents | 150e76fd3d0e |
children | 097f1c1c5f2b |
files | src/db.rs src/main.rs src/types.rs src/util.rs |
diffstat | 4 files changed, 69 insertions(+), 64 deletions(-) [+] |
line wrap: on
line diff
--- a/src/db.rs Thu Dec 03 00:05:18 2020 +0100 +++ b/src/db.rs Thu Dec 03 07:51:38 2020 +0100 @@ -8,10 +8,7 @@ last: &Option<i32>, limit: &Option<i64>, ) -> Option<(types::GeoJSON, i32)> { - let mut returnable = types::GeoJSON { - typ: "FeatureCollection".into(), - features: vec![], - }; + let mut returnable = types::GeoJSON::new(); let check_for_new = db.prepare_cached( r"SELECT id, t, lat, long, spd, ele FROM geohub.geodata WHERE (client = $1) and (id > $2) AND (secret = public.digest($3, 'sha256') or secret is null) @@ -25,7 +22,7 @@ if let Ok(rows) = rows { // If there are unknown entries, return those. if rows.len() > 0 { - returnable.features = Vec::with_capacity(rows.len()); + returnable.reserve_features(rows.len()); let mut last = 0; for row in rows.iter() { @@ -37,9 +34,7 @@ row.get(4), row.get(5), ); - returnable - .features - .push(types::geofeature_from_row(ts, lat, long, spd, ele)); + returnable.push_feature(types::geofeature_from_row(ts, lat, long, spd, ele)); if id > last { last = id; }
--- a/src/main.rs Thu Dec 03 00:05:18 2020 +0100 +++ b/src/main.rs Thu Dec 03 07:51:38 2020 +0100 @@ -4,6 +4,7 @@ mod ids; mod notifier; mod types; +mod util; use std::sync::{mpsc, Arc, Mutex}; use std::time; @@ -11,34 +12,9 @@ use postgres; use rocket; -use chrono::TimeZone; - #[rocket_contrib::database("geohub")] struct DBConn(postgres::Connection); -/// Parse timestamps flexibly. Without any zone information, UTC is assumed. -fn flexible_timestamp_parse(ts: String) -> Option<chrono::DateTime<chrono::Utc>> { - let fmtstrings = &[ - "%Y-%m-%dT%H:%M:%S%.f%:z", - "%Y-%m-%dT%H:%M:%S%.fZ", - "%Y-%m-%d %H:%M:%S%.f", - ]; - for fs in fmtstrings { - let (naive, withtz) = ( - chrono::NaiveDateTime::parse_from_str(ts.as_str(), fs).ok(), - chrono::DateTime::parse_from_str(ts.as_str(), fs).ok(), - ); - if let Some(p) = withtz { - return Some(p.with_timezone(&chrono::Utc)); - } - if let Some(p) = naive { - let utcd = chrono::Utc.from_utc_datetime(&p); - return Some(utcd); - } - } - None -} - /// Almost like retrieve/json, but sorts in descending order and doesn't work with intervals (only /// limit). Used for backfilling recent points in the UI. #[rocket::get("/geo/<name>/retrieve/last?<secret>&<last>&<limit>")] @@ -137,22 +113,18 @@ to: Option<String>, limit: Option<i64>, ) -> rocket_contrib::json::Json<types::GeoJSON> { - let mut returnable = types::GeoJSON { - typ: "FeatureCollection".into(), - features: vec![], - }; - - let from_ts = from - .and_then(flexible_timestamp_parse) - .unwrap_or(chrono::DateTime::from_utc( - chrono::NaiveDateTime::from_timestamp(0, 0), - chrono::Utc, - )); + let from_ts = + from.and_then(util::flexible_timestamp_parse) + .unwrap_or(chrono::DateTime::from_utc( + chrono::NaiveDateTime::from_timestamp(0, 0), + chrono::Utc, + )); let to_ts = to - .and_then(flexible_timestamp_parse) + .and_then(util::flexible_timestamp_parse) .unwrap_or(chrono::Utc::now()); let limit = limit.unwrap_or(16384); + let mut returnable = types::GeoJSON::new(); let stmt = db.0.prepare_cached( r"SELECT t, lat, long, spd, ele FROM geohub.geodata WHERE (client = $1) and (t between $2 and $3) AND (secret = public.digest($4, 'sha256') or secret is null) @@ -160,13 +132,11 @@ LIMIT $5").unwrap(); // Must succeed. let rows = stmt.query(&[&name, &from_ts, &to_ts, &secret, &limit]); if let Ok(rows) = rows { - returnable.features = Vec::with_capacity(rows.len()); + returnable.reserve_features(rows.len()); for row in rows.iter() { let (ts, lat, long, spd, ele) = (row.get(0), row.get(1), row.get(2), row.get(3), row.get(4)); - returnable - .features - .push(types::geofeature_from_row(ts, lat, long, spd, ele)); + returnable.push_feature(types::geofeature_from_row(ts, lat, long, spd, ele)); } } @@ -194,7 +164,7 @@ } let mut ts = chrono::Utc::now(); if let Some(time) = time { - ts = flexible_timestamp_parse(time).unwrap_or(ts); + ts = util::flexible_timestamp_parse(time).unwrap_or(ts); } let stmt = db.0.prepare_cached("INSERT INTO geohub.geodata (client, lat, long, spd, t, ele, secret) VALUES ($1, $2, $3, $4, $5, $6, public.digest($7, 'sha256'))").unwrap(); let channel = format!(
--- a/src/types.rs Thu Dec 03 00:05:18 2020 +0100 +++ b/src/types.rs Thu Dec 03 07:51:38 2020 +0100 @@ -3,24 +3,43 @@ /// #[derive(serde::Serialize, Debug, Clone)] pub struct GeoProperties { - pub time: chrono::DateTime<chrono::Utc>, - pub altitude: Option<f64>, - pub speed: Option<f64>, + time: chrono::DateTime<chrono::Utc>, + altitude: Option<f64>, + speed: Option<f64>, } #[derive(serde::Serialize, Debug, Clone)] pub struct GeoGeometry { #[serde(rename = "type")] - pub typ: String, // always "Point" - pub coordinates: Vec<f64>, // always [long, lat] + typ: String, // always "Point" + coordinates: Vec<f64>, // always [long, lat] } #[derive(serde::Serialize, Debug, Clone)] pub struct GeoFeature { #[serde(rename = "type")] - pub typ: String, // always "Feature" - pub properties: GeoProperties, - pub geometry: GeoGeometry, + typ: String, // always "Feature" + properties: GeoProperties, + geometry: GeoGeometry, +} + +#[derive(serde::Serialize, Debug, Clone)] +pub struct GeoJSON { + #[serde(rename = "type")] + typ: String, // always "FeatureCollection" + features: Vec<GeoFeature>, +} + +impl GeoJSON { + pub fn new() -> GeoJSON { + GeoJSON { typ: "FeatureCollection".into(), features: vec![] } + } + pub fn reserve_features(&mut self, cap: usize) { + self.features.reserve(cap); + } + pub fn push_feature(&mut self, feat: GeoFeature) { + self.features.push(feat); + } } pub fn geofeature_from_row( @@ -44,9 +63,3 @@ } } -#[derive(serde::Serialize, Debug, Clone)] -pub struct GeoJSON { - #[serde(rename = "type")] - pub typ: String, // always "FeatureCollection" - pub features: Vec<GeoFeature>, -}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/util.rs Thu Dec 03 07:51:38 2020 +0100 @@ -0,0 +1,27 @@ +use chrono; + +use chrono::TimeZone; + +/// Parse timestamps flexibly. Without any zone information, UTC is assumed. +pub fn flexible_timestamp_parse(ts: String) -> Option<chrono::DateTime<chrono::Utc>> { + let fmtstrings = &[ + "%Y-%m-%dT%H:%M:%S%.f%:z", + "%Y-%m-%dT%H:%M:%S%.fZ", + "%Y-%m-%d %H:%M:%S%.f", + ]; + for fs in fmtstrings { + let (naive, withtz) = ( + chrono::NaiveDateTime::parse_from_str(ts.as_str(), fs).ok(), + chrono::DateTime::parse_from_str(ts.as_str(), fs).ok(), + ); + if let Some(p) = withtz { + return Some(p.with_timezone(&chrono::Utc)); + } + if let Some(p) = naive { + let utcd = chrono::Utc.from_utc_datetime(&p); + return Some(utcd); + } + } + None +} +