datetime_string/rfc3339/date_time/
owned.rs

1//! RFC 3339 [`date-time`] owned string type.
2//!
3//! [`date-time`]: https://tools.ietf.org/html/rfc3339#section-5.6
4#![cfg(feature = "alloc")]
5
6use core::{convert::TryFrom, fmt, ops, str};
7
8use alloc::{string::String, vec::Vec};
9
10#[cfg(feature = "serde")]
11use serde::Serialize;
12
13use crate::{ConversionError, Error};
14
15use super::{validate_bytes, DateTimeStr};
16
17/// Owned string for a datetime in RFC 3339 [`date-time`] format, such as
18/// `2001-06-17T12:34:56.7890-23:12`.
19///
20/// Available when `alloc` feature is enabled.
21///
22/// # Examples
23///
24/// ```
25/// # use datetime_string::rfc3339::DateTimeString;
26/// use datetime_string::rfc3339::DateTimeStr;
27/// use std::convert::TryFrom;
28///
29/// let try_from = DateTimeString::try_from("2001-06-17T12:34:56.7890-23:12")?;
30///
31/// let parse = "2001-06-17T12:34:56.7890-23:12".parse::<DateTimeString>()?;
32/// let parse2: DateTimeString = "2001-06-17T12:34:56.7890-23:12".parse()?;
33///
34/// let to_owned = DateTimeStr::from_str("2001-06-17T12:34:56.7890-23:12")?.to_owned();
35/// let into: DateTimeString = DateTimeStr::from_str("2001-06-17T12:34:56.7890-23:12")?.into();
36/// # Ok::<_, datetime_string::Error>(())
37/// ```
38///
39/// [`date-time`]: https://tools.ietf.org/html/rfc3339#section-5.6
40#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
41#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
42#[repr(transparent)]
43// Note that `derive(Serialize)` cannot used here, because it encodes this as
44// `Vec<u8>` rather than as a string.
45//
46// Comparisons implemented for the type are consistent (at least it is intended to be so).
47// See <https://github.com/rust-lang/rust-clippy/issues/2025>.
48// Note that `clippy::derive_ord_xor_partial_ord` would be introduced since Rust 1.47.0.
49#[allow(clippy::derive_hash_xor_eq)]
50#[allow(unknown_lints, clippy::derive_ord_xor_partial_ord)]
51pub struct DateTimeString(Vec<u8>);
52
53impl DateTimeString {
54    /// Creates a `DateTimeString` from the given bytes.
55    ///
56    /// This performs assertion in debug build, but not in release build.
57    ///
58    /// # Safety
59    ///
60    /// `validate_bytes(&s)` should return `Ok(())`.
61    #[inline]
62    #[must_use]
63    unsafe fn from_bytes_maybe_unchecked(s: Vec<u8>) -> Self {
64        debug_assert_ok!(validate_bytes(&s));
65        Self(s)
66    }
67
68    /// Returns a `&DateTimeStr` for the string.
69    ///
70    /// # Examples
71    ///
72    /// ```
73    /// # use datetime_string::rfc3339::DateTimeString;
74    /// use datetime_string::rfc3339::DateTimeStr;
75    ///
76    /// let datetime = "2001-06-17T12:34:56.7890-23:12".parse::<DateTimeString>()?;
77    ///
78    /// // Usually you don't need to call `as_deref()` explicitly, because
79    /// // `Deref<Target = DateTimeStr>` trait is implemented.
80    /// let _: &DateTimeStr = datetime.as_deref();
81    /// # Ok::<_, datetime_string::Error>(())
82    /// ```
83    #[inline]
84    #[must_use]
85    pub fn as_deref(&self) -> &DateTimeStr {
86        unsafe {
87            // This is safe because `self.0` is already validated.
88            debug_assert_safe_version_ok!(DateTimeStr::from_bytes(&self.0));
89            DateTimeStr::from_bytes_maybe_unchecked(&self.0)
90        }
91    }
92
93    /// Returns a `&mut DateTimeStr` for the string.
94    ///
95    /// # Examples
96    ///
97    /// ```
98    /// # use datetime_string::rfc3339::DateTimeString;
99    /// use datetime_string::rfc3339::DateTimeStr;
100    ///
101    /// let mut datetime = "2001-06-17T12:34:56.7890-23:12".parse::<DateTimeString>()?;
102    ///
103    /// // Usually you don't need to call `as_deref_mut()` explicitly, because
104    /// // `DerefMut` trait is implemented.
105    /// let _: &mut DateTimeStr = datetime.as_deref_mut();
106    /// # Ok::<_, datetime_string::Error>(())
107    /// ```
108    #[inline]
109    #[must_use]
110    pub fn as_deref_mut(&mut self) -> &mut DateTimeStr {
111        unsafe {
112            // This is safe because `self.0` is already validated.
113            debug_assert_ok!(DateTimeStr::from_bytes(&self.0));
114            DateTimeStr::from_bytes_maybe_unchecked_mut(&mut self.0)
115        }
116    }
117}
118
119impl core::borrow::Borrow<DateTimeStr> for DateTimeString {
120    #[inline]
121    fn borrow(&self) -> &DateTimeStr {
122        self.as_deref()
123    }
124}
125
126impl core::borrow::BorrowMut<DateTimeStr> for DateTimeString {
127    #[inline]
128    fn borrow_mut(&mut self) -> &mut DateTimeStr {
129        self.as_deref_mut()
130    }
131}
132
133impl alloc::borrow::ToOwned for DateTimeStr {
134    type Owned = DateTimeString;
135
136    #[inline]
137    fn to_owned(&self) -> Self::Owned {
138        self.into()
139    }
140}
141
142impl AsRef<[u8]> for DateTimeString {
143    #[inline]
144    fn as_ref(&self) -> &[u8] {
145        self.as_bytes()
146    }
147}
148
149impl AsRef<str> for DateTimeString {
150    #[inline]
151    fn as_ref(&self) -> &str {
152        self.as_str()
153    }
154}
155
156impl AsRef<DateTimeStr> for DateTimeString {
157    #[inline]
158    fn as_ref(&self) -> &DateTimeStr {
159        self
160    }
161}
162
163impl AsMut<DateTimeStr> for DateTimeString {
164    #[inline]
165    fn as_mut(&mut self) -> &mut DateTimeStr {
166        self
167    }
168}
169
170impl From<DateTimeString> for Vec<u8> {
171    #[inline]
172    fn from(v: DateTimeString) -> Vec<u8> {
173        v.0
174    }
175}
176
177impl From<DateTimeString> for String {
178    #[inline]
179    fn from(v: DateTimeString) -> String {
180        unsafe {
181            // This is safe because a valid `DateTimeString` is an ASCII string.
182            debug_assert_ok!(str::from_utf8(&v.0));
183            String::from_utf8_unchecked(v.0)
184        }
185    }
186}
187
188impl From<&DateTimeStr> for DateTimeString {
189    fn from(v: &DateTimeStr) -> Self {
190        debug_assert_ok!(validate_bytes(&v.0));
191        unsafe {
192            // This is safe because the value is already validated.
193            Self::from_bytes_maybe_unchecked(v.0.into())
194        }
195    }
196}
197
198impl TryFrom<&[u8]> for DateTimeString {
199    type Error = Error;
200
201    #[inline]
202    fn try_from(v: &[u8]) -> Result<Self, Self::Error> {
203        DateTimeStr::from_bytes(v).map(Into::into)
204    }
205}
206
207impl TryFrom<&str> for DateTimeString {
208    type Error = Error;
209
210    #[inline]
211    fn try_from(v: &str) -> Result<Self, Self::Error> {
212        DateTimeStr::from_str(v).map(Into::into)
213    }
214}
215
216impl TryFrom<Vec<u8>> for DateTimeString {
217    type Error = ConversionError<Vec<u8>>;
218
219    #[inline]
220    fn try_from(v: Vec<u8>) -> Result<Self, Self::Error> {
221        match validate_bytes(&v) {
222            Ok(_) => Ok(unsafe {
223                // This is safe because the value is successfully validated.
224                Self::from_bytes_maybe_unchecked(v)
225            }),
226            Err(e) => Err(ConversionError::new(v, e)),
227        }
228    }
229}
230
231impl TryFrom<String> for DateTimeString {
232    type Error = ConversionError<String>;
233
234    #[inline]
235    fn try_from(v: String) -> Result<Self, Self::Error> {
236        match validate_bytes(v.as_bytes()) {
237            Ok(_) => Ok(unsafe {
238                // This is safe because the value is successfully validated.
239                Self::from_bytes_maybe_unchecked(v.into_bytes())
240            }),
241            Err(e) => Err(ConversionError::new(v, e)),
242        }
243    }
244}
245
246impl fmt::Display for DateTimeString {
247    #[inline]
248    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
249        self.as_deref().fmt(f)
250    }
251}
252
253impl ops::Deref for DateTimeString {
254    type Target = DateTimeStr;
255
256    #[inline]
257    fn deref(&self) -> &Self::Target {
258        self.as_deref()
259    }
260}
261
262impl ops::DerefMut for DateTimeString {
263    #[inline]
264    fn deref_mut(&mut self) -> &mut Self::Target {
265        self.as_deref_mut()
266    }
267}
268
269impl str::FromStr for DateTimeString {
270    type Err = Error;
271
272    #[inline]
273    fn from_str(s: &str) -> Result<Self, Self::Err> {
274        Self::try_from(s)
275    }
276}
277
278impl_cmp_symmetric!(DateTimeStr, DateTimeString, &DateTimeString);
279impl_cmp_symmetric!(DateTimeStr, DateTimeString, DateTimeStr);
280impl_cmp_symmetric!(DateTimeStr, DateTimeString, &DateTimeStr);
281impl_cmp_symmetric!(str, DateTimeString, str);
282impl_cmp_symmetric!(str, DateTimeString, &str);
283impl_cmp_symmetric!(str, &DateTimeString, str);
284impl_cmp_symmetric!([u8], DateTimeString, [u8]);
285impl_cmp_symmetric!([u8], DateTimeString, &[u8]);
286impl_cmp_symmetric!([u8], &DateTimeString, [u8]);
287
288#[cfg(feature = "serde")]
289#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
290impl Serialize for DateTimeString {
291    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
292    where
293        S: serde::Serializer,
294    {
295        serializer.serialize_str(self.as_str())
296    }
297}
298
299/// Items for serde support.
300#[cfg(feature = "serde")]
301mod serde_ {
302    use super::*;
303
304    use serde::de::{Deserialize, Deserializer, Visitor};
305
306    /// Visitor for `DateTimeString`.
307    struct StringVisitor;
308
309    impl<'de> Visitor<'de> for StringVisitor {
310        type Value = DateTimeString;
311
312        #[inline]
313        fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
314            f.write_str("RFC 3339 date-time string")
315        }
316
317        #[inline]
318        fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
319        where
320            E: serde::de::Error,
321        {
322            Self::Value::try_from(v).map_err(E::custom)
323        }
324
325        #[inline]
326        fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
327        where
328            E: serde::de::Error,
329        {
330            Self::Value::try_from(v).map_err(E::custom)
331        }
332    }
333
334    impl<'de> Deserialize<'de> for DateTimeString {
335        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
336        where
337            D: Deserializer<'de>,
338        {
339            deserializer.deserialize_any(StringVisitor)
340        }
341    }
342}
343
344#[cfg(feature = "serde")]
345#[cfg(test)]
346mod tests {
347    use super::*;
348
349    use serde_test::{assert_de_tokens, assert_tokens, Token};
350
351    #[test]
352    fn ser_de_string() {
353        let raw: &'static str = "2001-06-17T12:34:56.7890-23:12";
354        assert_tokens(&DateTimeString::try_from(raw).unwrap(), &[Token::Str(raw)]);
355    }
356
357    #[test]
358    fn de_bytes() {
359        let raw: &'static [u8] = b"2001-06-17T12:34:56.7890-23:12";
360        assert_de_tokens(
361            &DateTimeString::try_from(raw).unwrap(),
362            &[Token::Bytes(raw)],
363        );
364    }
365}