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