Skip to main content

rudof_rdf/rdf_core/term/literal/
xsd_datetime.rs

1use core::fmt;
2use oxsdatatypes::DateTime;
3use serde::de::{self, Visitor};
4use serde::{Deserialize, Deserializer, Serialize, Serializer};
5use std::str::FromStr;
6use thiserror::Error;
7
8/// A validated XSD DateTime wrapper that ensures type safety and correct formatting.
9///
10/// This type wraps `oxsdatatypes::DateTime` and provides a type-safe way to work
11/// with XSD DateTime values (e.g., "2026-01-20T12:34:56Z"). The datetime string
12/// is validated upon construction, ensuring it can be parsed into a proper `DateTime`.
13#[derive(Debug, PartialEq, Eq, Clone, PartialOrd)]
14pub struct XsdDateTime {
15    value: DateTime,
16}
17
18impl XsdDateTime {
19    /// Creates a new `XsdDateTime` from a string slice.
20    ///
21    /// # Errors
22    ///
23    /// Returns `XsdDateTimeParseError` if the string cannot be parsed as a valid XSD DateTime.
24    pub fn new(value: &str) -> Result<Self, XsdDateTimeParseError> {
25        DateTime::from_str(value)
26            .map(|dt| Self { value: dt })
27            .map_err(XsdDateTimeParseError::InvalidDateTime)
28    }
29
30    /// Returns a reference to the underlying `DateTime` value.
31    #[inline]
32    pub fn value(&self) -> &DateTime {
33        &self.value
34    }
35
36    /// Consumes self and returns the underlying `DateTime` value.
37    #[inline]
38    pub fn into_inner(self) -> DateTime {
39        self.value
40    }
41}
42
43impl std::hash::Hash for XsdDateTime {
44    /// Serializes the `XsdDateTime` as its string lexical representation.
45    ///
46    /// This uses `DateTime`'s `Display` implementation via `to_string()` and
47    /// encodes it as a JSON string (or equivalent, depending on the serializer).
48    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
49        self.value.hash(state);
50    }
51}
52
53impl Serialize for XsdDateTime {
54    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
55    where
56        S: Serializer,
57    {
58        serializer.serialize_str(&self.value.to_string())
59    }
60}
61
62impl<'de> Deserialize<'de> for XsdDateTime {
63    /// Deserializes an `XsdDateTime` from a string.
64    ///
65    /// The input is expected to be a string containing a valid XSD `dateTime`
66    /// lexical value. Any parsing error is converted into a serde
67    /// deserialization error via `de::Error::custom`.
68    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
69    where
70        D: Deserializer<'de>,
71    {
72        struct XsdDateTimeVisitor;
73
74        impl<'de> Visitor<'de> for XsdDateTimeVisitor {
75            type Value = XsdDateTime;
76
77            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
78                formatter.write_str("a valid XSD DateTime string")
79            }
80
81            fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
82            where
83                E: de::Error,
84            {
85                XsdDateTime::new(value).map_err(de::Error::custom)
86            }
87        }
88
89        deserializer.deserialize_str(XsdDateTimeVisitor)
90    }
91}
92
93impl fmt::Display for XsdDateTime {
94    /// Formats the `XsdDateTime` using the inner `DateTime`'s `Display` impl.
95    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
96        write!(f, "{}", self.value)
97    }
98}
99
100/// Error type for XSD DateTime parsing failures.
101#[derive(Error, Debug)]
102pub enum XsdDateTimeParseError {
103    #[error("Invalid XSD DateTime: {0}")]
104    InvalidDateTime(#[from] oxsdatatypes::ParseDateTimeError),
105}