ip_network 0.4.1

IPv4 and IPv6 network structs.
Documentation
use std::fmt;
use std::str;
use serde::de::{Deserialize, Deserializer, EnumAccess, Error, Unexpected, VariantAccess, Visitor};
use serde::ser::{Serialize, Serializer};
use crate::{IpNetwork, Ipv4Network, Ipv6Network};

impl Serialize for IpNetwork {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        if serializer.is_human_readable() {
            serializer.serialize_str(&self.to_string())
        } else {
            match self {
                IpNetwork::V4(a) => serializer.serialize_newtype_variant("IpNetwork", 0, "V4", a),
                IpNetwork::V6(a) => serializer.serialize_newtype_variant("IpNetwork", 1, "V6", a),
            }
        }
    }
}

impl<'de> Deserialize<'de> for IpNetwork {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        if deserializer.is_human_readable() {
            struct IpNetworkVisitor;

            impl<'de> Visitor<'de> for IpNetworkVisitor {
                type Value = IpNetwork;

                fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
                    formatter.write_str("IP network")
                }

                fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
                where
                    E: Error,
                {
                    s.parse().map_err(Error::custom)
                }
            }

            deserializer.deserialize_str(IpNetworkVisitor)
        } else {
            enum IpNetworkKind {
                V4,
                V6,
            }

            static VARIANTS: &[&str] = &["V4", "V6"];

            impl<'de> Deserialize<'de> for IpNetworkKind {
                fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
                where
                    D: Deserializer<'de>,
                {
                    struct KindVisitor;
                    impl<'de> Visitor<'de> for KindVisitor {
                        type Value = IpNetworkKind;
                        fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
                            formatter.write_str("`V4` or `V6`")
                        }
                        fn visit_u32<E: Error>(self, value: u32) -> Result<Self::Value, E> {
                            match value {
                                0 => Ok(IpNetworkKind::V4),
                                1 => Ok(IpNetworkKind::V6),
                                _ => Err(Error::invalid_value(
                                    Unexpected::Unsigned(value as u64),
                                    &self,
                                )),
                            }
                        }
                        fn visit_str<E: Error>(self, value: &str) -> Result<Self::Value, E> {
                            match value {
                                "V4" => Ok(IpNetworkKind::V4),
                                "V6" => Ok(IpNetworkKind::V6),
                                _ => Err(Error::unknown_variant(value, VARIANTS)),
                            }
                        }
                        fn visit_bytes<E: Error>(self, value: &[u8]) -> Result<Self::Value, E> {
                            match value {
                                b"V4" => Ok(IpNetworkKind::V4),
                                b"V6" => Ok(IpNetworkKind::V6),
                                _ => match str::from_utf8(value) {
                                    Ok(value) => Err(Error::unknown_variant(value, VARIANTS)),
                                    Err(_) => {
                                        Err(Error::invalid_value(Unexpected::Bytes(value), &self))
                                    }
                                },
                            }
                        }
                    }
                    deserializer.deserialize_identifier(KindVisitor)
                }
            }

            struct EnumVisitor;

            impl<'de> Visitor<'de> for EnumVisitor {
                type Value = IpNetwork;

                fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
                    formatter.write_str("IP network")
                }

                fn visit_enum<A>(self, data: A) -> Result<Self::Value, A::Error>
                where
                    A: EnumAccess<'de>,
                {
                    match data.variant()? {
                        (IpNetworkKind::V4, v) => v.newtype_variant().map(IpNetwork::V4),
                        (IpNetworkKind::V6, v) => v.newtype_variant().map(IpNetwork::V6),
                    }
                }
            }

            deserializer.deserialize_enum("IpNetwork", VARIANTS, EnumVisitor)
        }
    }
}

