// License: see LICENSE file at root directory of `master` branch

//! # IPv4 range iter

#![cfg(feature="iter")]

use {
    core::{
        convert::TryFrom,
        str::FromStr,
    },
    std::net::{IpAddr, Ipv4Addr},

    perm::PermIter,

    super::{Component, IPv4Range},

    crate::Error,
};

#[cfg(test)]
use {
    alloc::string::ToString,
};

/// # IPv4 range iter
///
/// ## Examples
///
/// ```
/// use {
///     core::str::FromStr,
///     std::net::{IpAddr, Ipv4Addr},
///     dia_ip_range::IPv4RangeIter,
/// };
///
/// let mut iter = IPv4RangeIter::from_str("10.0.0.[2,9]")?;
/// for sample in vec![
///     [10, 0, 0, 2], [10, 0, 0, 3], [10, 0, 0, 4], [10, 0, 0, 5],
///     [10, 0, 0, 6], [10, 0, 0, 7], [10, 0, 0, 8], [10, 0, 0, 9],
/// ] {
///     assert_eq!(iter.next().unwrap(), IpAddr::from(Ipv4Addr::from(sample)));
/// }
/// assert!(iter.next().is_none());
///
/// # Ok::<_, dia_ip_range::Error>(())
/// ```
#[derive(Debug)]
pub struct IPv4RangeIter {
    iter: PermIter<Component>,
}

impl TryFrom<&IPv4Range> for IPv4RangeIter {

    type Error = Error;

    fn try_from(ip_range: &IPv4Range) -> Result<Self, Self::Error> {
        let iter = PermIter::make(alloc::vec![
            ip_range.a.clone(),
            ip_range.b.clone(),
            ip_range.c.clone(),
            ip_range.d.clone(),
        ]).map_err(|e| err!("{}", e))?;

        Ok(Self {
            iter,
        })
    }

}

impl TryFrom<IPv4Range> for IPv4RangeIter {

    type Error = Error;

    fn try_from(ip_range: IPv4Range) -> Result<Self, Self::Error> {
        Self::try_from(&ip_range)
    }

}

impl FromStr for IPv4RangeIter {

    type Err = Error;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        Self::try_from(IPv4Range::from_str(s)?)
    }

}

impl Iterator for IPv4RangeIter {

    type Item = IpAddr;

    fn next(&mut self) -> Option<Self::Item> {
        self.iter.next().map(|data| IpAddr::V4(Ipv4Addr::from([data[0], data[1], data[2], data[3]])))
    }

}

#[test]
fn test_ip_range_iters() -> crate::Result<()> {
    let mut iter = IPv4RangeIter::try_from(IPv4Range {
        a: Component::new(192, 192),
        b: Component::new(168, 168),
        c: Component::new(0, 2),
        d: Component::new(0, 2),
    })?;
    for s in &[
        "192.168.0.0", "192.168.1.0", "192.168.2.0",
        "192.168.0.1", "192.168.1.1", "192.168.2.1",
        "192.168.0.2", "192.168.1.2", "192.168.2.2",
    ] {
        assert_eq!(iter.next().unwrap().to_string(), *s);
    }
    assert!(iter.next().is_none());

    let mut iter = IPv4RangeIter::try_from(IPv4Range {
        a: Component::new(0, 0),
        b: Component::new(0, 0),
        c: Component::new(0, 0),
        d: Component::new(0, 0),
    })?;
    for s in &["0.0.0.0"] {
        assert_eq!(iter.next().unwrap().to_string(), *s);
    }
    assert!(iter.next().is_none());

    Ok(())
}
