ip/concrete/prefix/
ord.rs

1use core::cmp::Ordering::{self, Equal, Greater, Less};
2
3use super::{Address, Prefix};
4use crate::traits::Afi;
5
6/// Ordering relationship between a pair of [`Prefix<A>`] `P` and `Q`.
7#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
8pub enum PrefixOrdering<A: Afi> {
9    /// `Q` is equal to `P`.
10    Equal,
11    /// `Q` is a subprefix of `P`.
12    Subprefix(Prefix<A>),
13    /// `Q` is a superprefix of `P`.
14    Superprefix(Prefix<A>),
15    /// Neither `P` nor `Q` is a subprefix of the other.
16    Divergent(Prefix<A>),
17}
18
19impl<A: Afi> Prefix<A> {
20    /// Perform ordinal comparison with another [`Prefix<A>`], calculating the
21    /// longest common prefix in the process.
22    pub fn compare(&self, other: &Self) -> PrefixOrdering<A> {
23        let common = self.common_with(other);
24        match (
25            self.length().cmp(&common.length()),
26            other.length().cmp(&common.length()),
27        ) {
28            (Equal, Equal) => PrefixOrdering::Equal,
29            (Equal, Greater) => PrefixOrdering::Subprefix(common),
30            (Greater, Equal) => PrefixOrdering::Superprefix(common),
31            (Greater, Greater) => PrefixOrdering::Divergent(common),
32            (Less, _) | (_, Less) => {
33                unreachable!("common must be shorter than both prefixes")
34            }
35        }
36    }
37}
38
39impl<A: Afi> PartialOrd<Self> for Prefix<A> {
40    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
41        match self.compare(other) {
42            PrefixOrdering::Equal => Some(Equal),
43            PrefixOrdering::Subprefix(_) => Some(Greater),
44            PrefixOrdering::Superprefix(_) => Some(Less),
45            PrefixOrdering::Divergent(_) => None,
46        }
47    }
48}
49
50impl<A: Afi> PartialEq<Address<A>> for Prefix<A> {
51    fn eq(&self, other: &Address<A>) -> bool {
52        self.eq(&Self::from(*other))
53    }
54}
55
56impl<A: Afi> PartialOrd<Address<A>> for Prefix<A> {
57    fn partial_cmp(&self, other: &Address<A>) -> Option<Ordering> {
58        self.partial_cmp(&Self::from(*other))
59    }
60}
61
62#[cfg(test)]
63mod tests {
64    use super::*;
65    use crate::{Ipv4, Ipv6};
66
67    mod ipv4 {
68        use super::*;
69
70        #[test]
71        fn equal() {
72            let x = "10.0.0.0/8".parse::<Prefix<Ipv4>>().unwrap();
73            let y = "10.0.0.0/8".parse::<Prefix<Ipv4>>().unwrap();
74            assert!(x == y);
75        }
76
77        #[test]
78        fn lt() {
79            let x = "10.0.0.0/16".parse::<Prefix<Ipv4>>().unwrap();
80            let y = "10.0.0.0/8".parse::<Prefix<Ipv4>>().unwrap();
81            assert!(x < y);
82        }
83
84        #[test]
85        fn gt() {
86            let x = "10.0.0.0/16".parse::<Prefix<Ipv4>>().unwrap();
87            let y = "10.0.0.0/24".parse::<Prefix<Ipv4>>().unwrap();
88            assert!(x > y);
89        }
90
91        #[test]
92        fn divergent() {
93            let x = "10.0.0.0/16".parse::<Prefix<Ipv4>>().unwrap();
94            let y = "10.1.0.0/16".parse::<Prefix<Ipv4>>().unwrap();
95            assert!(x.partial_cmp(&y).is_none());
96        }
97    }
98
99    mod ipv6 {
100        use super::*;
101
102        #[test]
103        fn equal() {
104            let x = "2001:db8::/32".parse::<Prefix<Ipv6>>().unwrap();
105            let y = "2001:db8::/32".parse::<Prefix<Ipv6>>().unwrap();
106            assert!(x == y);
107        }
108
109        #[test]
110        fn lt() {
111            let x = "2001:db8::/48".parse::<Prefix<Ipv6>>().unwrap();
112            let y = "2001:db8::/32".parse::<Prefix<Ipv6>>().unwrap();
113            assert!(x < y);
114        }
115
116        #[test]
117        fn gt() {
118            let x = "2001:db8::/48".parse::<Prefix<Ipv6>>().unwrap();
119            let y = "2001:db8::/64".parse::<Prefix<Ipv6>>().unwrap();
120            assert!(x > y);
121        }
122
123        #[test]
124        fn divergent() {
125            let x = "2001:db8:f::/48".parse::<Prefix<Ipv6>>().unwrap();
126            let y = "2001:db8:a::/48".parse::<Prefix<Ipv6>>().unwrap();
127            assert!(x.partial_cmp(&y).is_none());
128        }
129    }
130}