herolib-mos 0.3.13

Mycelium Operating System (MOS) - Network and VM abstraction layer
Documentation
use super::error::{NetworkError, Result};
use std::process::Command;

pub struct Bridge {
    pub name: String,
}

/// Create a new bridge
pub fn create_bridge(name: &str) -> Result<Bridge> {
    if bridge_exists(name)? {
        return Ok(Bridge {
            name: name.to_string(),
        });
    }

    // Create bridge using ip link
    let output = Command::new("ip")
        .args(["link", "add", "name", name, "type", "bridge"])
        .output()
        .map_err(|e| NetworkError::Bridge(format!("Failed to execute ip command: {}", e)))?;

    if !output.status.success() {
        return Err(NetworkError::Bridge(format!(
            "Failed to create bridge {}: {}",
            name,
            String::from_utf8_lossy(&output.stderr)
        )));
    }

    // Set MTU
    let output = Command::new("ip")
        .args(["link", "set", "dev", name, "mtu", "1500"])
        .output()
        .map_err(|e| NetworkError::Bridge(format!("Failed to set MTU: {}", e)))?;

    if !output.status.success() {
        return Err(NetworkError::Bridge(format!(
            "Failed to set MTU for bridge {}: {}",
            name,
            String::from_utf8_lossy(&output.stderr)
        )));
    }

    // Bring bridge up
    let output = Command::new("ip")
        .args(["link", "set", "dev", name, "up"])
        .output()
        .map_err(|e| NetworkError::Bridge(format!("Failed to bring bridge up: {}", e)))?;

    if !output.status.success() {
        return Err(NetworkError::Bridge(format!(
            "Failed to bring bridge {} up: {}",
            name,
            String::from_utf8_lossy(&output.stderr)
        )));
    }

    Ok(Bridge {
        name: name.to_string(),
    })
}

/// Check if a bridge exists by checking /sys/class/net/{name}/bridge
pub fn bridge_exists(name: &str) -> Result<bool> {
    use std::path::Path;
    
    // Check if the bridge sysfs path exists - this is the most reliable way
    let bridge_path = format!("/sys/class/net/{}/bridge", name);
    Ok(Path::new(&bridge_path).exists())
}

/// Get an existing bridge
pub fn get_bridge(name: &str) -> Result<Bridge> {
    if !bridge_exists(name)? {
        return Err(NetworkError::NotFound(format!("Bridge {} not found", name)));
    }

    Ok(Bridge {
        name: name.to_string(),
    })
}

/// Delete a bridge
pub fn delete_bridge(name: &str) -> Result<()> {
    if !bridge_exists(name)? {
        return Ok(());
    }

    let output = Command::new("ip")
        .args(["link", "delete", name])
        .output()
        .map_err(|e| NetworkError::Bridge(format!("Failed to delete bridge: {}", e)))?;

    if !output.status.success() {
        return Err(NetworkError::Bridge(format!(
            "Failed to delete bridge {}: {}",
            name,
            String::from_utf8_lossy(&output.stderr)
        )));
    }

    Ok(())
}

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

    #[test]
    fn test_bridge_struct_creation() {
        let bridge = Bridge {
            name: "test_bridge".to_string(),
        };
        assert_eq!(bridge.name, "test_bridge");
    }

    #[test]
    fn test_bridge_name_validation() {
        let valid_names = vec!["br0", "bridge1", "my-bridge", "test_br"];
        for name in valid_names {
            let bridge = Bridge {
                name: name.to_string(),
            };
            assert!(!bridge.name.is_empty());
        }
    }
}