use super::error::ConnectionError;
#[non_exhaustive]
#[derive(Debug, Clone)]
pub struct VlanConfig {
pub parent: String,
pub id: u16,
pub interface_name: Option<String>,
pub connection_name: Option<String>,
pub mtu: Option<u32>,
pub flags: Option<u32>,
pub ingress_priority_map: Option<Vec<String>>,
pub egress_priority_map: Option<Vec<String>>,
}
impl VlanConfig {
#[must_use]
pub fn new(parent: impl Into<String>, id: u16) -> Self {
Self {
parent: parent.into(),
id,
interface_name: None,
connection_name: None,
mtu: None,
flags: None,
ingress_priority_map: None,
egress_priority_map: None,
}
}
#[must_use]
pub fn with_interface_name(mut self, name: impl Into<String>) -> Self {
self.interface_name = Some(name.into());
self
}
#[must_use]
pub fn with_connection_name(mut self, name: impl Into<String>) -> Self {
self.connection_name = Some(name.into());
self
}
#[must_use]
pub fn with_mtu(mut self, mtu: u32) -> Self {
self.mtu = Some(mtu);
self
}
#[must_use]
pub fn with_flags(mut self, flags: u32) -> Self {
self.flags = Some(flags);
self
}
#[must_use]
pub fn with_ingress_priority_map(mut self, map: Vec<impl Into<String>>) -> Self {
self.ingress_priority_map = Some(map.into_iter().map(Into::into).collect());
self
}
#[must_use]
pub fn with_egress_priority_map(mut self, map: Vec<impl Into<String>>) -> Self {
self.egress_priority_map = Some(map.into_iter().map(Into::into).collect());
self
}
#[must_use]
pub fn effective_interface_name(&self) -> String {
self.interface_name
.clone()
.unwrap_or_else(|| format!("{}.{}", self.parent, self.id))
}
#[must_use]
pub fn effective_connection_name(&self) -> String {
self.connection_name
.clone()
.unwrap_or_else(|| format!("VLAN {} on {}", self.id, self.parent))
}
pub fn validate(&self) -> Result<(), ConnectionError> {
if self.id == 0 || self.id > 4094 {
return Err(ConnectionError::InvalidVlanId { id: self.id });
}
if self.parent.is_empty() {
return Err(ConnectionError::InvalidInput {
field: "parent".to_string(),
reason: "parent interface name cannot be empty".to_string(),
});
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn new_creates_basic_config() {
let config = VlanConfig::new("eth0", 100);
assert_eq!(config.parent, "eth0");
assert_eq!(config.id, 100);
assert!(config.interface_name.is_none());
assert!(config.connection_name.is_none());
}
#[test]
fn effective_interface_name_default() {
let config = VlanConfig::new("eth0", 100);
assert_eq!(config.effective_interface_name(), "eth0.100");
}
#[test]
fn effective_interface_name_custom() {
let config = VlanConfig::new("eth0", 100).with_interface_name("office-vlan");
assert_eq!(config.effective_interface_name(), "office-vlan");
}
#[test]
fn effective_connection_name_default() {
let config = VlanConfig::new("eth0", 100);
assert_eq!(config.effective_connection_name(), "VLAN 100 on eth0");
}
#[test]
fn effective_connection_name_custom() {
let config = VlanConfig::new("eth0", 100).with_connection_name("Office Network");
assert_eq!(config.effective_connection_name(), "Office Network");
}
#[test]
fn builder_methods_chain() {
let config = VlanConfig::new("enp3s0", 200)
.with_interface_name("vlan200")
.with_connection_name("Server VLAN")
.with_mtu(1496)
.with_flags(0x5)
.with_ingress_priority_map(vec!["0:0", "1:1"])
.with_egress_priority_map(vec!["0:0", "1:1"]);
assert_eq!(config.parent, "enp3s0");
assert_eq!(config.id, 200);
assert_eq!(config.interface_name, Some("vlan200".to_string()));
assert_eq!(config.connection_name, Some("Server VLAN".to_string()));
assert_eq!(config.mtu, Some(1496));
assert_eq!(config.flags, Some(0x5));
assert_eq!(
config.ingress_priority_map,
Some(vec!["0:0".to_string(), "1:1".to_string()])
);
}
#[test]
fn validate_rejects_zero_id() {
let config = VlanConfig::new("eth0", 0);
assert!(config.validate().is_err());
}
#[test]
fn validate_rejects_id_over_4094() {
let config = VlanConfig::new("eth0", 4095);
assert!(config.validate().is_err());
}
#[test]
fn validate_rejects_empty_parent() {
let config = VlanConfig::new("", 100);
assert!(config.validate().is_err());
}
#[test]
fn validate_accepts_valid_config() {
let config = VlanConfig::new("eth0", 100);
assert!(config.validate().is_ok());
let config = VlanConfig::new("eth0", 1);
assert!(config.validate().is_ok());
let config = VlanConfig::new("eth0", 4094);
assert!(config.validate().is_ok());
}
}