poem_openapi/types/external/
time.rs

1use std::borrow::Cow;
2
3use poem::web::Field;
4use serde_json::Value;
5use time::{
6    format_description::well_known::Rfc3339, macros::format_description, Date, OffsetDateTime,
7    PrimitiveDateTime, Time,
8};
9
10use crate::{
11    registry::{MetaSchema, MetaSchemaRef},
12    types::{
13        ParseError, ParseFromJSON, ParseFromMultipartField, ParseFromParameter, ParseResult,
14        ToJSON, Type,
15    },
16};
17
18impl Type for OffsetDateTime {
19    const IS_REQUIRED: bool = true;
20
21    type RawValueType = Self;
22
23    type RawElementValueType = Self;
24
25    fn name() -> Cow<'static, str> {
26        "string_date-time".into()
27    }
28
29    fn schema_ref() -> MetaSchemaRef {
30        MetaSchemaRef::Inline(Box::new(MetaSchema::new_with_format("string", "date-time")))
31    }
32
33    fn as_raw_value(&self) -> Option<&Self::RawValueType> {
34        Some(self)
35    }
36
37    fn raw_element_iter<'a>(
38        &'a self,
39    ) -> Box<dyn Iterator<Item = &'a Self::RawElementValueType> + 'a> {
40        Box::new(self.as_raw_value().into_iter())
41    }
42}
43
44impl ParseFromJSON for OffsetDateTime {
45    fn parse_from_json(value: Option<Value>) -> ParseResult<Self> {
46        let value = value.unwrap_or_default();
47        if let Value::String(value) = value {
48            Ok(OffsetDateTime::parse(&value, &Rfc3339)?)
49        } else {
50            Err(ParseError::expected_type(value))
51        }
52    }
53}
54
55impl ParseFromParameter for OffsetDateTime {
56    fn parse_from_parameter(value: &str) -> ParseResult<Self> {
57        Ok(OffsetDateTime::parse(value, &Rfc3339)?)
58    }
59}
60
61impl ParseFromMultipartField for OffsetDateTime {
62    async fn parse_from_multipart(field: Option<Field>) -> ParseResult<Self> {
63        match field {
64            Some(field) => Ok(OffsetDateTime::parse(&field.text().await?, &Rfc3339)?),
65            None => Err(ParseError::expected_input()),
66        }
67    }
68}
69
70impl ToJSON for OffsetDateTime {
71    fn to_json(&self) -> Option<Value> {
72        self.format(&Rfc3339).ok().map(Value::String)
73    }
74}
75
76macro_rules! impl_naive_datetime_types {
77    ($ty:ty, $type_name:literal, $format:literal, $format_description:expr) => {
78        impl Type for $ty {
79            const IS_REQUIRED: bool = true;
80
81            type RawValueType = Self;
82
83            type RawElementValueType = Self;
84
85            fn name() -> Cow<'static, str> {
86                concat!($type_name, "_", $format).into()
87            }
88
89            fn schema_ref() -> MetaSchemaRef {
90                MetaSchemaRef::Inline(Box::new(MetaSchema::new_with_format($type_name, $format)))
91            }
92
93            fn as_raw_value(&self) -> Option<&Self::RawValueType> {
94                Some(self)
95            }
96
97            fn raw_element_iter<'a>(
98                &'a self,
99            ) -> Box<dyn Iterator<Item = &'a Self::RawElementValueType> + 'a> {
100                Box::new(self.as_raw_value().into_iter())
101            }
102        }
103
104        impl ParseFromJSON for $ty {
105            fn parse_from_json(value: Option<Value>) -> ParseResult<Self> {
106                let value = value.unwrap_or_default();
107                if let Value::String(value) = value {
108                    Ok(<$ty>::parse(&value, $format_description)?)
109                } else {
110                    Err(ParseError::expected_type(value))
111                }
112            }
113        }
114
115        impl ParseFromParameter for $ty {
116            fn parse_from_parameter(value: &str) -> ParseResult<Self> {
117                Ok(<$ty>::parse(&value, $format_description)?)
118            }
119        }
120
121        impl ParseFromMultipartField for $ty {
122            async fn parse_from_multipart(field: Option<Field>) -> ParseResult<Self> {
123                match field {
124                    Some(field) => Ok(<$ty>::parse(&field.text().await?, $format_description)?),
125                    None => Err(ParseError::expected_input()),
126                }
127            }
128        }
129
130        impl ToJSON for $ty {
131            fn to_json(&self) -> Option<Value> {
132                self.format($format_description).ok().map(Value::String)
133            }
134        }
135    };
136}
137
138impl_naive_datetime_types!(
139    PrimitiveDateTime,
140    "string",
141    "naive-date-time",
142    format_description!("[year]-[month]-[day] [hour]:[minute]:[second]")
143);
144impl_naive_datetime_types!(
145    Date,
146    "string",
147    "naive-date",
148    format_description!("[year]-[month]-[day]")
149);
150impl_naive_datetime_types!(
151    Time,
152    "string",
153    "naive-time",
154    format_description!("[hour]:[minute]:[second]")
155);