did_toolkit/
time.rs

1use anyhow::anyhow;
2use serde::{de::Visitor, Deserialize, Serialize};
3use std::fmt::Display;
4use time::{
5    format_description::FormatItem, macros::format_description, OffsetDateTime, PrimitiveDateTime,
6};
7
8static VERSION_TIME_FORMAT: &[FormatItem<'static>] =
9    format_description!("[year]-[month]-[day]T[hour]:[minute]:[second]Z");
10
11/// VersionTime is a specific section of the query string in DID [crate::url::URL]s. It is required to be a
12/// certain format, based in UTC. See <https://www.w3.org/TR/did-core/#did-parameters> for more
13/// information. Formatting is provided by the [time] crate.
14#[derive(Clone, Debug, Hash, PartialOrd, Ord, Eq, PartialEq)]
15pub struct VersionTime(pub OffsetDateTime);
16
17impl Default for VersionTime {
18    fn default() -> Self {
19        VersionTime(OffsetDateTime::from_unix_timestamp(0).unwrap())
20    }
21}
22
23impl Display for VersionTime {
24    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
25        f.write_str(&format!(
26            "{}-{:02}-{:02}T{:02}:{:02}:{:02}Z",
27            self.0.year(),
28            u8::from(self.0.month()),
29            self.0.day(),
30            self.0.hour(),
31            self.0.minute(),
32            self.0.second()
33        ))
34    }
35}
36
37impl Serialize for VersionTime {
38    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
39    where
40        S: serde::Serializer,
41    {
42        serializer.serialize_str(&self.to_string())
43    }
44}
45
46impl Visitor<'_> for VersionTime {
47    type Value = VersionTime;
48
49    fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
50        formatter.write_str("Expecting a datetime in DID specification format")
51    }
52
53    fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
54    where
55        E: serde::de::Error,
56    {
57        match Self::parse(v) {
58            Ok(v) => Ok(v),
59            Err(e) => Err(E::custom(e)),
60        }
61    }
62}
63
64impl<'de> Deserialize<'de> for VersionTime {
65    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
66    where
67        D: serde::Deserializer<'de>,
68    {
69        deserializer.deserialize_str::<VersionTime>(Default::default())
70    }
71}
72
73impl VersionTime {
74    /// Parse a [VersionTime] from string.
75    pub fn parse(s: &str) -> Result<Self, anyhow::Error> {
76        match PrimitiveDateTime::parse(s, VERSION_TIME_FORMAT) {
77            Ok(dt) => Ok(VersionTime(dt.assume_utc())),
78            Err(e) => Err(anyhow!(e)),
79        }
80    }
81}