ntp_daemon/config/
subnet.rs

1use serde::{de, Deserialize, Deserializer};
2use std::net::{AddrParseError, IpAddr};
3use thiserror::Error;
4
5#[derive(Debug, Clone, PartialEq, Eq)]
6pub struct IpSubnet {
7    pub addr: IpAddr,
8    pub mask: u8,
9}
10
11#[derive(Debug, Clone, PartialEq, Eq, Error)]
12pub enum SubnetParseError {
13    #[error("Invalid subnet syntax")]
14    InvalidSubnet,
15    #[error("{0} in subnet")]
16    InvalidIp(#[from] AddrParseError),
17    #[error("Invalid subnet mask")]
18    InvalidMask,
19}
20
21impl std::str::FromStr for IpSubnet {
22    type Err = SubnetParseError;
23
24    fn from_str(s: &str) -> Result<Self, Self::Err> {
25        let (addr, mask) = s.split_once('/').ok_or(SubnetParseError::InvalidSubnet)?;
26        let addr: IpAddr = addr.parse()?;
27        let mask: u8 = mask.parse().map_err(|_| SubnetParseError::InvalidMask)?;
28        let max_mask = match addr {
29            IpAddr::V4(_) => 32,
30            IpAddr::V6(_) => 128,
31        };
32        if mask > max_mask {
33            return Err(SubnetParseError::InvalidMask);
34        }
35        Ok(IpSubnet { addr, mask })
36    }
37}
38
39impl<'de> Deserialize<'de> for IpSubnet {
40    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
41    where
42        D: Deserializer<'de>,
43    {
44        let s = String::deserialize(deserializer)?;
45        std::str::FromStr::from_str(&s).map_err(de::Error::custom)
46    }
47}
48
49#[cfg(test)]
50mod tests {
51    use super::*;
52
53    #[test]
54    fn test_subnet_parsing() {
55        let a = "0.0.0.0/0".parse::<IpSubnet>().unwrap();
56        assert_eq!(a.mask, 0);
57    }
58}