prefix-trie 0.8.3

Prefix trie (tree) datastructure (both a set and a map) that provides exact and longest-prefix matches.
Documentation
use crate::joint::{JointPrefix, JointPrefixMap, JointPrefixSet};
use crate::*;
use ipnet::{Ipv4Net, Ipv6Net};
use num_traits::{NumCast, PrimInt};
use std::fmt::Debug;

fn ipv4<P: Prefix>(s: &str) -> P {
    let ip: Ipv4Net = s.parse().unwrap();
    let r = ip.addr().to_bits();
    let len = ip.prefix_len();

    let type_len = P::zero().repr().count_zeros() as usize;
    assert!(type_len == 32);

    let r: <P as Prefix>::R = <<P as Prefix>::R as NumCast>::from(r).unwrap();
    P::from_repr_len(r, len)
}

fn ipv6<P: Prefix>(s: &str) -> P {
    let ip: Ipv6Net = s.parse().unwrap();
    let r = ip.addr().to_bits();
    let len = ip.prefix_len();

    let type_len = P::zero().repr().count_zeros() as usize;
    assert!(type_len == 128);

    let r: <P as Prefix>::R = <<P as Prefix>::R as NumCast>::from(r).unwrap();
    P::from_repr_len(r, len)
}

fn ip<P: JointPrefix + Debug + PartialEq>(s: &str) -> P {
    if s.contains(":") {
        P::from_p2(&ipv6(s))
    } else {
        P::from_p1(&ipv4(s))
    }
}

#[generic_tests::define]
mod set {
    use super::*;

    #[test]
    fn iter<P: JointPrefix + Debug + PartialEq>() {
        let mut set: JointPrefixSet<P> = JointPrefixSet::new();
        set.insert(ip("192.168.1.0/24"));
        set.insert(ip("192.168.0.0/24"));
        set.insert(ip("2001::1:0:0/96"));
        assert_eq!(
            set.iter().collect::<Vec<_>>(),
            vec![
                ip("192.168.0.0/24"),
                ip("192.168.1.0/24"),
                ip("2001::1:0:0/96")
            ],
        );
    }

    #[test]
    fn into_iter<P: JointPrefix + Debug + PartialEq>() {
        let mut set: JointPrefixSet<P> = JointPrefixSet::new();
        set.insert(ip("192.168.1.0/24"));
        set.insert(ip("192.168.0.0/24"));
        set.insert(ip("2001::1:0:0/96"));
        assert_eq!(
            set.into_iter().collect::<Vec<_>>(),
            vec![
                ip("192.168.0.0/24"),
                ip("192.168.1.0/24"),
                ip("2001::1:0:0/96")
            ],
        );
    }

    #[test]
    fn children<P: JointPrefix + Debug + PartialEq>() {
        let mut set: JointPrefixSet<P> = JointPrefixSet::new();
        set.insert(ip("192.168.0.0/22"));
        set.insert(ip("192.168.0.0/23"));
        set.insert(ip("192.168.2.0/23"));
        set.insert(ip("192.168.0.0/24"));
        set.insert(ip("192.168.2.0/24"));
        assert_eq!(
            set.children(&ip("192.168.0.0/23")).collect::<Vec<_>>(),
            vec![ip("192.168.0.0/23"), ip("192.168.0.0/24"),]
        );

        set.insert(ip("2001:0::/30"));
        set.insert(ip("2001:0::/31"));
        set.insert(ip("2001:2::/31"));
        set.insert(ip("2001:0::/32"));
        set.insert(ip("2001:2::/32"));
        assert_eq!(
            set.children(&ip("2001:0::/31")).collect::<Vec<_>>(),
            vec![ip("2001:0::/31"), ip("2001:0::/32"),]
        );
    }

    #[test]
    fn cover<P: JointPrefix + Debug + PartialEq>() {
        let mut set: JointPrefixSet<P> = JointPrefixSet::new();
        set.insert(ip("192.168.0.0/22"));
        set.insert(ip("192.168.0.0/23"));
        set.insert(ip("192.168.2.0/23"));
        set.insert(ip("192.168.0.0/24"));
        set.insert(ip("192.168.2.0/24"));
        assert_eq!(
            set.cover(&ip("192.168.2.0/24")).collect::<Vec<_>>(),
            vec![
                ip("192.168.0.0/22"),
                ip("192.168.2.0/23"),
                ip("192.168.2.0/24"),
            ]
        );

        set.insert(ip("2001:0::/30"));
        set.insert(ip("2001:0::/31"));
        set.insert(ip("2001:2::/31"));
        set.insert(ip("2001:0::/32"));
        set.insert(ip("2001:2::/32"));
        assert_eq!(
            set.cover(&ip("2001:2::/32")).collect::<Vec<_>>(),
            vec![ip("2001:0::/30"), ip("2001:2::/31"), ip("2001:2::/32"),]
        );
    }

