Skip to main content

vld/primitives/
socket_addr.rs

1use serde_json::Value;
2use std::net::SocketAddr;
3
4use crate::error::{FormatKind, IssueCode, SizeOrigin, VldError};
5use crate::schema::VldSchema;
6
7/// Schema for socket addresses (`ipv4:port` / `[ipv6]:port`; hostnames are
8/// rejected). Created via [`vld::socket_addr()`](crate::socket_addr).
9#[derive(Clone)]
10pub struct ZSocketAddr {
11    min_port: Option<u16>,
12    max_port: Option<u16>,
13    custom_type_error: Option<String>,
14}
15
16impl ZSocketAddr {
17    pub fn new() -> Self {
18        Self {
19            min_port: None,
20            max_port: None,
21            custom_type_error: None,
22        }
23    }
24
25    pub fn type_error(mut self, msg: impl Into<String>) -> Self {
26        self.custom_type_error = Some(msg.into());
27        self
28    }
29
30    pub fn min_port(mut self, p: u16) -> Self {
31        self.min_port = Some(p);
32        self
33    }
34
35    pub fn max_port(mut self, p: u16) -> Self {
36        self.max_port = Some(p);
37        self
38    }
39}
40
41impl Default for ZSocketAddr {
42    fn default() -> Self {
43        Self::new()
44    }
45}
46
47impl VldSchema for ZSocketAddr {
48    type Output = SocketAddr;
49
50    fn parse_value(&self, value: &Value) -> Result<Self::Output, VldError> {
51        let s = value.as_str().ok_or_else(|| {
52            crate::primitives::type_error(&self.custom_type_error, "socket_addr", value)
53        })?;
54        let addr = s.parse::<SocketAddr>().map_err(|_| {
55            VldError::from_code_with_value(
56                IssueCode::InvalidFormat {
57                    format: FormatKind::SocketAddr,
58                },
59                value,
60            )
61        })?;
62        let port = addr.port();
63        if let Some(min) = self.min_port {
64            if port < min {
65                return Err(VldError::from_code_with_value(
66                    IssueCode::TooSmall {
67                        origin: SizeOrigin::Port,
68                        minimum: min.into(),
69                        inclusive: true,
70                    },
71                    value,
72                ));
73            }
74        }
75        if let Some(max) = self.max_port {
76            if port > max {
77                return Err(VldError::from_code_with_value(
78                    IssueCode::TooBig {
79                        origin: SizeOrigin::Port,
80                        maximum: max.into(),
81                        inclusive: true,
82                    },
83                    value,
84                ));
85            }
86        }
87        Ok(addr)
88    }
89}
90
91#[cfg(feature = "openapi")]
92impl crate::openapi::JsonSchema for ZSocketAddr {
93    fn json_schema(&self) -> serde_json::Value {
94        serde_json::json!({
95            "type": "string",
96            "format": "socket-addr"
97        })
98    }
99}
100
101#[cfg(feature = "openapi")]
102impl crate::openapi::CollectNestedSchemas for ZSocketAddr {}