1use crate::{BridgeController, CommandError, CommandExecutor};
2use lazy_regex::regex_captures;
3use log::{debug, error, info};
4use std::fmt;
5
6#[derive(Debug)]
7pub struct Bridge {
8 name: String,
9}
10
11impl fmt::Display for Bridge {
12 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
13 write!(f, "{}", self.name)
14 }
15}
16
17impl Bridge {
18 pub fn new(name: &str) -> Bridge {
19 info!("Creating new Bridge: {}", name);
20 Bridge { name: name.to_string() }
21 }
22
23 pub fn get_name(&self) -> &str {
24 &self.name
25 }
26
27 pub fn delete(&self) -> Result<(), CommandError> {
28 info!("Deleting Bridge: {}", self.name);
29 BridgeController::delete_bridge(&self.name)
30 }
31
32 pub fn add_interface(&self, interface: &str) -> Result<(), CommandError> {
33 info!("Adding interface {} to Bridge: {}", interface, self.name);
34 CommandExecutor::run_command(vec!["brctl", "addif", &self.name, interface]).map(|_| ())
35 }
36
37 pub fn remove_interface(&self, interface: &str) -> Result<(), CommandError> {
38 info!("Removing interface {} from Bridge: {}", interface, self.name);
39 CommandExecutor::run_command(vec!["brctl", "delif", &self.name, interface]).map(|_| ())
40 }
41
42 pub fn set_hairpin(&self, port: &str, enable: bool) -> Result<(), CommandError> {
43 let state = if enable { "on" } else { "off" };
44 info!("Setting hairpin {} on port {} for Bridge: {}", state, port, self.name);
45 CommandExecutor::run_command(vec!["brctl", "hairpin", &self.name, port, state]).map(|_| ())
46 }
47
48 pub fn set_stp(&self, enable: bool) -> Result<(), CommandError> {
49 let state = if enable { "on" } else { "off" };
50 info!("Setting STP {} for Bridge: {}", state, self.name);
51 CommandExecutor::run_command(vec!["brctl", "stp", &self.name, state]).map(|_| ())
52 }
53
54 pub fn set_ageing_time(&self, time: u32) -> Result<(), CommandError> {
55 info!("Setting ageing time to {} for Bridge: {}", time, self.name);
56 CommandExecutor::run_command(vec!["brctl", "setageing", &self.name, &time.to_string()]).map(|_| ())
57 }
58
59 fn show(&self) -> Result<String, CommandError> {
60 debug!("Fetching details for Bridge: {}", self.name);
61 let output = CommandExecutor::run_command(vec!["brctl", "show", &self.name])?;
62 let stdout = String::from_utf8_lossy(&output.stdout).to_string();
63 Ok(stdout)
64 }
65
66 pub fn get_id(&self) -> Result<String, CommandError> {
67 debug!("Getting ID for Bridge: {}", self.name);
68 let details = self.show()?;
69
70 if let Some((_, _, bridge_id)) = regex_captures!(r"^\s*(\S+)\s+(\S+)\s+", &details.lines().nth(1).unwrap_or(""))
71 {
72 return Ok(bridge_id.to_string());
73 }
74
75 error!("Failed to get bridge ID for {}", self.name);
76 Err(CommandError::ExecutionError("Failed to get bridge ID".into()))
77 }
78
79 pub fn get_interfaces(&self) -> Result<Vec<String>, CommandError> {
80 debug!("Getting interfaces for Bridge: {}", self.name);
81 let details = self.show()?;
82 let mut interfaces = Vec::new();
83
84 for line in details.lines() {
85 if let Some((_, interface)) = regex_captures!(r"^\s*\S+\s+\S+\s+\S+\s+(\S+)", line) {
86 interfaces.push(interface.to_string());
87 }
88 }
89
90 Ok(interfaces)
91 }
92
93 pub fn get_stp(&self) -> Result<bool, CommandError> {
94 debug!("Checking STP state for Bridge: {}", self.name);
95 let details = self.show()?;
96
97 if let Some((_, stp_state)) = regex_captures!(r"^\s*\S+\s+\S+\s+(\S+)", details.lines().nth(1).unwrap_or("")) {
98 return Ok(match stp_state {
99 "yes" => true,
100 "no" => false,
101 _ => {
102 error!("Unknown STP state for Bridge: {}", self.name);
103 unimplemented!("Unknown STP state {}", stp_state)
104 }
105 });
106 }
107
108 error!("Failed to determine STP state for Bridge: {}", self.name);
109 Err(CommandError::ExecutionError("Failed to determine STP state".into()))
110 }
111}