    #[instantiate_tests(<::either::Either<(u32, u8), (u128, u8)>>)]
    mod either {}

    #[cfg(feature = "ipnet")]
    #[instantiate_tests(<::ipnet::IpNet>)]
    mod ipnet {}

    #[cfg(feature = "ipnetwork")]
    #[instantiate_tests(<::ipnetwork::IpNetwork>)]
    mod ipnetwork {}

    #[cfg(feature = "cidr")]
    #[instantiate_tests(<::cidr::IpCidr>)]
    mod cidr {}
}

#[generic_tests::define]
mod map {
    use super::*;

    #[test]
    fn iter<P: JointPrefix + Debug + PartialEq>() {
        let mut map: JointPrefixMap<P, usize> = JointPrefixMap::new();
        map.insert(ip("192.168.1.0/24"), 0);
        map.insert(ip("192.168.0.0/24"), 1);
        map.insert(ip("2001::1:0:0/96"), 2);
        assert_eq!(
            map.iter().collect::<Vec<_>>(),
            vec![
                (ip("192.168.0.0/24"), &1),
                (ip("192.168.1.0/24"), &0),
                (ip("2001::1:0:0/96"), &2)
            ],
        );
    }

    #[test]
    fn into_iter<P: JointPrefix + Debug + PartialEq>() {
        let mut map: JointPrefixMap<P, usize> = JointPrefixMap::new();
        map.insert(ip("192.168.1.0/24"), 0);
        map.insert(ip("192.168.0.0/24"), 1);
        map.insert(ip("2001::1:0:0/96"), 2);
        assert_eq!(
            map.into_iter().collect::<Vec<_>>(),
            vec![
                (ip("192.168.0.0/24"), 1),
                (ip("192.168.1.0/24"), 0),
                (ip("2001::1:0:0/96"), 2)
            ],
        );
    }

    #[test]
    fn children<P: JointPrefix + Debug + PartialEq>() {
        let mut map: JointPrefixMap<P, usize> = JointPrefixMap::new();
        map.insert(ip("192.168.0.0/22"), 0);
        map.insert(ip("192.168.0.0/23"), 1);
        map.insert(ip("192.168.2.0/23"), 2);
        map.insert(ip("192.168.0.0/24"), 3);
        map.insert(ip("192.168.2.0/24"), 4);
        assert_eq!(
            map.children(&ip("192.168.0.0/23")).collect::<Vec<_>>(),
            vec![(ip("192.168.0.0/23"), &1), (ip("192.168.0.0/24"), &3)]
        );

        map.insert(ip("2001:0::/30"), 0);
        map.insert(ip("2001:0::/31"), 1);
        map.insert(ip("2001:2::/31"), 2);
        map.insert(ip("2001:0::/32"), 3);
        map.insert(ip("2001:2::/32"), 4);
        assert_eq!(
            map.children(&ip("2001:0::/31")).collect::<Vec<_>>(),
            vec![(ip("2001:0::/31"), &1), (ip("2001:0::/32"), &3)]
        );
    }

    #[test]
    fn cover<P: JointPrefix + Debug + PartialEq>() {
        let mut map: JointPrefixMap<P, usize> = JointPrefixMap::new();
        map.insert(ip("192.168.0.0/22"), 0);
        map.insert(ip("192.168.0.0/23"), 1);
        map.insert(ip("192.168.2.0/23"), 2);
        map.insert(ip("192.168.0.0/24"), 3);
        map.insert(ip("192.168.2.0/24"), 4);
        assert_eq!(
            map.cover(&ip("192.168.2.0/24")).collect::<Vec<_>>(),
            vec![
                (ip("192.168.0.0/22"), &0),
                (ip("192.168.2.0/23"), &2),
                (ip("192.168.2.0/24"), &4),
            ]
        );

        map.insert(ip("2001:0::/30"), 0);
        map.insert(ip("2001:0::/31"), 1);
        map.insert(ip("2001:2::/31"), 2);
        map.insert(ip("2001:0::/32"), 3);
        map.insert(ip("2001:2::/32"), 4);
        assert_eq!(
            map.cover(&ip("2001:2::/32")).collect::<Vec<_>>(),
            vec![
                (ip("2001:0::/30"), &0),
                (ip("2001:2::/31"), &2),
                (ip("2001:2::/32"), &4)
            ]
        );
    }

    #[instantiate_tests(<::either::Either<(u32, u8), (u128, u8)>>)]
    mod either {}

    #[cfg(feature = "ipnet")]
    #[instantiate_tests(<::ipnet::IpNet>)]
    mod ipnet {}

    #[cfg(feature = "ipnetwork")]
    #[instantiate_tests(<::ipnetwork::IpNetwork>)]
    mod ipnetwork {}

    #[cfg(feature = "cidr")]
    #[instantiate_tests(<::cidr::IpCidr>)]
    mod cidr {}
}