use std::fmt;
use std::net::Ipv4Addr;
use std::str::FromStr;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct KernelIpParam {
pub client: Ipv4Addr,
pub gateway: Ipv4Addr,
pub netmask: Ipv4Addr,
}
impl fmt::Display for KernelIpParam {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"ip={}::{}:{}::eth0:off",
self.client, self.gateway, self.netmask,
)
}
}
impl FromStr for KernelIpParam {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let value = s.strip_prefix("ip=").ok_or("missing ip= prefix")?;
let mut fields = value.split(':');
let client = fields
.next()
.filter(|s| !s.is_empty())
.ok_or("missing client field")?
.parse::<Ipv4Addr>()
.map_err(|e| format!("invalid client IP: {e}"))?;
fields.next();
let gateway = fields
.next()
.filter(|s| !s.is_empty())
.ok_or("missing gateway field")?
.parse::<Ipv4Addr>()
.map_err(|e| format!("invalid gateway IP: {e}"))?;
let netmask = fields
.next()
.filter(|s| !s.is_empty())
.ok_or("missing netmask field")?
.parse::<Ipv4Addr>()
.map_err(|e| format!("invalid netmask: {e}"))?;
Ok(Self {
client,
gateway,
netmask,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn roundtrip() {
let param = KernelIpParam {
client: Ipv4Addr::new(172, 20, 0, 2),
gateway: Ipv4Addr::new(172, 20, 0, 1),
netmask: Ipv4Addr::new(255, 255, 0, 0),
};
let s = param.to_string();
assert_eq!(s, "ip=172.20.0.2::172.20.0.1:255.255.0.0::eth0:off");
assert_eq!(s.parse::<KernelIpParam>().unwrap(), param);
}
#[test]
fn rejects_invalid() {
assert!("no-prefix".parse::<KernelIpParam>().is_err());
assert!("ip=".parse::<KernelIpParam>().is_err());
assert!(
"ip=bogus::1.2.3.4:255.0.0.0::eth0:off"
.parse::<KernelIpParam>()
.is_err()
);
}
}