datetime_string/rfc3339/
secfrac.rs

1//! RFC 3339 [`time-secfrac`] string types.
2//!
3//! [`time-secfrac`]: https://tools.ietf.org/html/rfc3339#section-5.6
4
5#[cfg(feature = "alloc")]
6mod owned;
7
8use core::{
9    convert::TryFrom,
10    fmt,
11    ops::{self, RangeFrom},
12    str,
13};
14
15use crate::{
16    common::SecfracDigitsStr,
17    error::{ComponentKind, Error, ErrorKind},
18};
19
20#[cfg(feature = "alloc")]
21pub use self::owned::SecfracString;
22
23/// Range of digits.
24const DIGITS_RANGE: RangeFrom<usize> = 1..;
25
26/// Validates the given string as an RFC 3339 [`time-secfrac`] string.
27///
28/// [`time-secfrac`]: https://tools.ietf.org/html/rfc3339#section-5.6
29fn validate_bytes(s: &[u8]) -> Result<(), Error> {
30    if s.len() <= 1 {
31        return Err(ErrorKind::TooShort.into());
32    }
33
34    if s[0] != b'.' {
35        return Err(ErrorKind::InvalidSeparator.into());
36    }
37
38    let secfrac_s = &s[DIGITS_RANGE];
39    if !secfrac_s.iter().all(u8::is_ascii_digit) {
40        return Err(ErrorKind::InvalidComponentType(ComponentKind::Secfrac).into());
41    }
42
43    Ok(())
44}
45
46/// String slice for a time in RFC 3339 [`time-secfrac`] format, such as `.7890`.
47///
48/// [`time-secfrac`]: https://tools.ietf.org/html/rfc3339#section-5.6
49#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
50#[repr(transparent)]
51// Note that `derive(Serialize)` cannot used here, because it encodes this as
52// `[u8]` rather than as a string.
53//
54// Comparisons implemented for the type are consistent (at least it is intended to be so).
55// See <https://github.com/rust-lang/rust-clippy/issues/2025>.
56// Note that `clippy::derive_ord_xor_partial_ord` would be introduced since Rust 1.47.0.
57#[allow(clippy::derive_hash_xor_eq)]
58#[allow(unknown_lints, clippy::derive_ord_xor_partial_ord)]
59pub struct SecfracStr([u8]);
60
61impl SecfracStr {
62    /// Creates a `&SecfracStr` from the given byte slice.
63    ///
64    /// This performs assertion in debug build, but not in release build.
65    ///
66    /// # Safety
67    ///
68    /// `validate_bytes(s)` should return `Ok(())`.
69    #[inline]
70    #[must_use]
71    pub(crate) unsafe fn from_bytes_maybe_unchecked(s: &[u8]) -> &Self {
72        debug_assert_ok!(validate_bytes(s));
73        &*(s as *const [u8] as *const Self)
74    }
75
76    /// Creates a `&mut SecfracStr` from the given mutable byte slice.
77    ///
78    /// This performs assertion in debug build, but not in release build.
79    ///
80    /// # Safety
81    ///
82    /// `validate_bytes(s)` should return `Ok(())`.
83    #[inline]
84    #[must_use]
85    pub(crate) unsafe fn from_bytes_maybe_unchecked_mut(s: &mut [u8]) -> &mut Self {
86        debug_assert_ok!(validate_bytes(s));
87        &mut *(s as *mut [u8] as *mut Self)
88    }
89
90    /// Creates a `&mut SecfracStr` from the given mutable string slice.
91    ///
92    /// This performs assertion in debug build, but not in release build.
93    ///
94    /// # Safety
95    ///
96    /// `validate_bytes(s.as_bytes())` should return `Ok(())`.
97    #[inline]
98    #[must_use]
99    unsafe fn from_str_maybe_unchecked_mut(s: &mut str) -> &mut Self {
100        // This is safe because `SecfracStr` ensures that the underlying
101        // bytes are ASCII string after modification.
102        Self::from_bytes_maybe_unchecked_mut(s.as_bytes_mut())
103    }
104
105    /// Creates a new `&SecfracStr` from a string slice.
106    ///
107    /// # Examples
108    ///
109    /// ```
110    /// # use datetime_string::rfc3339::SecfracStr;
111    /// let secfrac = SecfracStr::from_str(".1234")?;
112    /// assert_eq!(secfrac.as_str(), ".1234");
113    ///
114    /// assert!(SecfracStr::from_str(".0").is_ok());
115    /// assert!(SecfracStr::from_str(".0000000000").is_ok());
116    /// assert!(SecfracStr::from_str(".9999999999").is_ok());
117    ///
118    /// assert!(SecfracStr::from_str("0").is_err(), "A leading period is required");
119    /// assert!(SecfracStr::from_str(".").is_err(), "One or more digits are required");
120    /// # Ok::<_, datetime_string::Error>(())
121    /// ```
122    #[inline]
123    // `FromStr` trait cannot be implemented for a slice.
124    #[allow(clippy::should_implement_trait)]
125    pub fn from_str(s: &str) -> Result<&Self, Error> {
126        TryFrom::try_from(s)
127    }
128
129    /// Creates a new `&mut SecfracStr` from a mutable string slice.
130    ///
131    /// # Examples
132    ///
133    /// ```
134    /// # use datetime_string::rfc3339::SecfracStr;
135    /// let mut buf = ".1234".to_owned();
136    /// let secfrac = SecfracStr::from_mut_str(&mut buf)?;
137    /// assert_eq!(secfrac.as_str(), ".1234");
138    ///
139    /// secfrac.digits_mut().fill_with_zero();
140    /// assert_eq!(secfrac.as_str(), ".0000");
141    ///
142    /// assert_eq!(buf, ".0000");
143    /// # Ok::<_, datetime_string::Error>(())
144    /// ```
145    #[inline]
146    pub fn from_mut_str(s: &mut str) -> Result<&mut Self, Error> {
147        TryFrom::try_from(s)
148    }
149
150    /// Creates a new `&SecfracStr` from a byte slice.
151    ///
152    /// # Examples
153    ///
154    /// ```
155    /// # use datetime_string::rfc3339::SecfracStr;
156    /// let secfrac = SecfracStr::from_bytes(b".1234")?;
157    /// assert_eq!(secfrac.as_str(), ".1234");
158    ///
159    /// assert!(SecfracStr::from_bytes(b".0").is_ok());
160    /// assert!(SecfracStr::from_bytes(b".0000000000").is_ok());
161    /// assert!(SecfracStr::from_bytes(b".9999999999").is_ok());
162    ///
163    /// assert!(SecfracStr::from_bytes(b"0").is_err(), "A leading period is required");
164    /// assert!(SecfracStr::from_bytes(b".").is_err(), "One or more digits are required");
165    /// # Ok::<_, datetime_string::Error>(())
166    /// ```
167    #[inline]
168    pub fn from_bytes(s: &[u8]) -> Result<&Self, Error> {
169        TryFrom::try_from(s)
170    }
171
172    /// Creates a new `&mut SecfracStr` from a mutable byte slice.
173    ///
174    /// # Examples
175    ///
176    /// ```
177    /// # use datetime_string::rfc3339::SecfracStr;
178    /// let mut buf: [u8; 5] = *b".1234";
179    /// let secfrac = SecfracStr::from_bytes_mut(&mut buf)?;
180    /// assert_eq!(secfrac.as_str(), ".1234");
181    ///
182    /// secfrac.digits_mut().fill_with_zero();
183    /// assert_eq!(secfrac.as_str(), ".0000");
184    ///
185    /// assert_eq!(&buf[..], b".0000");
186    /// # Ok::<_, datetime_string::Error>(())
187    /// ```
188    #[inline]
189    pub fn from_bytes_mut(s: &mut [u8]) -> Result<&mut Self, Error> {
190        TryFrom::try_from(s)
191    }
192
193    /// Returns a string slice.
194    ///
195    /// # Examples
196    ///
197    /// ```
198    /// # use datetime_string::rfc3339::SecfracStr;
199    /// let secfrac = SecfracStr::from_str(".1234")?;
200    ///
201    /// assert_eq!(secfrac.as_str(), ".1234");
202    /// # Ok::<_, datetime_string::Error>(())
203    /// ```
204    #[inline]
205    #[must_use]
206    pub fn as_str(&self) -> &str {
207        unsafe {
208            // This is safe because the `SecfracStr` ensures that the
209            // underlying bytes are ASCII string.
210            debug_assert_safe_version_ok!(str::from_utf8(&self.0));
211            str::from_utf8_unchecked(&self.0)
212        }
213    }
214
215    /// Returns a byte slice.
216    ///
217    /// # Examples
218    ///
219    /// ```
220    /// # use datetime_string::rfc3339::SecfracStr;
221    /// let secfrac = SecfracStr::from_str(".1234")?;
222    ///
223    /// assert_eq!(secfrac.as_str(), ".1234");
224    /// # Ok::<_, datetime_string::Error>(())
225    /// ```
226    #[inline]
227    #[must_use]
228    pub fn as_bytes(&self) -> &[u8] {
229        &self.0
230    }
231
232    /// Returns the digits.
233    ///
234    /// # Examples
235    ///
236    /// ```
237    /// # use datetime_string::rfc3339::SecfracStr;
238    /// use datetime_string::common::SecfracDigitsStr;
239    ///
240    /// let secfrac = SecfracStr::from_str(".1234")?;
241    /// assert_eq!(secfrac.digits().as_str(), "1234");
242    ///
243    /// let secfrac2 = SecfracStr::from_str(".012340")?;
244    /// assert_eq!(secfrac2.digits().as_str(), "012340");
245    /// # Ok::<_, datetime_string::Error>(())
246    /// ```
247    #[inline]
248    #[must_use]
249    pub fn digits(&self) -> &SecfracDigitsStr {
250        unsafe {
251            // This is safe because the digits part contains only ASCII digits.
252            debug_assert_safe_version_ok!(SecfracDigitsStr::from_bytes(&self.0[DIGITS_RANGE]));
253            SecfracDigitsStr::from_bytes_maybe_unchecked(self.0.get_unchecked(DIGITS_RANGE))
254        }
255    }
256
257    /// Returns the digits as a mutable reference.
258    ///
259    /// # Examples
260    ///
261    /// ```
262    /// # use datetime_string::rfc3339::SecfracStr;
263    /// use datetime_string::common::SecfracDigitsStr;
264    ///
265    /// let mut buf = ".1234".to_owned();
266    /// let secfrac = SecfracStr::from_mut_str(&mut buf)?;
267    /// let digits = secfrac.digits_mut();
268    /// assert_eq!(digits.as_str(), "1234");
269    ///
270    /// digits.fill_with_zero();
271    /// assert_eq!(digits.as_str(), "0000");
272    /// assert_eq!(secfrac.as_str(), ".0000");
273    /// assert_eq!(buf, ".0000");
274    /// # Ok::<_, datetime_string::Error>(())
275    /// ```
276    #[inline]
277    #[must_use]
278    pub fn digits_mut(&mut self) -> &mut SecfracDigitsStr {
279        unsafe {
280            // This is safe because a `SecfracStr` string is an ASCII string,
281            // and `SecfracDigitsStr` ensures that the underlying bytes are
282            // also ASCII string after modification.
283            debug_assert_ok!(SecfracDigitsStr::from_bytes(&self.0[DIGITS_RANGE]));
284            SecfracDigitsStr::from_bytes_maybe_unchecked_mut(self.0.get_unchecked_mut(DIGITS_RANGE))
285        }
286    }
287
288    /// Returns a milliseconds precision secfrac if there are enough digits.
289    ///
290    /// # Examples
291    ///
292    /// ```
293    /// # use datetime_string::rfc3339::SecfracStr;
294    /// let not_precise = SecfracStr::from_str(".1")?;
295    /// assert_eq!(not_precise.milliseconds_secfrac(), None);
296    ///
297    /// let expected = SecfracStr::from_str(".012")?;
298    /// assert_eq!(expected.milliseconds_secfrac(), Some(expected));
299    ///
300    /// let more_precise = SecfracStr::from_str(".012345678901")?;
301    /// assert_eq!(more_precise.milliseconds_secfrac(), Some(expected));
302    /// # Ok::<_, datetime_string::Error>(())
303    /// ```
304    #[inline]
305    #[must_use]
306    pub fn milliseconds_secfrac(&self) -> Option<&SecfracStr> {
307        self.0.get(..4).map(|s| unsafe {
308            // This is safe because ".NNN" value (where Ns are digits) is a
309            // valid time-secfrac string.
310            debug_assert_safe_version_ok!(Self::from_bytes(s));
311            Self::from_bytes_maybe_unchecked(s)
312        })
313    }
314
315    /// Returns a microseconds precision secfrac if there are enough digits.
316    ///
317    /// # Examples
318    ///
319    /// ```
320    /// # use datetime_string::rfc3339::SecfracStr;
321    /// let not_precise = SecfracStr::from_str(".1234")?;
322    /// assert_eq!(not_precise.microseconds_secfrac(), None);
323    ///
324    /// let expected = SecfracStr::from_str(".012345")?;
325    /// assert_eq!(expected.microseconds_secfrac(), Some(expected));
326    ///
327    /// let more_precise = SecfracStr::from_str(".012345678901")?;
328    /// assert_eq!(more_precise.microseconds_secfrac(), Some(expected));
329    /// # Ok::<_, datetime_string::Error>(())
330    /// ```
331    #[inline]
332    #[must_use]
333    pub fn microseconds_secfrac(&self) -> Option<&SecfracStr> {
334        self.0.get(..7).map(|s| unsafe {
335            // This is safe because ".NNNNNN" value (where Ns are digits) is a
336            // valid time-secfrac string.
337            debug_assert_safe_version_ok!(Self::from_bytes(s));
338            Self::from_bytes_maybe_unchecked(s)
339        })
340    }
341
342    /// Returns a nanoseconds precision secfrac if there are enough digits.
343    ///
344    /// # Examples
345    ///
346    /// ```
347    /// # use datetime_string::rfc3339::SecfracStr;
348    /// let not_precise = SecfracStr::from_str(".1234")?;
349    /// assert_eq!(not_precise.nanoseconds_secfrac(), None);
350    ///
351    /// let expected = SecfracStr::from_str(".012345678")?;
352    /// assert_eq!(expected.nanoseconds_secfrac(), Some(expected));
353    ///
354    /// let more_precise = SecfracStr::from_str(".012345678901")?;
355    /// assert_eq!(more_precise.nanoseconds_secfrac(), Some(expected));
356    /// # Ok::<_, datetime_string::Error>(())
357    /// ```
358    #[inline]
359    #[must_use]
360    pub fn nanoseconds_secfrac(&self) -> Option<&SecfracStr> {
361        self.0.get(..10).map(|s| unsafe {
362            // This is safe because ".NNNNNNNNN" value (where Ns are digits) is
363            // a valid time-secfrac string.
364            debug_assert_safe_version_ok!(Self::from_bytes(s));
365            Self::from_bytes_maybe_unchecked(s)
366        })
367    }
368}
369
370impl AsRef<[u8]> for SecfracStr {
371    #[inline]
372    fn as_ref(&self) -> &[u8] {
373        self.as_bytes()
374    }
375}
376
377impl AsRef<str> for SecfracStr {
378    #[inline]
379    fn as_ref(&self) -> &str {
380        self.as_str()
381    }
382}
383
384impl AsRef<SecfracStr> for SecfracStr {
385    #[inline]
386    fn as_ref(&self) -> &SecfracStr {
387        self
388    }
389}
390
391impl AsMut<SecfracStr> for SecfracStr {
392    #[inline]
393    fn as_mut(&mut self) -> &mut SecfracStr {
394        self
395    }
396}
397
398impl<'a> From<&'a SecfracStr> for &'a str {
399    #[inline]
400    fn from(v: &'a SecfracStr) -> Self {
401        v.as_str()
402    }
403}
404
405impl<'a> TryFrom<&'a [u8]> for &'a SecfracStr {
406    type Error = Error;
407
408    #[inline]
409    fn try_from(v: &'a [u8]) -> Result<Self, Self::Error> {
410        validate_bytes(v)?;
411        Ok(unsafe {
412            // This is safe because a valid `time-secfrac` string is also an ASCII string.
413            SecfracStr::from_bytes_maybe_unchecked(v)
414        })
415    }
416}
417
418impl<'a> TryFrom<&'a mut [u8]> for &'a mut SecfracStr {
419    type Error = Error;
420
421    #[inline]
422    fn try_from(v: &'a mut [u8]) -> Result<Self, Self::Error> {
423        validate_bytes(v)?;
424        Ok(unsafe {
425            // This is safe because a valid `time-secfrac` string is also an ASCII string.
426            SecfracStr::from_bytes_maybe_unchecked_mut(v)
427        })
428    }
429}
430
431impl<'a> TryFrom<&'a str> for &'a SecfracStr {
432    type Error = Error;
433
434    #[inline]
435    fn try_from(v: &'a str) -> Result<Self, Self::Error> {
436        Self::try_from(v.as_bytes())
437    }
438}
439
440impl<'a> TryFrom<&'a mut str> for &'a mut SecfracStr {
441    type Error = Error;
442
443    #[inline]
444    fn try_from(v: &'a mut str) -> Result<Self, Self::Error> {
445        validate_bytes(v.as_bytes())?;
446        Ok(unsafe {
447            // This is safe because it is successfully validated, and
448            // `SecfracStr` ensures that the underlying bytes are ASCII string
449            // after modification.
450            SecfracStr::from_str_maybe_unchecked_mut(v)
451        })
452    }
453}
454
455impl fmt::Display for SecfracStr {
456    #[inline]
457    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
458        self.as_str().fmt(f)
459    }
460}
461
462impl ops::Deref for SecfracStr {
463    type Target = str;
464
465    #[inline]
466    fn deref(&self) -> &Self::Target {
467        self.as_str()
468    }
469}
470
471impl_cmp_symmetric!(str, SecfracStr, &SecfracStr);
472impl_cmp_symmetric!([u8], SecfracStr, [u8]);
473impl_cmp_symmetric!([u8], SecfracStr, &[u8]);
474impl_cmp_symmetric!([u8], &SecfracStr, [u8]);
475impl_cmp_symmetric!(str, SecfracStr, str);
476impl_cmp_symmetric!(str, SecfracStr, &str);
477impl_cmp_symmetric!(str, &SecfracStr, str);
478
479#[cfg(feature = "serde")]
480#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
481impl serde::Serialize for SecfracStr {
482    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
483    where
484        S: serde::Serializer,
485    {
486        serializer.serialize_str(self.as_str())
487    }
488}
489
490/// Items for serde support.
491#[cfg(feature = "serde")]
492mod serde_ {
493    use super::*;
494
495    use serde::de::{Deserialize, Deserializer, Visitor};
496
497    /// Visitor for `&SecfracStr`.
498    struct StrVisitor;
499
500    impl<'de> Visitor<'de> for StrVisitor {
501        type Value = &'de SecfracStr;
502
503        #[inline]
504        fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
505            f.write_str("RFC 3339 time-secfrac string")
506        }
507
508        #[inline]
509        fn visit_borrowed_bytes<E>(self, v: &'de [u8]) -> Result<Self::Value, E>
510        where
511            E: serde::de::Error,
512        {
513            Self::Value::try_from(v).map_err(E::custom)
514        }
515
516        #[inline]
517        fn visit_borrowed_str<E>(self, v: &'de str) -> Result<Self::Value, E>
518        where
519            E: serde::de::Error,
520        {
521            Self::Value::try_from(v).map_err(E::custom)
522        }
523    }
524
525    impl<'de> Deserialize<'de> for &'de SecfracStr {
526        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
527        where
528            D: Deserializer<'de>,
529        {
530            deserializer.deserialize_any(StrVisitor)
531        }
532    }
533}
534
535#[cfg(test)]
536mod tests {
537    #[cfg(feature = "serde")]
538    use super::*;
539
540    use super::validate_bytes as s_validate;
541
542    #[cfg(feature = "serde")]
543    use serde_test::{assert_de_tokens, assert_tokens, Token};
544
545    #[test]
546    fn validate_bytes() {
547        assert!(s_validate(b".0").is_ok());
548        assert!(s_validate(b".9").is_ok());
549        assert!(s_validate(b".1234").is_ok());
550        assert!(s_validate(b".001200").is_ok());
551        assert!(s_validate(b".0000000").is_ok());
552        assert!(s_validate(b".9999999").is_ok());
553        assert!(s_validate(b".00000000000000000000000000000000").is_ok());
554        assert!(s_validate(b".99999999999999999999999999999999").is_ok());
555
556        assert!(s_validate(b".").is_err());
557        assert!(s_validate(b"0").is_err());
558        assert!(s_validate(b".+0").is_err());
559        assert!(s_validate(b".-0").is_err());
560        assert!(s_validate(b".0 ").is_err());
561    }
562
563    #[cfg(feature = "serde")]
564    #[test]
565    fn ser_de_str() {
566        let raw: &'static str = ".1234";
567        assert_tokens(
568            &SecfracStr::from_str(raw).unwrap(),
569            &[Token::BorrowedStr(raw)],
570        );
571    }
572
573    #[cfg(feature = "serde")]
574    #[test]
575    fn de_bytes_slice() {
576        let raw: &'static [u8; 5] = b".1234";
577        assert_de_tokens(
578            &SecfracStr::from_bytes(raw).unwrap(),
579            &[Token::BorrowedBytes(raw)],
580        );
581    }
582}