dia_ip_range/ip_v4_range/
ip_v4_range_iter.rs

1/*
2==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--
3
4Dia-IP-Range
5
6Copyright (C) 2021-2024  Anonymous
7
8There are several releases over multiple years,
9they are listed as ranges, such as: "2021-2024".
10
11This program is free software: you can redistribute it and/or modify
12it under the terms of the GNU Lesser General Public License as published by
13the Free Software Foundation, either version 3 of the License, or
14(at your option) any later version.
15
16This program is distributed in the hope that it will be useful,
17but WITHOUT ANY WARRANTY; without even the implied warranty of
18MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19GNU Lesser General Public License for more details.
20
21You should have received a copy of the GNU Lesser General Public License
22along with this program.  If not, see <https://www.gnu.org/licenses/>.
23
24::--::--::--::--::--::--::--::--::--::--::--::--::--::--::--::--
25*/
26
27//! # IPv4 range iter
28
29#![cfg(feature="iter")]
30#![doc(cfg(feature="iter"))]
31
32use {
33    std::net::IpAddr,
34    crate::IPv4ComponentRange,
35    super::IPv4Range,
36    perm::{Buffer, PermIter},
37};
38
39#[cfg(test)]
40use {
41    alloc::{
42        collections::BTreeSet,
43        string::ToString,
44    },
45};
46
47/// # IPv4 range iter
48///
49/// **WARNING:** The order of generated items is **not** guaranteed to be constant between crate versions.
50///
51/// ## Examples
52///
53/// ```
54/// use {
55///     core::str::FromStr,
56///     std::{
57///         collections::BTreeSet,
58///         net::IpAddr,
59///     },
60///
61///     dia_ip_range::{IPv4Range, IPv4RangeIter},
62/// };
63///
64/// let buffer = IPv4RangeIter::new_buffer();
65/// let iter = IPv4RangeIter::new(
66///     IPv4Range::from_str("10.0.0.[2,9]")?,
67///     &buffer,
68/// );
69/// assert_eq!(
70///     iter.collect::<BTreeSet<_>>(),
71///     [
72///         [10, 0, 0, 2], [10, 0, 0, 3], [10, 0, 0, 4], [10, 0, 0, 5],
73///         [10, 0, 0, 6], [10, 0, 0, 7], [10, 0, 0, 8], [10, 0, 0, 9],
74///     ].into_iter().map(|i| IpAddr::from(i)).collect(),
75/// );
76///
77/// # dia_ip_range::Result::Ok(())
78/// ```
79#[derive(Debug, Eq, PartialEq, Hash)]
80pub struct IPv4RangeIter<'a> {
81    iter: PermIter<'a, IPv4ComponentRange>,
82}
83
84impl<'a> IPv4RangeIter<'a> {
85
86    /// # Makes new buffer for use with `new()`
87    ///
88    /// This is a shortcut, so you don't have to manually declare [`perm`][crate:perm] crate in your `Cargo.toml` file.
89    ///
90    /// [crate:perm]: https://bitbucket.org/haibison/perm
91    pub fn new_buffer() -> Buffer<u8> {
92        Buffer::with_capacity(4)
93    }
94
95    /// # Makes new instance
96    ///
97    /// Output buffer is used for better performance.
98    ///
99    /// ## Panics
100    ///
101    /// Each buffer can only be used by one instance of `IPv4RangeIter`. Sharing it to multiple instances will cause panics.
102    pub fn new<I>(ip_v4_range: I, buffer: &'a Buffer<u8>) -> Self where I: Into<IPv4Range> {
103        let ip_v4_range = ip_v4_range.into();
104        Self {
105            iter: PermIter::new(
106                alloc::vec![ip_v4_range.a, ip_v4_range.b, ip_v4_range.c, ip_v4_range.d],
107                buffer,
108            ),
109        }
110    }
111
112}
113
114impl Iterator for IPv4RangeIter<'_> {
115
116    type Item = IpAddr;
117
118    fn next(&mut self) -> Option<Self::Item> {
119        self.nth(0)
120    }
121
122    fn nth(&mut self, n: usize) -> Option<Self::Item> {
123        self.iter.nth(n).map(|d| IpAddr::from([d[0], d[1], d[2], d[3]]))
124    }
125
126}
127
128#[test]
129fn test_ip_v4_range_iters() -> crate::Result<()> {
130    use {
131        alloc::borrow::Cow,
132        core::str::FromStr,
133    };
134
135    let buffer = IPv4RangeIter::new_buffer();
136
137    {
138        let iter = IPv4RangeIter::new(IPv4Range::from((192, 168, 0..=2, 0..=2)), &buffer);
139        assert_eq!(
140            [
141                "192.168.0.0", "192.168.1.0", "192.168.2.0",
142                "192.168.0.1", "192.168.1.1", "192.168.2.1",
143                "192.168.0.2", "192.168.1.2", "192.168.2.2",
144            ].into_iter().map(|s| Cow::Borrowed(s)).collect::<BTreeSet<_>>(),
145            iter.map(|i| i.to_string().into()).collect(),
146        );
147    }
148
149    {
150        let mut iter = IPv4RangeIter::new(IPv4Range::from((0, 0, 0, 0)), &buffer);
151        assert_eq!(iter.next().unwrap().to_string(), "0.0.0.0");
152        assert!(iter.next().is_none());
153    }
154
155    for ip in ["0.0.0.[1,0]", "0.0.0.(0,0)", "0.0.0.(0,1)"] {
156        assert_eq!(IPv4RangeIter::new(IPv4Range::from_str(ip)?, &buffer).count(), 0);
157    }
158    for ip in ["0.1.0.1", "0.0.0.[0,0]", "0.0.0.[0,1)", "0.(0,1].0.0", "0.0.0.(1,3)"] {
159        assert_eq!(IPv4RangeIter::new(IPv4Range::from_str(ip)?, &buffer).count(), 1);
160    }
161
162    Ok(())
163}