interface_rs/interface/interface_struct.rs
1use super::{Family, InterfaceBuilder, InterfaceOption, Mapping, Method};
2use std::fmt;
3
4/// Represents a network interface configuration in an `interfaces(5)` file.
5///
6/// The `Interface` struct encapsulates all the configuration details for a
7/// network interface, including its name, whether it starts automatically,
8/// allowed hotplug options, address family, method of configuration, and
9/// additional options.
10///
11/// To construct an `Interface`, it is recommended to use the [`InterfaceBuilder`]
12/// via the [`Interface::builder`] method for a more ergonomic and fluent API.
13///
14/// # Examples
15///
16/// Creating a new `Interface` using the builder pattern:
17///
18/// ```rust
19/// use interface_rs::interface::{Interface, Family, Method};
20///
21/// let iface = Interface::builder("eth0")
22/// .with_auto(true)
23/// .with_allow("hotplug")
24/// .with_family(Family::Inet)
25/// .with_method(Method::Dhcp)
26/// .with_option("mtu", "1500")
27/// .build();
28/// ```
29#[derive(Debug, Clone, PartialEq)]
30pub struct Interface {
31 /// The name of the interface (e.g., `"eth0"`).
32 pub name: String,
33 /// Indicates if the interface is set to start automatically.
34 pub auto: bool,
35 /// A list of `allow-*` directives associated with the interface.
36 pub allow: Vec<String>,
37 /// The address family (e.g., `inet`).
38 pub family: Option<Family>,
39 /// The method of configuration (e.g., `static`, `dhcp`).
40 pub method: Option<Method>,
41 /// A list of options specified under the `iface` stanza.
42 pub options: Vec<InterfaceOption>,
43 /// Optional mapping configuration for the interface.
44 pub mapping: Option<Mapping>,
45}
46
47impl Interface {
48 /// Creates a new [`InterfaceBuilder`] for constructing an `Interface`.
49 ///
50 /// # Arguments
51 ///
52 /// * `name` - The name of the interface (e.g., `"eth0"`).
53 ///
54 /// # Examples
55 ///
56 /// ```rust
57 /// use interface_rs::interface::Interface;
58 ///
59 /// let builder = Interface::builder("eth0");
60 /// ```
61 pub fn builder(name: impl Into<String>) -> InterfaceBuilder {
62 InterfaceBuilder::new(name)
63 }
64
65 /// Creates a new [`InterfaceBuilder`] initialized with this `Interface`'s data.
66 ///
67 /// This method allows you to modify an existing `Interface` using the builder pattern.
68 ///
69 /// # Examples
70 ///
71 /// ```rust
72 /// use interface_rs::interface::{Interface, Family, Method};
73 ///
74 /// let iface = Interface::builder("eth0")
75 /// .with_auto(true)
76 /// .with_family(Family::Inet)
77 /// .with_method(Method::Dhcp)
78 /// .build();
79 ///
80 /// // Modify the existing interface
81 /// let modified_iface = iface.edit()
82 /// .with_method(Method::Static)
83 /// .with_option("address", "192.168.1.50")
84 /// .build();
85 /// ```
86 pub fn edit(&self) -> InterfaceBuilder {
87 InterfaceBuilder {
88 name: self.name.clone(),
89 auto: self.auto,
90 allow: self.allow.clone(),
91 family: self.family.clone(),
92 method: self.method.clone(),
93 options: self.options.clone(),
94 mapping: self.mapping.clone(),
95 }
96 }
97
98 /// Returns the first value for the given option key.
99 ///
100 /// # Arguments
101 ///
102 /// * `key` - The option name to look up (e.g., `"address"`, `"netmask"`).
103 ///
104 /// # Returns
105 ///
106 /// `Some(String)` if the option exists, `None` otherwise.
107 ///
108 /// # Examples
109 ///
110 /// ```rust
111 /// use interface_rs::interface::{Interface, Method};
112 ///
113 /// let iface = Interface::builder("eth0")
114 /// .with_method(Method::Static)
115 /// .with_option("address", "192.168.1.100")
116 /// .with_option("netmask", "255.255.255.0")
117 /// .build();
118 ///
119 /// assert_eq!(iface.get_option("address"), Some("192.168.1.100".to_string()));
120 /// assert_eq!(iface.get_option("gateway"), None);
121 /// ```
122 pub fn get_option(&self, key: &str) -> Option<String> {
123 self.options
124 .iter()
125 .find(|opt| opt.name() == key)
126 .map(|opt| opt.value())
127 }
128
129 /// Returns all values for the given option key.
130 ///
131 /// Some options like `address` can appear multiple times. This method
132 /// returns all values for a given key.
133 ///
134 /// # Arguments
135 ///
136 /// * `key` - The option name to look up.
137 ///
138 /// # Returns
139 ///
140 /// A `Vec` of strings containing all values for the key.
141 ///
142 /// # Examples
143 ///
144 /// ```rust
145 /// use interface_rs::interface::{Interface, Method};
146 ///
147 /// let iface = Interface::builder("eth0")
148 /// .with_method(Method::Static)
149 /// .with_option("address", "192.168.1.100")
150 /// .with_option("address", "192.168.1.101")
151 /// .build();
152 ///
153 /// let addresses = iface.get_options("address");
154 /// assert_eq!(addresses, vec!["192.168.1.100", "192.168.1.101"]);
155 /// ```
156 pub fn get_options(&self, key: &str) -> Vec<String> {
157 self.options
158 .iter()
159 .filter(|opt| opt.name() == key)
160 .map(|opt| opt.value())
161 .collect()
162 }
163}
164
165impl fmt::Display for Interface {
166 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
167 if self.auto {
168 writeln!(f, "auto {}", self.name)?;
169 }
170 for allow_type in &self.allow {
171 writeln!(f, "allow-{} {}", allow_type, self.name)?;
172 }
173 if let Some(mapping) = &self.mapping {
174 writeln!(f, "mapping {}", self.name)?;
175 writeln!(f, " script {}", mapping.script.display())?;
176 for map in &mapping.maps {
177 writeln!(f, " map {}", map)?;
178 }
179 }
180 write!(f, "iface {}", self.name)?;
181 if let Some(family) = &self.family {
182 write!(f, " {}", family)?;
183 }
184 if let Some(method) = &self.method {
185 write!(f, " {}", method)?;
186 }
187 writeln!(f)?;
188 // Sort options before printing
189 let mut sorted_options = self.options.clone();
190 sorted_options.sort_by(|a, b| a.name().cmp(b.name()));
191 for option in &sorted_options {
192 writeln!(f, " {}", option)?;
193 }
194 Ok(())
195 }
196}