poem_openapi/types/external/
integers.rs

1use std::borrow::Cow;
2
3use poem::{http::HeaderValue, web::Field};
4use serde_json::Value;
5
6use crate::{
7    registry::{MetaSchema, MetaSchemaRef},
8    types::{
9        ParseError, ParseFromJSON, ParseFromMultipartField, ParseFromParameter, ParseFromXML,
10        ParseResult, ToHeader, ToJSON, ToXML, Type,
11    },
12};
13
14macro_rules! impl_type_for_integers {
15    ($(($ty:ty, $format:literal)),*) => {
16        $(
17        impl Type for $ty {
18            const IS_REQUIRED: bool = true;
19
20            type RawValueType = Self;
21
22            type RawElementValueType = Self;
23
24            fn name() -> Cow<'static, str> {
25                format!("integer_{}", $format).into()
26            }
27
28            fn schema_ref() -> MetaSchemaRef {
29                MetaSchemaRef::Inline(Box::new(MetaSchema::new_with_format("integer", $format)))
30            }
31
32            fn as_raw_value(&self) -> Option<&Self::RawValueType> {
33                Some(self)
34            }
35
36            fn raw_element_iter<'a>(
37                &'a self
38            ) -> Box<dyn Iterator<Item = &'a Self::RawElementValueType> + 'a> {
39                Box::new(self.as_raw_value().into_iter())
40            }
41        }
42
43        impl ParseFromJSON for $ty {
44             fn parse_from_json(value: Option<Value>) -> ParseResult<Self> {
45                 let value = value.unwrap_or_default();
46                 if let Value::Number(n) = value {
47                     let n = n
48                         .as_i64()
49                         .ok_or_else(|| ParseError::from("invalid integer"))?;
50
51                     if n < Self::MIN as i64 || n > Self::MAX as i64 {
52                         return Err(ParseError::from(format!(
53                             "Only integers from {} to {} are accepted.",
54                             Self::MIN,
55                             Self::MAX
56                         )));
57                     }
58
59                     Ok(n as Self)
60                 } else {
61                     Err(ParseError::expected_type(value))
62                 }
63            }
64        }
65
66        impl ParseFromXML for $ty {
67             fn parse_from_xml(value: Option<Value>) -> ParseResult<Self> {
68                 let value = value.unwrap_or_default();
69                 if let Value::Number(n) = value {
70                     let n = n
71                         .as_i64()
72                         .ok_or_else(|| ParseError::from("invalid integer"))?;
73
74                     if n < Self::MIN as i64 || n > Self::MAX as i64 {
75                         return Err(ParseError::from(format!(
76                             "Only integers from {} to {} are accepted.",
77                             Self::MIN,
78                             Self::MAX
79                         )));
80                     }
81
82                     Ok(n as Self)
83                 } else {
84                     Err(ParseError::expected_type(value))
85                 }
86            }
87        }
88
89        impl ParseFromParameter for $ty {
90            fn parse_from_parameter(value: &str) -> ParseResult<Self> {
91                value.parse().map_err(ParseError::custom)
92            }
93        }
94
95        impl ParseFromMultipartField for $ty {
96            async fn parse_from_multipart(field: Option<Field>) -> ParseResult<Self> {
97                match field {
98                    Some(field) => Ok(field.text().await?.parse()?),
99                    None => Err(ParseError::expected_input()),
100                }
101            }
102        }
103
104        impl ToJSON for $ty {
105            fn to_json(&self) -> Option<Value> {
106                Some(Value::Number((*self).into()))
107            }
108        }
109
110        impl ToXML for $ty {
111            fn to_xml(&self) -> Option<Value> {
112                Some(Value::Number((*self).into()))
113            }
114        }
115
116        impl ToHeader for $ty {
117            fn to_header(&self) -> Option<HeaderValue> {
118                match HeaderValue::from_str(&format!("{}", self)) {
119                    Ok(value) => Some(value),
120                    Err(_) => None,
121                }
122            }
123        }
124
125        )*
126    };
127}
128
129macro_rules! impl_type_for_unsigneds {
130    ($(($ty:ty, $format:literal)),*) => {
131        $(
132        impl Type for $ty {
133            const IS_REQUIRED: bool = true;
134
135            type RawValueType = Self;
136
137            type RawElementValueType = Self;
138
139            fn name() -> Cow<'static, str> {
140                format!("integer({})", $format).into()
141            }
142
143            fn schema_ref() -> MetaSchemaRef {
144                MetaSchemaRef::Inline(Box::new(MetaSchema::new_with_format("integer", $format)))
145            }
146
147            fn as_raw_value(&self) -> Option<&Self::RawValueType> {
148                Some(self)
149            }
150
151            fn raw_element_iter<'a>(
152                &'a self
153            ) -> Box<dyn Iterator<Item = &'a Self::RawElementValueType> + 'a> {
154                Box::new(self.as_raw_value().into_iter())
155            }
156        }
157
158        impl ParseFromJSON for $ty {
159             fn parse_from_json(value: Option<Value>) -> ParseResult<Self> {
160                 let value = value.unwrap_or_default();
161                 if let Value::Number(n) = value {
162                     let n = n
163                         .as_u64()
164                         .ok_or_else(|| ParseError::from("invalid integer"))?;
165
166                     if n < Self::MIN as u64 || n > Self::MAX as u64 {
167                         return Err(ParseError::from(format!(
168                             "Only integers from {} to {} are accepted.",
169                             Self::MIN,
170                             Self::MAX
171                         )));
172                     }
173
174                     Ok(n as Self)
175                 } else {
176                     Err(ParseError::expected_type(value))
177                 }
178            }
179        }
180
181        impl ParseFromParameter for $ty {
182            fn parse_from_parameter(value: &str) -> ParseResult<Self> {
183                value.parse().map_err(ParseError::custom)
184            }
185        }
186
187        impl ParseFromMultipartField for $ty {
188            async fn parse_from_multipart(field: Option<Field>) -> ParseResult<Self> {
189                match field {
190                    Some(field) => Ok(field.text().await?.parse()?),
191                    None => Err(ParseError::expected_input()),
192                }
193            }
194        }
195
196        impl ToJSON for $ty {
197            fn to_json(&self) -> Option<Value> {
198                Some(Value::Number((*self).into()))
199            }
200        }
201
202        impl ToHeader for $ty {
203            fn to_header(&self) -> Option<HeaderValue> {
204                match HeaderValue::from_str(&format!("{}", self)) {
205                    Ok(value) => Some(value),
206                    Err(_) => None,
207                }
208            }
209        }
210
211        )*
212    };
213}
214
215impl_type_for_integers!((i8, "int8"), (i16, "int16"), (i32, "int32"), (i64, "int64"));
216
217impl_type_for_unsigneds!(
218    (u8, "uint8"),
219    (u16, "uint16"),
220    (u32, "uint32"),
221    (u64, "uint64"),
222    (usize, "uint64")
223);