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() {
let ip_str = "127.0.0.1";
let ip: EgressIpRange = ip_str.parse().unwrap();
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() {
let ip_str = "127.0.0.1/32";
let ip: EgressIpRange = ip_str.parse().unwrap();
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() {
let ip_str = "127.0.0.1/24";
let ip: EgressIpRange = ip_str.parse().unwrap();
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() {
let ip_str = "127.0.0.1/thisiswrong";
let ip = ip_str.parse::<EgressIpRange>().unwrap_err();
assert_eq!(ip, EgressIpRangeParseError(IpNetworkError::InvalidPrefix))
}
#[test]
fn to_string__bare_ip_address_stringifies_with_mask_suffix() {
let ip: EgressIpRange = "127.0.0.1".parse().unwrap();
let ip_str = ip.to_string();
assert_eq!(ip_str, "127.0.0.1/32")
}
#[test]
fn to_string__bare_ip_address_with_mask_suffix_stringifies_correctly() {
let ip: EgressIpRange = "127.0.0.1/24".parse().unwrap();
let ip_str = ip.to_string();
assert_eq!(ip_str, "127.0.0.1/24")
}
}