Documentation
/*
==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--

Dia-IP-Range

Copyright (C) 2021-2024  Anonymous

There are several releases over multiple years,
they are listed as ranges, such as: "2021-2024".

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License
along with this program.  If not, see <https://www.gnu.org/licenses/>.

::--::--::--::--::--::--::--::--::--::--::--::--::--::--::--::--
*/

//! # IPv4 component range

use core::ops::{Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive};

mod impls;

const ONE: u8 = 1;

/// # IPv4 component range
///
/// A range can be respresented in string, following these rules:
///
/// - A single `u8` value can be used. It will be parsed as a one-value range.
/// - Start and end are placed inside one of `[]`, `[)`..., separated by a comma.
/// - `[` and `]` are inclusive.
/// - `(` and `)` are exclusive.
/// - White spaces can be included. They will be ignored by parser.
/// - Length of the string must be equal to or smaller than 16 bytes. It's for protection against flood attack.
///
/// This struct can be used to make [`IPv4Range`][struct:IPv4Range].
///
/// ## Examples
///
/// ```
/// use {
///     core::str::FromStr,
///     dia_ip_range::IPv4ComponentRange,
/// };
///
/// assert_eq!(
///     IPv4ComponentRange::from_str("[2,100)")?,
///     IPv4ComponentRange::from(2..100),
/// );
/// assert_eq!(
///     IPv4ComponentRange::from_str("(9,99]")?,
///     IPv4ComponentRange::from(10..100),
/// );
///
/// # dia_ip_range::Result::Ok(())
/// ```
///
/// [struct:IPv4Range]: struct.IPv4Range.html
#[derive(Debug, Eq, PartialEq, Hash, Clone)]
pub struct IPv4ComponentRange {
    start: Option<u8>,
    end: Option<u8>,
}

impl From<u8> for IPv4ComponentRange {

    fn from(n: u8) -> Self {
        Self {
            start: Some(n),
            end: Some(n),
        }
    }

}

impl From<Range<u8>> for IPv4ComponentRange {

    fn from(range: Range<u8>) -> Self {
        Self {
            start: Some(range.start),
            end: range.end.checked_sub(ONE),
        }
    }

}

impl From<RangeFrom<u8>> for IPv4ComponentRange {

    fn from(range: RangeFrom<u8>) -> Self {
        Self {
            start: Some(range.start),
            end: Some(u8::max_value()),
        }
    }

}

impl From<RangeFull> for IPv4ComponentRange {

    fn from(_: RangeFull) -> Self {
        Self {
            start: Some(u8::min_value()),
            end: Some(u8::max_value()),
        }
    }

}

impl From<RangeInclusive<u8>> for IPv4ComponentRange {

    fn from(range: RangeInclusive<u8>) -> Self {
        Self {
            start: Some(*range.start()),
            end: Some(*range.end()),
        }
    }

}

impl From<RangeTo<u8>> for IPv4ComponentRange {

    fn from(range: RangeTo<u8>) -> Self {
        Self {
            start: Some(u8::min_value()),
            end: range.end.checked_sub(ONE),
        }
    }

}

impl From<RangeToInclusive<u8>> for IPv4ComponentRange {

    fn from(range: RangeToInclusive<u8>) -> Self {
        Self {
            start: Some(u8::min_value()),
            end: Some(range.end),
        }
    }

}

impl Iterator for IPv4ComponentRange {

    type Item = u8;

    fn next(&mut self) -> Option<Self::Item> {
        match (self.start, self.end) {
            (Some(start), Some(end)) if start <= end => {
                self.start = start.checked_add(ONE);
                Some(start)
            },
            _ => None,
        }
    }

    fn nth(&mut self, mut n: usize) -> Option<Self::Item> {
        while n > 0 {
            match (self.start, self.end) {
                (Some(start), Some(end)) => match end.checked_sub(start).map(|skip| usize::from(skip).min(n)) {
                    Some(skip) if skip > 0 => {
                        self.start = Some(start + skip as u8);
                        n -= skip;
                    },
                    _ => self.start = None,
                },
                _ => return None,
            };
        }

        self.next()
    }

    // See ExactSizeIterator implementation for details.
    #[cfg(not(target_pointer_width = "8"))]
    fn size_hint(&self) -> (usize, Option<usize>) {
        match (self.start, self.end) {
            (Some(start), Some(end)) if end >= start => {
                let result = usize::from(end - start);
                match result.checked_add(ONE.into()) {
                    None => (result, None),
                    Some(result) => (result, Some(result)),
                }
            },
            _ => (0, Some(0)),
        }
    }

}

// Size of `0..=255` is `256`. `usize` on machines with 8-bit pointer width cannot hold such value. Provided function ExactSizeIterator::len()
// will panic in such case.
#[cfg(not(target_pointer_width = "8"))]
impl ExactSizeIterator for IPv4ComponentRange {}

#[test]
fn test_iterator_nth() -> crate::Result<()> {
    use alloc::vec::Vec;

    for (range, size_hint, values) in [
        (0..5, (5, Some(5)), (0_usize..=9).collect::<Vec<_>>()), (0..255, (255, Some(255)), (0..512).collect()),
    ] {
        for n in values {
            let mut ipv4_component_range = IPv4ComponentRange::from(range.clone());

            assert_eq!(ipv4_component_range.size_hint(), size_hint);
            assert_eq!(size_hint.0, ipv4_component_range.clone().count());

            if n < range.end.into() {
                assert_eq!(n, ipv4_component_range.nth(n).unwrap().into());
            } else {
                assert!(ipv4_component_range.nth(n).is_none());
                assert!(ipv4_component_range.next().is_none());
            }
        }
    }
    for (range, size_hint, values) in [
        (0..=5, (6, Some(6)), (0_usize..=9).collect::<Vec<_>>()), (0..=255, (256, Some(256)), (0..512).collect()),
    ] {
        for n in values {
            let mut ipv4_component_range = IPv4ComponentRange::from(range.clone());

            assert_eq!(ipv4_component_range.size_hint(), size_hint);
            assert_eq!(size_hint.0, ipv4_component_range.clone().count());

            if n <= (*range.end()).into() {
                assert_eq!(n, ipv4_component_range.nth(n).unwrap().into());
            } else {
                assert!(ipv4_component_range.nth(n).is_none());
                assert!(ipv4_component_range.next().is_none());
            }
        }
    }

    Ok(())
}

#[test]
fn test_iterator() -> crate::Result<()> {
    use core::str::FromStr;

    for range in ["[1,0]", "(0,0)", "(0,1)"] {
        assert_eq!(IPv4ComponentRange::from_str(range)?.count(), 0);
    }
    for (range, expected) in [("0", 0), ("1", 1), ("[0,0]", 0), ("(0,1]", 1), ("(1,3)", 2)] {
        let mut range = IPv4ComponentRange::from_str(range)?;
        assert_eq!(range.size_hint(), (1, Some(1)));
        assert_eq!(range.nth(0), Some(expected));
        assert_eq!(range.next(), None);
    }
    for (range, expected) in [
        ("(0,2)", None), ("(0,3)", Some(2)), ("(1,4)", Some(3)),
        ("[0,2]", Some(1)), ("[0,3]", Some(1)), ("[1,4]", Some(2)),
        ("[0,2)", Some(1)), ("[0,1)", None),
        ("(1,3]", Some(3)), ("(1,2]", None),
    ] {
        let mut range = IPv4ComponentRange::from_str(range)?;
        assert_eq!(range.nth(1), expected);
    }

    Ok(())
}