1use crate::error::NetworkInterfacesError;
2use crate::interface::Interface;
3use crate::parser::Parser;
4use crate::helper::sort::natural;
5use std::collections::HashMap;
6use std::fmt;
7use std::fs;
8use std::io::Write;
9use std::path::{Path, PathBuf};
10use std::time::SystemTime;
11
12#[derive(Debug)]
37pub struct NetworkInterfaces {
38 interfaces: HashMap<String, Interface>,
40 path: Option<PathBuf>,
42 last_modified: Option<SystemTime>,
44 comments: Vec<String>,
46 sources: Vec<String>,
48}
49
50impl NetworkInterfaces {
51 fn new(
53 interfaces: HashMap<String, Interface>,
54 comments: Vec<String>,
55 sources: Vec<String>,
56 path: Option<PathBuf>,
57 last_modified: Option<SystemTime>,
58 ) -> Self {
59 NetworkInterfaces {
60 interfaces,
61 comments,
62 sources,
63 path,
64 last_modified,
65 }
66 }
67
68 pub fn load<P: AsRef<Path>>(path: P) -> Result<Self, NetworkInterfacesError> {
82 let path_buf = path.as_ref().to_path_buf();
83 let metadata = fs::metadata(&path_buf)?;
84 let last_modified = metadata.modified()?;
85
86 let content = fs::read_to_string(&path_buf)?;
87 let parser = Parser::new();
88 let (interfaces, comments, sources) = parser.parse(&content)?;
89
90 Ok(NetworkInterfaces::new(
91 interfaces,
92 comments,
93 sources,
94 Some(path_buf),
95 Some(last_modified),
96 ))
97 }
98
99 pub fn get_interface(&self, name: &str) -> Option<&Interface> {
109 self.interfaces.get(name)
110 }
111
112 pub fn get_interface_mut(&mut self, name: &str) -> Option<&mut Interface> {
122 self.interfaces.get_mut(name)
123 }
124
125 pub fn add_interface(&mut self, iface: Interface) {
131 self.interfaces.insert(iface.name.clone(), iface);
132 }
133
134 pub fn delete_interface(&mut self, name: &str) {
140 self.interfaces.remove(name);
141 }
142
143 pub fn len(&self) -> usize {
145 self.interfaces.len()
146 }
147
148 pub fn is_empty(&self) -> bool {
150 self.interfaces.is_empty()
151 }
152
153 pub fn next_unused_vlan_in_range(&self, start: u16, end: u16) -> Option<u16> {
164 for vlan_id in start..=end {
165 let vlan_name = format!("vlan{}", vlan_id);
166 if !self.interfaces.contains_key(&vlan_name) {
167 return Some(vlan_id);
168 }
169 }
170 None }
172
173 pub fn get_existing_vni_vlan(&self, vni_id: u32) -> Option<u16> {
184 let vni_name = format!("vni{}", vni_id);
185 let interface = self.interfaces.get(&vni_name)?;
186 interface.get_option("bridge-access").and_then(|v| v.parse().ok())
187 }
188
189 pub fn get_bridge_interfaces(&self) -> Vec<String> {
195 self.interfaces
196 .iter()
197 .filter_map(|(name, iface)| {
198 iface.get_option("bridge-access").map(|_| name.clone())
199 })
200 .collect()
201 }
202
203 pub fn save(&mut self) -> Result<(), NetworkInterfacesError> {
209 let path = match &self.path {
210 Some(p) => p.clone(),
211 None => {
212 return Err(NetworkInterfacesError::Other(
213 "No file path specified".to_string(),
214 ))
215 }
216 };
217
218 let metadata = fs::metadata(&path)?;
220 let current_modified = metadata.modified()?;
221 if let Some(last_modified) = self.last_modified {
222 if current_modified > last_modified {
223 return Err(NetworkInterfacesError::FileModified);
225 }
226 }
227
228 let mut file = fs::File::create(&path)?;
230 write!(file, "{}", self)?;
231
232 self.last_modified = Some(SystemTime::now());
234 Ok(())
235 }
236
237 pub fn reload(&mut self) -> Result<(), NetworkInterfacesError> {
243 let path = match &self.path {
244 Some(p) => p.clone(),
245 None => {
246 return Err(NetworkInterfacesError::Other(
247 "No file path specified".to_string(),
248 ))
249 }
250 };
251 let reloaded = NetworkInterfaces::load(path)?;
252 self.interfaces = reloaded.interfaces;
253 self.comments = reloaded.comments;
254 self.sources = reloaded.sources;
255 self.last_modified = reloaded.last_modified;
256 Ok(())
257 }
258}
259
260impl fmt::Display for NetworkInterfaces {
262 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
263 for comment in &self.comments {
265 writeln!(f, "{}", comment)?;
266 }
267
268 for source in &self.sources {
270 writeln!(f, "{}", source)?;
271 }
272
273 let mut interfaces: Vec<&Interface> = self.interfaces.values().collect();
275 interfaces.sort_by(|a, b| natural(&a.name, &b.name));
276
277 for iface in interfaces {
279 writeln!(f)?;
280 write!(f, "{}", iface)?;
281 }
282 Ok(())
283 }
284}
285
286impl NetworkInterfaces {
288 pub fn iter(&self) -> impl Iterator<Item = (&String, &Interface)> {
290 self.interfaces.iter()
291 }
292}
293
294#[cfg(test)]
295mod tests {
296 use super::*;
297
298 #[test]
299 fn test_next_unused_vlan_in_range() {
300 let mut network_interfaces = NetworkInterfaces {
302 interfaces: HashMap::new(),
303 path: None,
304 last_modified: None,
305 comments: Vec::new(),
306 sources: Vec::new(),
307 };
308
309 network_interfaces.add_interface(Interface::builder("vlan1000").build());
311 network_interfaces.add_interface(Interface::builder("vlan1001").build());
312 network_interfaces.add_interface(Interface::builder("vlan1003").build());
313
314 let next_vlan_id = network_interfaces.next_unused_vlan_in_range(1000, 1999);
316 assert_eq!(next_vlan_id, Some(1002));
317
318 network_interfaces.add_interface(Interface::builder("vlan1002").build());
320 let next_vlan_id = network_interfaces.next_unused_vlan_in_range(1000, 1003);
321 assert_eq!(next_vlan_id, None);
322
323 let next_vlan_id = network_interfaces.next_unused_vlan_in_range(2000, 2005);
325 assert_eq!(next_vlan_id, Some(2000));
326 }
327
328 #[test]
329 fn test_get_existing_vni_vlan() {
330 let mut network_interfaces = NetworkInterfaces {
331 interfaces: HashMap::new(),
332 path: None,
333 last_modified: None,
334 comments: Vec::new(),
335 sources: Vec::new(),
336 };
337
338 network_interfaces.add_interface(
340 Interface::builder("vni123456")
341 .with_auto(true)
342 .with_option("bridge-access", "1002")
343 .build(),
344 );
345
346 assert_eq!(network_interfaces.get_existing_vni_vlan(123456), Some(1002));
348
349 network_interfaces.add_interface(Interface::builder("vni987654").with_auto(true).build());
351 assert_eq!(network_interfaces.get_existing_vni_vlan(987654), None);
352
353 assert_eq!(network_interfaces.get_existing_vni_vlan(666), None);
355 }
356
357 #[test]
358 fn test_get_bridge_interfaces() {
359 let mut network_interfaces = NetworkInterfaces {
360 interfaces: HashMap::new(),
361 path: None,
362 last_modified: None,
363 comments: Vec::new(),
364 sources: Vec::new(),
365 };
366
367 network_interfaces.add_interface(
369 Interface::builder("vni1234")
370 .with_option("bridge-access", "1000")
371 .build(),
372 );
373 network_interfaces.add_interface(
374 Interface::builder("swp2")
375 .with_option("bridge-access", "1001")
376 .build(),
377 );
378
379 network_interfaces.add_interface(Interface::builder("swp1").build());
381
382 let mut bridge_interfaces = network_interfaces.get_bridge_interfaces();
384 bridge_interfaces.sort();
385 assert_eq!(bridge_interfaces, vec!["swp2", "vni1234"]);
386 }
387}