diff options
-rw-r--r-- | Cargo.toml | 1 | ||||
-rw-r--r-- | src/stream.rs | 2 | ||||
-rw-r--r-- | src/stream/datetime.rs | 93 | ||||
-rw-r--r-- | src/stream/streamitem.rs | 11 |
4 files changed, 107 insertions, 0 deletions
@@ -21,6 +21,7 @@ authors = ["haraldei"] edition = "2021" [dependencies] +chrono = "0.4.0" clap = "2.33.0" dotenv = "0.15" reqwest = { version = "0.11", features = ["default-tls", "multipart"] } diff --git a/src/stream.rs b/src/stream.rs index 438ef0f..0348822 100644 --- a/src/stream.rs +++ b/src/stream.rs @@ -11,9 +11,11 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ +mod datetime; mod streamitem; mod verb; +pub use datetime::DateTime; pub use streamitem::{ StreamItem, StreamItemEncoding, diff --git a/src/stream/datetime.rs b/src/stream/datetime.rs new file mode 100644 index 0000000..8b67e78 --- /dev/null +++ b/src/stream/datetime.rs @@ -0,0 +1,93 @@ +/** + * Date and time representation in the stream. + * + * SPDX-FileCopyrightText: 2023 Eilertsens Kodeknekkeri + * SPDX-FileCopyrightText: 2023 Harald Eilertsen <haraldei@anduin.net> + * + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +use chrono::NaiveDateTime; +use serde::Deserialize; + +/** + * Handle date and time. + * + * This is mainly a wrapper around the chrono::NaiveDateTime struct, + * as the date and time returned by the json data from the zot stream + * API's does not contain any timezone information. + */ +#[derive(Debug, PartialEq)] +pub struct DateTime { + datetime: Option<NaiveDateTime> +} + +/* + * Implement Display for the DateTime type, so that it will display + * using the correct time and date format by default. + */ +impl std::fmt::Display for DateTime { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self.datetime { + Some(dt) => write!(f, "{}", dt.format("%Y-%m-%d %H:%M:%S")), + None => write!(f, "0000-00-00 00:00:00"), + } + } +} + +/* + * Since the date and time format returned in the json from the + * stream API, does not conform to the RFC3339 format expected by + * the default chrono deserializer, we have to implement our own + * deserializer. + */ +impl<'de> Deserialize<'de> for DateTime { + fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> + where + D: serde::de::Deserializer<'de>, + { + Ok(deserializer.deserialize_str(DateTimeVisitor)?) + } +} + +struct DateTimeVisitor; + +impl<'de> serde::de::Visitor<'de> for DateTimeVisitor { + type Value = DateTime; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(formatter, "a date and time formatted string") + } + + fn visit_str<E>(self, s: &str) -> Result<Self::Value, E> + where + E: serde::de::Error, + { + let datetime = if s == "0000-00-00 00:00:00" { + None + } else { + Some(NaiveDateTime::parse_from_str(s, "%Y-%m-%d %H:%M:%S") + .map_err(|_| E::custom(format!("invalid date time format: {}", s)))?) + }; + + Ok(Self::Value{ datetime }) + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_deserialize_datetime() { + let tests = vec![ + r#""0000-00-00 00:00:00""#, + r#""2016-12-17 13:37:00""#, + ]; + + for test in tests { + let datetime: DateTime = serde_json::from_str(test).unwrap(); + assert_eq!(test.trim_matches('"'), datetime.to_string()); + } + } +} diff --git a/src/stream/streamitem.rs b/src/stream/streamitem.rs index 1934dfe..514cae3 100644 --- a/src/stream/streamitem.rs +++ b/src/stream/streamitem.rs @@ -7,6 +7,7 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ +use super::datetime::DateTime; use super::verb::Verb; use serde::Deserialize; @@ -32,6 +33,16 @@ pub struct StreamItem { pub item_type: StreamItemType, pub encoding: StreamItemEncoding, + /* + * Date and time fields for when the item was + * created, last edited or commented, as well as + * when/if it expires. + */ + pub created: DateTime, + pub edited: DateTime, + pub expires: DateTime, + pub commented: DateTime, + pub verb: Verb, pub title: String, |