http_acl/
utils.rs

1//! Utility functions for the http-acl crate.
2
3use std::collections::HashSet;
4use std::hash::Hash;
5use std::net::IpAddr;
6use std::ops::RangeInclusive;
7
8use ipnet::IpNet;
9
10pub mod authority;
11pub(crate) mod ip;
12pub mod url;
13
14// Taken from https://stackoverflow.com/a/46767732
15pub(crate) fn has_unique_elements<T>(iter: T) -> bool
16where
17    T: IntoIterator,
18    T::Item: Eq + Hash,
19{
20    let mut uniq = HashSet::new();
21    iter.into_iter().all(move |x| uniq.insert(x))
22}
23
24/// Helper function to check if any ranges in a slice overlap.
25pub(crate) fn has_overlapping_ranges<T: Ord + Clone>(ranges: &[RangeInclusive<T>]) -> bool {
26    let mut sorted = ranges.to_vec();
27    sorted.sort_by(|a, b| a.start().cmp(b.start()));
28    for pair in sorted.windows(2) {
29        if let [a, b] = pair {
30            if a.end() >= b.start() {
31                return true;
32            }
33        }
34    }
35    false
36}
37
38/// Checks if a range overlaps with any existing ranges in a slice.
39#[inline]
40pub(crate) fn range_overlaps<T: Ord + Clone>(
41    ranges: &[RangeInclusive<T>],
42    range: &RangeInclusive<T>,
43    self_index: Option<usize>,
44) -> bool {
45    ranges
46        .iter()
47        .enumerate()
48        .filter_map(|(i, r)| {
49            self_index.map_or(Some(r), |index| if i != index { Some(r) } else { None })
50        })
51        .any(|r| r.start() <= range.end() && r.end() >= range.start())
52}
53
54/// Converts a type into an IP range.
55pub trait IntoIpRange {
56    /// Converts the type into an IP range.
57    fn into_range(self) -> Option<RangeInclusive<IpAddr>>;
58
59    /// Validates the IP range.
60    fn validate(ip_range: RangeInclusive<IpAddr>) -> Option<RangeInclusive<IpAddr>> {
61        if ip_range.start() <= ip_range.end() {
62            Some(ip_range)
63        } else {
64            None
65        }
66    }
67}
68
69impl IntoIpRange for IpNet {
70    fn into_range(self) -> Option<RangeInclusive<IpAddr>> {
71        let start = self.network();
72        let end = self.broadcast();
73        Some(start..=end)
74    }
75}
76
77impl IntoIpRange for RangeInclusive<IpAddr> {
78    fn into_range(self) -> Option<RangeInclusive<IpAddr>> {
79        Self::validate(self)
80    }
81}
82
83impl IntoIpRange for (IpAddr, IpAddr) {
84    fn into_range(self) -> Option<RangeInclusive<IpAddr>> {
85        Self::validate(self.0..=self.1)
86    }
87}