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
186 let interface = self.interfaces.get(&vni_name)?;
188
189 for (key, value) in &interface.options {
191 if key == "bridge-access" {
192 if let Ok(vlan_id) = value.parse::<u16>() {
194 return Some(vlan_id);
195 }
196 }
197 }
198
199 None }
201
202 pub fn get_bridge_interfaces(&self) -> Vec<String> {
208 self.interfaces
209 .iter()
210 .filter_map(|(name, iface)| {
211 for (key, _) in &iface.options {
212 if key == "bridge-access" {
213 return Some(name.clone());
214 }
215 }
216 None
217 })
218 .collect()
219 }
220
221 pub fn save(&mut self) -> Result<(), NetworkInterfacesError> {
227 let path = match &self.path {
228 Some(p) => p.clone(),
229 None => {
230 return Err(NetworkInterfacesError::Other(
231 "No file path specified".to_string(),
232 ))
233 }
234 };
235
236 let metadata = fs::metadata(&path)?;
238 let current_modified = metadata.modified()?;
239 if let Some(last_modified) = self.last_modified {
240 if current_modified > last_modified {
241 return Err(NetworkInterfacesError::FileModified);
243 }
244 }
245
246 let mut file = fs::File::create(&path)?;
248 write!(file, "{}", self)?;
249
250 self.last_modified = Some(SystemTime::now());
252 Ok(())
253 }
254
255 pub fn reload(&mut self) -> Result<(), NetworkInterfacesError> {
261 let path = match &self.path {
262 Some(p) => p.clone(),
263 None => {
264 return Err(NetworkInterfacesError::Other(
265 "No file path specified".to_string(),
266 ))
267 }
268 };
269 let reloaded = NetworkInterfaces::load(path)?;
270 self.interfaces = reloaded.interfaces;
271 self.comments = reloaded.comments;
272 self.sources = reloaded.sources;
273 self.last_modified = reloaded.last_modified;
274 Ok(())
275 }
276}
277
278impl fmt::Display for NetworkInterfaces {
280 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
281 for comment in &self.comments {
283 writeln!(f, "{}", comment)?;
284 }
285
286 for source in &self.sources {
288 writeln!(f, "{}", source)?;
289 }
290
291 let mut interfaces: Vec<&Interface> = self.interfaces.values().collect();
293 interfaces.sort_by(|a, b| natural(&a.name, &b.name));
294
295 for iface in interfaces {
297 writeln!(f)?;
298 write!(f, "{}", iface)?;
299 }
300 Ok(())
301 }
302}
303
304impl NetworkInterfaces {
306 pub fn iter(&self) -> impl Iterator<Item = (&String, &Interface)> {
308 self.interfaces.iter()
309 }
310}
311
312#[cfg(test)]
313mod tests {
314 use super::*;
315
316 #[test]
317 fn test_next_unused_vlan_in_range() {
318 let mut network_interfaces = NetworkInterfaces {
320 interfaces: HashMap::new(),
321 path: None,
322 last_modified: None,
323 comments: Vec::new(),
324 sources: Vec::new(),
325 };
326
327 network_interfaces.add_interface(Interface::builder("vlan1000").build());
329 network_interfaces.add_interface(Interface::builder("vlan1001").build());
330 network_interfaces.add_interface(Interface::builder("vlan1003").build());
331
332 let next_vlan_id = network_interfaces.next_unused_vlan_in_range(1000, 1999);
334 assert_eq!(next_vlan_id, Some(1002));
335
336 network_interfaces.add_interface(Interface::builder("vlan1002").build());
338 let next_vlan_id = network_interfaces.next_unused_vlan_in_range(1000, 1003);
339 assert_eq!(next_vlan_id, None);
340
341 let next_vlan_id = network_interfaces.next_unused_vlan_in_range(2000, 2005);
343 assert_eq!(next_vlan_id, Some(2000));
344 }
345
346 #[test]
347 fn test_get_existing_vni_vlan() {
348 let mut network_interfaces = NetworkInterfaces {
349 interfaces: HashMap::new(),
350 path: None,
351 last_modified: None,
352 comments: Vec::new(),
353 sources: Vec::new(),
354 };
355
356 network_interfaces.add_interface(
358 Interface::builder("vni123456")
359 .with_auto(true)
360 .with_option("bridge-access", "1002")
361 .build(),
362 );
363
364 assert_eq!(network_interfaces.get_existing_vni_vlan(123456), Some(1002));
366
367 network_interfaces.add_interface(Interface::builder("vni987654").with_auto(true).build());
369 assert_eq!(network_interfaces.get_existing_vni_vlan(987654), None);
370
371 assert_eq!(network_interfaces.get_existing_vni_vlan(666), None);
373 }
374
375 #[test]
376 fn test_get_bridge_interfaces() {
377 let mut network_interfaces = NetworkInterfaces {
378 interfaces: HashMap::new(),
379 path: None,
380 last_modified: None,
381 comments: Vec::new(),
382 sources: Vec::new(),
383 };
384
385 network_interfaces.add_interface(
387 Interface::builder("vni1234")
388 .with_option("bridge-access", "1000")
389 .build(),
390 );
391 network_interfaces.add_interface(
392 Interface::builder("swp2")
393 .with_option("bridge-access", "1001")
394 .build(),
395 );
396
397 network_interfaces.add_interface(Interface::builder("swp1").build());
399
400 let bridge_interfaces = network_interfaces.get_bridge_interfaces();
402 assert_eq!(bridge_interfaces, vec!["swp2", "vni1234"]);
403 }
404}