poem_openapi/types/external/
ip.rs

1use std::{
2    borrow::Cow,
3    net::{IpAddr, Ipv4Addr, Ipv6Addr},
4};
5
6use poem::{http::HeaderValue, web::Field};
7use serde_json::Value;
8
9use crate::{
10    registry::{MetaSchema, MetaSchemaRef},
11    types::{
12        ParseError, ParseFromJSON, ParseFromMultipartField, ParseFromParameter, ParseResult,
13        ToHeader, ToJSON, Type,
14    },
15};
16
17macro_rules! meta_scheme {
18    ($format:literal,) => {
19        MetaSchema::new_with_format("string", $format)
20    };
21    ($format:literal, $($oneof:ty),+) => {
22        MetaSchema {
23            one_of: vec![$(<$oneof as Type>::schema_ref()),+],
24            ..MetaSchema::ANY
25        }
26    };
27}
28
29macro_rules! impl_type_for_ip {
30    ($(($ty:ty, $format:literal $(,)? $($oneof:ty),*)),*) => {
31        $(
32        impl Type for $ty {
33            const IS_REQUIRED: bool = true;
34
35            type RawValueType = Self;
36
37            type RawElementValueType = Self;
38
39            fn name() -> Cow<'static, str> {
40                format!("string_{}", $format).into()
41            }
42
43            fn schema_ref() -> MetaSchemaRef {
44                MetaSchemaRef::Inline(Box::new(meta_scheme!($format, $($oneof),*)))
45            }
46
47            fn as_raw_value(&self) -> Option<&Self::RawValueType> {
48                Some(self)
49            }
50
51            fn raw_element_iter<'a>(
52                &'a self,
53            ) -> Box<dyn Iterator<Item = &'a Self::RawElementValueType> + 'a> {
54                Box::new(self.as_raw_value().into_iter())
55            }
56        }
57
58        impl ParseFromJSON for $ty {
59            fn parse_from_json(value: Option<Value>) -> ParseResult<Self> {
60                let value = value.unwrap_or_default();
61                if let Value::String(value) = value {
62                    Ok(value.parse()?)
63                } else {
64                    Err(ParseError::expected_type(value))
65                }
66            }
67        }
68
69        impl ParseFromParameter for $ty {
70            fn parse_from_parameter(value: &str) -> ParseResult<Self> {
71                value.parse().map_err(ParseError::custom)
72            }
73        }
74
75        impl ParseFromMultipartField for $ty {
76            async fn parse_from_multipart(field: Option<Field>) -> ParseResult<Self> {
77                match field {
78                    Some(field) => Ok(field.text().await?.parse()?),
79                    None => Err(ParseError::expected_input()),
80                }
81            }
82        }
83
84        impl ToJSON for $ty {
85            fn to_json(&self) -> Option<Value> {
86                Some(Value::String(self.to_string()))
87            }
88        }
89
90        impl ToHeader for $ty {
91            fn to_header(&self) -> Option<HeaderValue> {
92                HeaderValue::from_str(&self.to_string()).ok()
93            }
94        }
95        )*
96    }
97}
98
99impl_type_for_ip!(
100    (Ipv4Addr, "ipv4"),
101    (Ipv6Addr, "ipv6"),
102    (IpAddr, "ip", Ipv4Addr, Ipv6Addr)
103);
104
105#[cfg(feature = "ipnet")]
106impl_type_for_ip!(
107    (ipnet::Ipv4Net, "ipv4net"),
108    (ipnet::Ipv6Net, "ipv6net"),
109    (ipnet::IpNet, "ipnet", ipnet::Ipv4Net, ipnet::Ipv6Net)
110);