From affa986bf1f84b725bd23309986250ff04cf2c93 Mon Sep 17 00:00:00 2001 From: rtkay123 Date: Sat, 9 Aug 2025 10:36:07 +0200 Subject: feat: data cache --- lib/warden-core/src/google/parser/dt.rs | 233 ++++++++++++++++++++++++++++++++ 1 file changed, 233 insertions(+) create mode 100644 lib/warden-core/src/google/parser/dt.rs (limited to 'lib/warden-core/src/google/parser/dt.rs') diff --git a/lib/warden-core/src/google/parser/dt.rs b/lib/warden-core/src/google/parser/dt.rs new file mode 100644 index 0000000..ced6f12 --- /dev/null +++ b/lib/warden-core/src/google/parser/dt.rs @@ -0,0 +1,233 @@ +use crate::google::{protobuf::Timestamp, r#type::Date}; + +impl From for Date { + fn from(dt: time::OffsetDateTime) -> Self { + Self { + year: dt.year(), + month: dt.month() as i32, + day: dt.day() as i32, + } + } +} + +impl From for Date { + fn from(value: time::Date) -> Self { + Self { + year: value.year(), + month: value.month() as i32, + day: value.day() as i32, + } + } +} + +impl TryFrom for time::Date { + type Error = time::Error; + + fn try_from(value: Date) -> Result { + Ok(Self::from_calendar_date( + value.year, + time::Month::try_from(value.month as u8)?, + value.day as u8, + )?) + } +} + +impl std::str::FromStr for Date { + type Err = time::Error; + + fn from_str(s: &str) -> Result { + let date = time::OffsetDateTime::parse(s, &time::format_description::well_known::Rfc3339) + .map(Date::from); + + match date { + Ok(dt) => Ok(dt), + Err(_e) => { + let my_format = time::macros::format_description!("[year]-[month]-[day]"); + let date = time::Date::parse(s, &my_format)?; + Ok(Date::from(date)) + } + } + } +} + +impl TryFrom for Date { + type Error = time::Error; + + fn try_from(value: String) -> Result { + ::from_str(&value) + } +} + +impl TryFrom for Date { + type Error = time::Error; + + fn try_from(value: DateItem) -> Result { + match value { + DateItem::String(ref string) => ::from_str(string), + #[cfg(feature = "iso20022")] + DateItem::Date { year, month, day } => Ok(Date { year, month, day }), + DateItem::Timestamp { seconds, nanos } => { + let odt = time::OffsetDateTime::try_from(crate::google::protobuf::Timestamp { + seconds, + nanos, + })?; + Ok(Self { + year: odt.year(), + month: odt.month() as i32, + day: odt.day() as i32, + }) + } + } + } +} + +impl From for String { + fn from(value: Date) -> Self { + let prepend = |value: i32| -> String { + match value.lt(&10) { + true => format!("0{}", value), + false => value.to_string(), + } + }; + format!( + "{}-{}-{}", + value.year, + prepend(value.month), + prepend(value.day), + ) + } +} + +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", serde(untagged))] +/// Date utility +#[derive(Clone, Debug)] +pub enum DateItem { + /// string + String(String), + /// ts + Timestamp { seconds: i64, nanos: i32 }, + /// date + #[cfg(feature = "iso20022")] + Date { year: i32, month: i32, day: i32 }, +} + +impl TryFrom for Timestamp { + type Error = time::Error; + + fn try_from(value: DateItem) -> Result { + match value { + DateItem::String(ref string) => ::from_str(string), + #[cfg(feature = "iso20022")] + DateItem::Date { year, month, day } => { + let date = time::Date::try_from(crate::google::r#type::Date { year, month, day })?; + let time = time::Time::MIDNIGHT; + let offset = time::UtcOffset::UTC; + Ok(date.with_time(time).assume_offset(offset).into()) + } + DateItem::Timestamp { seconds, nanos } => Ok(Self { seconds, nanos }), + } + } +} + +impl From for Timestamp { + fn from(dt: time::OffsetDateTime) -> Self { + Timestamp { + seconds: dt.unix_timestamp(), + nanos: dt.nanosecond() as i32, + } + } +} + +impl From for String { + fn from(value: Timestamp) -> Self { + let odt = time::OffsetDateTime::try_from(value).expect("invalid date"); + odt.format(&time::format_description::well_known::Rfc3339) + .expect("format is not rfc3339") + } +} + +impl std::str::FromStr for Timestamp { + type Err = time::Error; + + fn from_str(s: &str) -> Result { + let timestamp = + time::OffsetDateTime::parse(s, &time::format_description::well_known::Rfc3339)?; + + Ok(Timestamp::from(timestamp)) + } +} + +impl TryFrom for Timestamp { + type Error = time::Error; + + fn try_from(value: String) -> Result { + ::from_str(&value) + } +} + +impl TryFrom for time::OffsetDateTime { + type Error = time::Error; + + fn try_from(value: Timestamp) -> Result { + let dt = time::OffsetDateTime::from_unix_timestamp(value.seconds)?; + + Ok(dt.replace_nanosecond(value.nanos as u32)?) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use time::{Duration, OffsetDateTime}; + + #[test] + fn test_offsetdatetime_to_timestamp() { + let now = OffsetDateTime::now_utc(); + let timestamp: Timestamp = now.into(); + + assert_eq!(timestamp.seconds, now.unix_timestamp()); + assert_eq!(timestamp.nanos, now.nanosecond() as i32); + } + + #[test] + fn test_timestamp_to_offsetdatetime() { + let now = OffsetDateTime::now_utc(); + let timestamp: Timestamp = now.into(); + let dt: OffsetDateTime = timestamp.try_into().unwrap(); + + assert_eq!(dt, now); + } + + #[test] + fn test_timestamp_to_offsetdatetime_with_nanos() { + let now = OffsetDateTime::now_utc(); + let nanos = 123456789; + let dt = now + Duration::nanoseconds(nanos); + let timestamp: Timestamp = dt.into(); + let dt_from_timestamp: OffsetDateTime = timestamp.try_into().unwrap(); + + assert_eq!(dt_from_timestamp, dt); + } + + #[test] + fn test_timestamp_to_offsetdatetime_with_negative_nanos() { + let now = OffsetDateTime::now_utc(); + let nanos = -123456789; + let dt = now + Duration::nanoseconds(nanos); + let timestamp: Timestamp = dt.into(); + let dt_from_timestamp: OffsetDateTime = timestamp.try_into().unwrap(); + + assert_eq!(dt_from_timestamp, dt); + } + + #[test] + fn test_timestamp_to_offsetdatetime_invalid_seconds() { + let timestamp = Timestamp { + seconds: i64::MIN, + nanos: 0, + }; + let result: Result = timestamp.try_into(); + assert!(result.is_err()); + } +} -- cgit v1.2.3