Skip to main content

tanzim_validate/
datetime.rs

1use crate::error::{Error, ErrorKind};
2use crate::{Meta, Validator};
3use tanzim_value::{Value, ValueType};
4
5/// Borrow the inner string, or produce a `Type` error expecting a string.
6fn as_str(value: &mut Value) -> Result<&str, Error> {
7    match value {
8        Value::String(text) => Ok(text),
9        other => Err(Error::new(ErrorKind::Type {
10            expected: ValueType::String,
11            found: other.type_name(),
12        })),
13    }
14}
15
16/// (`datetime` feature) Accepts an RFC 3339 timestamp such as `2024-01-02T15:04:05Z`.
17#[derive(Debug, Clone, Default)]
18pub struct DateTime {
19    meta: Meta,
20}
21
22impl DateTime {
23    pub fn new() -> Self {
24        Self {
25            meta: Meta::default(),
26        }
27    }
28
29    /// Attach human-facing metadata (name, description, examples, default, output conversion).
30    pub fn with_meta(mut self, meta: Meta) -> Self {
31        self.meta = meta;
32        self
33    }
34}
35
36crate::impl_meta_methods!(DateTime);
37
38impl Validator for DateTime {
39    fn meta(&self) -> &Meta {
40        &self.meta
41    }
42
43    fn meta_mut(&mut self) -> &mut Meta {
44        &mut self.meta
45    }
46
47    fn check(&self, value: &mut Value) -> Result<(), Error> {
48        let text = as_str(value)?;
49        match text.parse::<jiff::Timestamp>() {
50            Ok(_) => Ok(()),
51            Err(_) => Err(Error::new(ErrorKind::Format {
52                expected: "RFC 3339 datetime",
53            })),
54        }
55    }
56}
57
58/// (`datetime` feature) Accepts a calendar date such as `2024-01-02`.
59#[derive(Debug, Clone, Default)]
60pub struct Date {
61    meta: Meta,
62}
63
64impl Date {
65    pub fn new() -> Self {
66        Self {
67            meta: Meta::default(),
68        }
69    }
70
71    /// Attach human-facing metadata (name, description, examples, default, output conversion).
72    pub fn with_meta(mut self, meta: Meta) -> Self {
73        self.meta = meta;
74        self
75    }
76}
77
78crate::impl_meta_methods!(Date);
79
80impl Validator for Date {
81    fn meta(&self) -> &Meta {
82        &self.meta
83    }
84
85    fn meta_mut(&mut self) -> &mut Meta {
86        &mut self.meta
87    }
88
89    fn check(&self, value: &mut Value) -> Result<(), Error> {
90        let text = as_str(value)?;
91        match text.parse::<jiff::civil::Date>() {
92            Ok(_) => Ok(()),
93            Err(_) => Err(Error::new(ErrorKind::Format { expected: "date" })),
94        }
95    }
96}
97
98#[cfg(test)]
99mod tests {
100    use super::*;
101
102    #[test]
103    fn datetime_accepts_rfc3339() {
104        assert!(
105            DateTime::new()
106                .validate(&mut Value::String("2024-01-02T15:04:05Z".into()))
107                .is_ok()
108        );
109        assert!(
110            DateTime::new()
111                .validate(&mut Value::String("yesterday".into()))
112                .is_err()
113        );
114    }
115
116    #[test]
117    fn date_accepts_calendar_date() {
118        assert!(
119            Date::new()
120                .validate(&mut Value::String("2024-01-02".into()))
121                .is_ok()
122        );
123        assert!(
124            Date::new()
125                .validate(&mut Value::String("2024-13-99".into()))
126                .is_err()
127        );
128    }
129}