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 range

use {
    core::str::FromStr,
    crate::{Error, IPv4ComponentRange},
};

#[cfg(feature="iter")]
#[doc(cfg(feature="iter"))]
mod ip_v4_range_iter;

#[cfg(feature="iter")]
#[doc(cfg(feature="iter"))]
pub use self::ip_v4_range_iter::*;

/// # IPv4 range
///
/// A range can be respresented in string, following these rules:
///
/// - 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 64 bytes. It's for protection against flood attack.
///
/// Currently this struct is itself useless. You need to enable feature `iter`, in order to use [`IPv4RangeIter`][struct::IPv4RangeIter].
///
/// ## Examples
///
/// ```
/// use {
///     core::str::FromStr,
///     dia_ip_range::IPv4Range,
/// };
///
/// assert_eq!(
///     IPv4Range::from_str("10.0.0.[2,100)")?,
///     IPv4Range::from((10, 0, 0..=0, 2..100)),
/// );
///
/// # Ok::<_, dia_ip_range::Error>(())
/// ```
///
/// [struct::IPv4RangeIter]: struct.IPv4RangeIter.html
#[derive(Debug, Eq, PartialEq, Hash, Clone)]
pub struct IPv4Range {
    a: IPv4ComponentRange,
    b: IPv4ComponentRange,
    c: IPv4ComponentRange,
    d: IPv4ComponentRange,
}

impl FromStr for IPv4Range {

    type Err = Error;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        const MAX_LEN: usize = 64;

        if s.len() > MAX_LEN {
            return Err(err!("String is too long, max length supported: {} bytes", MAX_LEN));
        }

        let mut parts = s.trim().split('.');
        match (parts.next(), parts.next(), parts.next(), parts.next(), parts.next()) {
            (Some(a), Some(b), Some(c), Some(d), None) => Ok(Self {
                a: IPv4ComponentRange::from_str(a)?,
                b: IPv4ComponentRange::from_str(b)?,
                c: IPv4ComponentRange::from_str(c)?,
                d: IPv4ComponentRange::from_str(d)?,
            }),
            _ => Err(err!("Invalid IPv4 range: {:?}", s)),
        }
    }

}

impl<A, B, C, D> From<(A, B, C, D)> for IPv4Range
where A: Into<IPv4ComponentRange>, B: Into<IPv4ComponentRange>, C: Into<IPv4ComponentRange>, D: Into<IPv4ComponentRange> {

    fn from((a, b, c, d): (A, B, C, D)) -> Self {
        Self {
            a: a.into(),
            b: b.into(),
            c: c.into(),
            d: d.into(),
        }
    }

}

#[test]
fn tests() -> crate::Result<()> {
    for s in &["0.0.0.0", "[255,255].[255,255].[255,255].[255,255]", "192.168.[0,9].[9,10)", "192.168.[    0   ,9].[9  ,    10)"] {
        IPv4Range::from_str(s)?;
    }
    for s in &["0.0.0", "192.168.[09].[9-10)", "192.168.[    0   ,9.9  ,    10)"] {
        IPv4Range::from_str(s).unwrap_err();
    }

    Ok(())
}