Skip to content

Commit

Permalink
fix doc tests and add vni/vlan helpers
Browse files Browse the repository at this point in the history
  • Loading branch information
wiggels committed Nov 19, 2024
1 parent b2970e0 commit 46d27ce
Show file tree
Hide file tree
Showing 4 changed files with 145 additions and 29 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
/target
/tests
45 changes: 22 additions & 23 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,25 +25,23 @@
//! use interface_rs::NetworkInterfaces;
//! use interface_rs::interface::{Interface, Family};
//!
//! fn main() -> std::io::Result<()> {
//! // Load the interfaces from a file
//! let mut net_ifaces = NetworkInterfaces::load("/path/to/interfaces")?;
//! fn main() -> Result<(), Box<dyn std::error::Error>> {
//! // Load interfaces (replace with an existing file path during tests)
//! let mut net_ifaces = NetworkInterfaces::load("tests/interfaces")?;
//!
//! // Retrieve and modify an existing interface using the builder pattern
//! // Retrieve and modify an existing interface
//! if let Some(iface) = net_ifaces.get_interface("eth0") {
//! let modified_iface = iface.edit()
//! .with_method("static")
//! .remove_option("address")
//! .with_option("address", "192.168.1.50")
//! .with_option("netmask", "255.255.255.0")
//! .build();
//!
//! // Replace the existing interface with the modified one
//! net_ifaces.add_interface(modified_iface);
//! }
//!
//! // Save changes back to the file
//! // Save changes
//! net_ifaces.save()?;
//!
//! Ok(())
//! }
//! ```
Expand All @@ -54,21 +52,21 @@
//! use interface_rs::NetworkInterfaces;
//! use interface_rs::interface::{Interface, Family};
//!
//! fn main() -> std::io::Result<()> {
//! let mut net_ifaces = NetworkInterfaces::load("/path/to/interfaces")?;
//! fn main() -> Result<(), Box<dyn std::error::Error>> {
//! // Load interfaces (replace with an existing file path during tests)
//! let mut net_ifaces = NetworkInterfaces::load("tests/interfaces")?;
//!
//! // Create a new interface using the builder pattern
//! let new_iface = Interface::builder("swp1")
//! .with_auto(true)
//! .with_allow("hotplug")
//! .with_family(Family::Inet)
//! .with_method("static")
//! .with_option("address", "192.168.100.1")
//! .with_option("netmask", "255.255.255.0")
//! .build();
//!
//! // Add the new interface to the collection
//! net_ifaces.add_interface(new_iface);
//! net_ifaces.add_interface(
//! Interface::builder("swp1")
//! .with_auto(true)
//! .with_allow("hotplug")
//! .with_family(Family::Inet)
//! .with_method("static")
//! .with_option("address", "192.168.100.1")
//! .with_option("netmask", "255.255.255.0")
//! .build()
//! );
//!
//! // Save changes back to the file
//! net_ifaces.save()?;
Expand All @@ -82,8 +80,9 @@
//! ```rust
//! use interface_rs::NetworkInterfaces;
//!
//! fn main() -> std::io::Result<()> {
//! let mut net_ifaces = NetworkInterfaces::load("/path/to/interfaces")?;
//! fn main() -> Result<(), Box<dyn std::error::Error>> {
//! // Load interfaces (replace with an existing file path during tests)
//! let mut net_ifaces = NetworkInterfaces::load("tests/interfaces")?;
//!
//! // Delete an interface by name
//! net_ifaces.delete_interface("eth0");
Expand Down
115 changes: 114 additions & 1 deletion src/network_interfaces.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use std::time::SystemTime;
/// use interface_rs::NetworkInterfaces;
/// use interface_rs::interface::Interface;
///
/// let mut net_ifaces = NetworkInterfaces::load("/path/to/interfaces").unwrap();
/// let mut net_ifaces = NetworkInterfaces::load("tests/interfaces").unwrap();
///
/// // Modify an interface
/// if let Some(iface) = net_ifaces.get_interface_mut("eth0") {
Expand Down Expand Up @@ -149,6 +149,55 @@ impl NetworkInterfaces {
self.interfaces.is_empty()
}

