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
36impl Validator for DateTime {
37    fn meta(&self) -> &Meta {
38        &self.meta
39    }
40
41    fn meta_mut(&mut self) -> &mut Meta {
42        &mut self.meta
43    }
44
45    fn check(&self, value: &mut Value) -> Result<(), Error> {
46        let text = as_str(value)?;
47        match text.parse::<jiff::Timestamp>() {
48            Ok(_) => Ok(()),
49            Err(_) => Err(Error::new(ErrorKind::Format {
50                expected: "RFC 3339 datetime",
51            })),
52        }
53    }
54}
55
56/// (`datetime` feature) Accepts a calendar date such as `2024-01-02`.
57#[derive(Debug, Clone, Default)]
58pub struct Date {
59    meta: Meta,
60}
61
62impl Date {
63    pub fn new() -> Self {
64        Self {
65            meta: Meta::default(),
66        }
67    }
68
69    /// Attach human-facing metadata (name, description, examples, default, output conversion).
70    pub fn with_meta(mut self, meta: Meta) -> Self {
71        self.meta = meta;
72        self
73    }
74}
75
76impl Validator for Date {
77    fn meta(&self) -> &Meta {
78        &self.meta
79    }
80
81    fn meta_mut(&mut self) -> &mut Meta {
82        &mut self.meta
83    }
84
85    fn check(&self, value: &mut Value) -> Result<(), Error> {
86        let text = as_str(value)?;
87        match text.parse::<jiff::civil::Date>() {
88            Ok(_) => Ok(()),
89            Err(_) => Err(Error::new(ErrorKind::Format { expected: "date" })),
90        }
91    }
92}
93
94#[cfg(test)]
95mod tests {
96    use super::*;
97
98    #[test]
99    fn datetime_accepts_rfc3339() {
100        assert!(
101            DateTime::new()
102                .validate(&mut Value::String("2024-01-02T15:04:05Z".into()))
103                .is_ok()
104        );
105        assert!(
106            DateTime::new()
107                .validate(&mut Value::String("yesterday".into()))
108                .is_err()
109        );
110    }
111
112    #[test]
113    fn date_accepts_calendar_date() {
114        assert!(
115            Date::new()
116                .validate(&mut Value::String("2024-01-02".into()))
117                .is_ok()
118        );
119        assert!(
120            Date::new()
121                .validate(&mut Value::String("2024-13-99".into()))
122                .is_err()
123        );
124    }
125}