chain-spec-generator 13.0.1-beta.196

Generates a chainspec artifact for use in genesis block creation of a Xand network.
Documentation
use ipnetwork::{IpNetworkError, Ipv4Network};
use serde::Deserialize;
use std::{net::Ipv4Addr, str::FromStr};

#[derive(Clone, Debug, Deserialize, Eq, PartialEq)]
pub struct EgressIpRange(Ipv4Network);

impl EgressIpRange {
    pub fn as_allowlist_json(&self) -> serde_json::Value {
        serde_json::Value::Array(
            self.0
                .ip()
                .octets()
                .iter()
                .cloned()
                .chain(std::iter::once(self.0.prefix()))
                .map(Into::into)
                .collect::<Vec<_>>(),
        )
    }
}

#[derive(Clone, Debug, Eq, PartialEq, thiserror::Error)]
#[error("failed to parse CIDR address string: {}", .0)]
pub struct EgressIpRangeParseError(#[from] IpNetworkError);

impl From<&Ipv4Addr> for EgressIpRange {
    fn from(ip: &Ipv4Addr) -> Self {
        Self((*ip).into())
    }
}

impl FromStr for EgressIpRange {
    type Err = EgressIpRangeParseError;

    fn from_str(ip: &str) -> Result<Self, Self::Err> {
        Ok(Self(ip.parse()?))
    }
}

impl ToString for EgressIpRange {
    fn to_string(&self) -> String {
        self.0.to_string()
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn from_str__bare_ip_address_parses_correctly() {
        // Given
        let ip_str = "127.0.0.1";

        // When
        let ip: EgressIpRange = ip_str.parse().unwrap();

        // Then
        assert_eq!(
            ip,
            EgressIpRange(Ipv4Network::new(Ipv4Addr::new(127, 0, 0, 1), 32).unwrap())
        )
    }

    #[test]
    fn from_str__ip_address_with_single_ip_range_parses_correctly() {
        // Given
        let ip_str = "127.0.0.1/32";

        // When
        let ip: EgressIpRange = ip_str.parse().unwrap();

        // Then
        assert_eq!(
            ip,
            EgressIpRange(Ipv4Network::new(Ipv4Addr::new(127, 0, 0, 1), 32).unwrap())
        )
    }

    #[test]
    fn from_str__ip_address_with_partial_mask_parses_correctly() {
        // Given
        let ip_str = "127.0.0.1/24";

        // When
        let ip: EgressIpRange = ip_str.parse().unwrap();

        // Then
        assert_eq!(
            ip,
            EgressIpRange(Ipv4Network::new(Ipv4Addr::new(127, 0, 0, 1), 24).unwrap())
        )
    }

    #[test]
    fn from_str__ip_address_with_invalid_mask_fails_to_parse() {
        // Given
        let ip_str = "127.0.0.1/thisiswrong";

        // When
        let ip = ip_str.parse::<EgressIpRange>().unwrap_err();

        // Then
        assert_eq!(ip, EgressIpRangeParseError(IpNetworkError::InvalidPrefix))
    }

    #[test]
    fn to_string__bare_ip_address_stringifies_with_mask_suffix() {
        // Given
        let ip: EgressIpRange = "127.0.0.1".parse().unwrap();

        // When
        let ip_str = ip.to_string();

        // Then
        assert_eq!(ip_str, "127.0.0.1/32")
    }

    #[test]
    fn to_string__bare_ip_address_with_mask_suffix_stringifies_correctly() {
        // Given
        let ip: EgressIpRange = "127.0.0.1/24".parse().unwrap();

        // When
        let ip_str = ip.to_string();

        // Then
        assert_eq!(ip_str, "127.0.0.1/24")
    }
}