garde/rules/
ip.rs

1//! IP validation.
2//!
3//! ```rust
4//! #[derive(garde::Validate)]
5//! struct Test {
6//!     #[garde(ip)]
7//!     v: String,
8//! }
9//! ```
10//!
11//! The entrypoint is the [`Ip`] trait. Implementing this trait for a type allows that type to be used with the `#[garde(ip)]` rule.
12//!
13//! This trait has a blanket implementation for all `T: garde::rules::AsStr`.
14
15use std::fmt::Display;
16
17use super::AsStr;
18use crate::error::Error;
19
20pub fn apply<T: Ip>(v: &T, (kind,): (IpKind,)) -> Result<(), Error> {
21    if v.validate_ip(kind).is_err() {
22        return Err(Error::new(format!("not a valid {kind} address")));
23    }
24    Ok(())
25}
26
27pub trait Ip {
28    type Error: Display;
29
30    fn validate_ip(&self, kind: IpKind) -> Result<(), Self::Error>;
31}
32
33#[derive(Clone, Copy)]
34pub enum IpKind {
35    Any,
36    V4,
37    V6,
38}
39
40impl Display for IpKind {
41    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
42        match self {
43            IpKind::Any => write!(f, "IP"),
44            IpKind::V4 => write!(f, "IPv4"),
45            IpKind::V6 => write!(f, "IPv6"),
46        }
47    }
48}
49
50impl<T: AsStr> Ip for T {
51    type Error = std::net::AddrParseError;
52
53    fn validate_ip(&self, kind: IpKind) -> Result<(), Self::Error> {
54        let v = self.as_str();
55        match kind {
56            IpKind::Any => {
57                let _ = v.parse::<std::net::IpAddr>()?;
58            }
59            IpKind::V4 => {
60                let _ = v.parse::<std::net::Ipv4Addr>()?;
61            }
62            IpKind::V6 => {
63                let _ = v.parse::<std::net::Ipv6Addr>()?;
64            }
65        };
66        Ok(())
67    }
68}
69
70impl<T: Ip> Ip for Option<T> {
71    type Error = T::Error;
72
73    fn validate_ip(&self, kind: IpKind) -> Result<(), Self::Error> {
74        match self {
75            Some(value) => value.validate_ip(kind),
76            None => Ok(()),
77        }
78    }
79}