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