/// Finds the next unused VLAN ID within a specified range.
///
/// # Arguments
///
/// * `start` - The starting VLAN ID (inclusive).
/// * `end` - The ending VLAN ID (inclusive).
///
/// # Returns
///
/// * `Option<u16>` - The next unused VLAN ID, or `None` if all are used.
pub fn next_unused_vlan_in_range(&self, start: u16, end: u16) -> Option<u16> {
for vlan_id in start..=end {
let vlan_name = format!("vlan{}", vlan_id);
if !self.interfaces.contains_key(&vlan_name) {
return Some(vlan_id);
}
}
None // All VLAN IDs in the specified range are used
}

/// Retrieves the VLAN associated with an existing VNI interface.
///
/// # Arguments
///
/// * `vni_id` - The VNI ID to search for (e.g., `1347682`).
///
/// # Returns
///
/// * `Option<u16>` - The VLAN ID specified in the `bridge-access` option, or `None` if
/// the interface does not exist or the option is not present.
pub fn get_existing_vni_vlan(&self, vni_id: u32) -> Option<u16> {
let vni_name = format!("vni{}", vni_id);

// Check if the interface exists
let interface = self.interfaces.get(&vni_name)?;

// Look for the `bridge-access` option
for (key, value) in &interface.options {
if key == "bridge-access" {
// Try to parse the value as a u16
if let Ok(vlan_id) = value.parse::<u16>() {
return Some(vlan_id);
}
}
}

None // No `bridge-access` option or invalid value
}

/// Saves changes back to the `interfaces(5)` file.
///
/// # Errors
Expand Down Expand Up @@ -257,3 +306,67 @@ impl NetworkInterfaces {
self.interfaces.iter()
}
}

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

#[test]
fn test_next_unused_vlan_in_range() {
// Create a `NetworkInterfaces` instance with some used VLANs
let mut network_interfaces = NetworkInterfaces {
interfaces: HashMap::new(),
path: None,
last_modified: None,
comments: Vec::new(),
sources: Vec::new(),
};

// Add some VLAN interfaces to simulate used IDs
network_interfaces.add_interface(Interface::builder("vlan1000").build());
network_interfaces.add_interface(Interface::builder("vlan1001").build());
network_interfaces.add_interface(Interface::builder("vlan1003").build());

// Test: Find the next unused VLAN ID in the range 1000 to 1999
let next_vlan_id = network_interfaces.next_unused_vlan_in_range(1000, 1999);
assert_eq!(next_vlan_id, Some(1002));

// Test: Find the next unused VLAN ID when all VLANs are used in the range
network_interfaces.add_interface(Interface::builder("vlan1002").build());
let next_vlan_id = network_interfaces.next_unused_vlan_in_range(1000, 1003);
assert_eq!(next_vlan_id, None);

// Test: Find the next unused VLAN ID in a different range
let next_vlan_id = network_interfaces.next_unused_vlan_in_range(2000, 2005);
assert_eq!(next_vlan_id, Some(2000));
}

#[test]
fn test_get_existing_vni_vlan() {
let mut network_interfaces = NetworkInterfaces {
interfaces: HashMap::new(),
path: None,
last_modified: None,
comments: Vec::new(),
sources: Vec::new(),
};

// Add a VNI interface
network_interfaces.add_interface(
Interface::builder("vni123456")
.with_auto(true)
.with_option("bridge-access", "1002")
.build(),
);

// Test: Find VLAN ID for existing VNI
assert_eq!(network_interfaces.get_existing_vni_vlan(123456), Some(1002));

// Test: No `bridge-access` option
network_interfaces.add_interface(Interface::builder("vni987654").with_auto(true).build());
assert_eq!(network_interfaces.get_existing_vni_vlan(987654), None);

// Test: Nonexistent VNI
assert_eq!(network_interfaces.get_existing_vni_vlan(666), None);
}
}
13 changes: 8 additions & 5 deletions src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,15 +126,18 @@ impl Parser {

// Parse family
let family = match tokens.get(2) {
Some(s) => Some(s.parse::<Family>().map_err(|e| ParserError {
message: e.to_string(),
line: Some(line_number + 1),
})?),
Some(s) => s.parse::<Family>().ok(),
None => None,
};

// Parse method
let method = tokens.get(3).map(|s| s.to_string());
let method = match tokens.len() {
// If family is valid, method is the next token
4 if family.is_some() => Some(tokens[3].to_string()),
// If family is absent, interpret the third token as the method
3 if family.is_none() => Some(tokens[2].to_string()),
_ => None,
};

if let Some(family) = family {
builder = builder.with_family(family);
Expand Down

0 comments on commit 46d27ce

Please sign in to comment.