macro_rules! ser_de_impl {
    ($expecting:tt $ty:ty) => {
        impl Serialize for $ty {
            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
            where
                S: Serializer,
            {
                if serializer.is_human_readable() {
                    serializer.serialize_str(&self.to_string())
                } else {
                    (self.network_address(), self.netmask()).serialize(serializer)
                }
            }
        }

        impl<'de> Deserialize<'de> for $ty {
            fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
            where
                D: Deserializer<'de>,
            {
                if deserializer.is_human_readable() {
                    struct IpNetworkVisitor;

                    impl<'de> Visitor<'de> for IpNetworkVisitor {
                        type Value = $ty;

                        fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
                            formatter.write_str($expecting)
                        }

                        fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
                        where
                            E: Error,
                        {
                            s.parse().map_err(Error::custom)
                        }
                    }

                    deserializer.deserialize_str(IpNetworkVisitor)
                } else {
                    let (network_address, netmask) = <(_, u8)>::deserialize(deserializer)?;
                    Self::new(network_address, netmask).map_err(Error::custom)
                }
            }
        }
    };
}

ser_de_impl!("IPv4 network" Ipv4Network);
ser_de_impl!("IPv6 network" Ipv6Network);

#[cfg(test)]
mod tests {
    use crate::{IpNetwork, Ipv4Network, Ipv6Network};
    use serde_test::{assert_tokens, Configure, Token};
    use std::net::{Ipv4Addr, Ipv6Addr};

    #[test]
    fn ip_network_serialize_readable() {
        let ip_network = IpNetwork::new(Ipv4Addr::new(1, 2, 3, 4), 32).unwrap();

        assert_tokens(&ip_network.readable(), &[Token::BorrowedStr("1.2.3.4/32")]);
    }

    #[test]
    fn ip_network_serialize_compact() {
        let ip_network = IpNetwork::new(Ipv4Addr::new(1, 2, 3, 4), 32).unwrap();

        assert_tokens(
            &ip_network.compact(),
            &[
                Token::NewtypeVariant {
                    name: "IpNetwork",
                    variant: "V4",
                },
                Token::Tuple { len: 2 },
                Token::Tuple { len: 4 },
                Token::U8(1),
                Token::U8(2),
                Token::U8(3),
                Token::U8(4),
                Token::TupleEnd,
                Token::U8(32),
                Token::TupleEnd,
            ],
        );
    }

    #[test]
    fn ipv4_network_serialize_readable() {
        let ip_network = Ipv4Network::new(Ipv4Addr::new(1, 2, 3, 4), 32).unwrap();

        assert_tokens(&ip_network.readable(), &[Token::BorrowedStr("1.2.3.4/32")]);
    }

    #[test]
    fn ipv4_network_serialize_compact() {
        let ip_network = Ipv4Network::new(Ipv4Addr::new(1, 2, 3, 4), 32).unwrap();

        assert_tokens(
            &ip_network.compact(),
            &[
                Token::Tuple { len: 2 },
                Token::Tuple { len: 4 },
                Token::U8(1),
                Token::U8(2),
                Token::U8(3),
                Token::U8(4),
                Token::TupleEnd,
                Token::U8(32),
                Token::TupleEnd,
            ],
        );
    }

    #[test]
    fn ipv6_network_serialize_readable() {
        let ip_network = Ipv6Network::new(Ipv6Addr::new(1, 2, 3, 4, 0, 0, 0, 0), 64).unwrap();

        assert_tokens(
            &ip_network.readable(),
            &[Token::BorrowedStr("1:2:3:4::/64")],
        );
    }

    #[test]
    fn ipv6_network_serialize_compact() {
        let ip_network = Ipv6Network::new(Ipv6Addr::new(1, 2, 3, 4, 0, 0, 0, 0), 64).unwrap();

        assert_tokens(
            &ip_network.compact(),
            &[
                Token::Tuple { len: 2 },
                Token::Tuple { len: 16 },
                Token::U8(0),
                Token::U8(1),
                Token::U8(0),
                Token::U8(2),
                Token::U8(0),
                Token::U8(3),
                Token::U8(0),
                Token::U8(4),
                Token::U8(0),
                Token::U8(0),
                Token::U8(0),
                Token::U8(0),
                Token::U8(0),
                Token::U8(0),
                Token::U8(0),
                Token::U8(0),
                Token::TupleEnd,
                Token::U8(64),
                Token::TupleEnd,
            ],
        );
    }
}