poem_openapi/types/external/
time.rs1use 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);