1#![deny(missing_docs)]
8
9#[cfg(target_os = "windows")]
10#[path = "windows.rs"]
11mod os;
12
13#[cfg(any(
14 target_os = "linux",
15 target_os = "macos",
16 target_os = "freebsd",
17 target_os = "netbsd",
18 target_os = "openbsd",
19 target_os = "android",
20 target_os = "illumos",
21))]
22#[path = "linux.rs"]
23mod os;
24
25mod iter;
26pub use iter::MacAddressIterator;
27
28#[derive(Debug)]
32pub enum MacAddressError {
33 InternalError,
35}
36
37#[cfg(any(
38 target_os = "linux",
39 target_os = "macos",
40 target_os = "freebsd",
41 target_os = "netbsd",
42 target_os = "openbsd",
43 target_os = "android",
44 target_os = "illumos",
45))]
46impl From<nix::Error> for MacAddressError {
47 fn from(_: nix::Error) -> MacAddressError {
48 MacAddressError::InternalError
49 }
50}
51
52impl std::fmt::Display for MacAddressError {
53 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
54 f.write_str(match self {
55 MacAddressError::InternalError => "Internal API error",
56 })
57 }
58}
59
60impl std::error::Error for MacAddressError {}
61
62#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
64pub enum MacParseError {
65 InvalidDigit,
67 InvalidLength,
69}
70
71impl std::fmt::Display for MacParseError {
72 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
73 f.write_str(match *self {
74 MacParseError::InvalidDigit => "invalid digit",
75 MacParseError::InvalidLength => "invalid length",
76 })
77 }
78}
79
80impl std::error::Error for MacParseError {}
81
82impl From<core::num::ParseIntError> for MacParseError {
83 fn from(_: core::num::ParseIntError) -> Self {
84 MacParseError::InvalidDigit
85 }
86}
87
88#[derive(Debug, Clone, Copy, PartialEq, Default, Eq, PartialOrd, Ord, Hash)]
90#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
91#[cfg_attr(feature = "serde", serde(try_from = "std::borrow::Cow<'_, str>"))]
92pub struct MacAddress {
93 bytes: [u8; 6],
94}
95
96impl MacAddress {
97 pub fn new(bytes: [u8; 6]) -> MacAddress {
99 MacAddress { bytes }
100 }
101}
102
103impl From<[u8; 6]> for MacAddress {
104 fn from(v: [u8; 6]) -> Self {
105 MacAddress::new(v)
106 }
107}
108
109#[cfg(feature = "serde")]
110impl serde::Serialize for MacAddress {
111 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
112 where
113 S: serde::Serializer,
114 {
115 serializer.collect_str(self)
116 }
117}
118
119pub fn get_mac_address() -> Result<Option<MacAddress>, MacAddressError> {
122 let bytes = os::get_mac(None)?;
123
124 Ok(bytes.map(|b| MacAddress { bytes: b }))
125}
126
127pub fn mac_address_by_name(name: &str) -> Result<Option<MacAddress>, MacAddressError> {
131 let bytes = os::get_mac(Some(name))?;
132
133 Ok(bytes.map(|b| MacAddress { bytes: b }))
134}
135
136pub fn name_by_mac_address(mac: &MacAddress) -> Result<Option<String>, MacAddressError> {
138 os::get_ifname(&mac.bytes)
139}
140
141impl MacAddress {
142 pub fn bytes(self) -> [u8; 6] {
144 self.bytes
145 }
146}
147
148impl std::str::FromStr for MacAddress {
149 type Err = MacParseError;
150
151 fn from_str(input: &str) -> Result<Self, Self::Err> {
152 let mut array = [0u8; 6];
153
154 if !input.is_ascii() {
158 return Err(MacParseError::InvalidLength);
161 }
162
163 match input.len() {
164 17 => {
166 array
167 .iter_mut()
168 .zip(input.split(|c| c == ':' || c == '-'))
169 .try_for_each::<_, Result<(), MacParseError>>(|(b, s)| {
170 *b = u8::from_str_radix(s, 16)?;
171 Ok(())
172 })?;
173 }
174 12 => {
176 array
177 .iter_mut()
178 .zip((0..6).map(|i| &input[i * 2..=i * 2 + 1]))
179 .try_for_each::<_, Result<(), MacParseError>>(|(b, s)| {
180 *b = u8::from_str_radix(s, 16)?;
181 Ok(())
182 })?;
183 }
184 _ => return Err(MacParseError::InvalidLength),
185 }
186
187 Ok(MacAddress::new(array))
188 }
189}
190
191impl std::convert::TryFrom<&'_ str> for MacAddress {
192 type Error = MacParseError;
193
194 fn try_from(value: &str) -> Result<Self, Self::Error> {
195 value.parse()
196 }
197}
198
199impl std::convert::TryFrom<std::borrow::Cow<'_, str>> for MacAddress {
200 type Error = MacParseError;
201
202 fn try_from(value: std::borrow::Cow<'_, str>) -> Result<Self, Self::Error> {
203 value.parse()
204 }
205}
206
207impl std::fmt::Display for MacAddress {
208 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
209 let _ = write!(
210 f,
211 "{:<02X}:{:<02X}:{:<02X}:{:<02X}:{:<02X}:{:<02X}",
212 self.bytes[0],
213 self.bytes[1],
214 self.bytes[2],
215 self.bytes[3],
216 self.bytes[4],
217 self.bytes[5]
218 );
219
220 Ok(())
221 }
222}
223
224#[cfg(test)]
225mod tests {
226 use super::*;
227
228 #[test]
229 fn parse_str_colon() {
230 let string = "80:FA:5B:41:10:6B";
231 let address = string.parse::<MacAddress>().unwrap();
232 assert_eq!(address.bytes(), [128, 250, 91, 65, 16, 107]);
233 assert_eq!(&format!("{}", address), string);
234 }
235
236 #[test]
237 fn parse_str_hyphen() {
238 let string = "01-23-45-67-89-AB";
239 let address = string.parse::<MacAddress>().unwrap();
240 assert_eq!(address.bytes(), [0x01, 0x23, 0x45, 0x67, 0x89, 0xAB]);
241 assert_eq!(format!("{}", address), string.replace('-', ":"));
242 }
243
244 #[test]
245 fn parse_str_no_sep() {
246 let string = "4827e24425d8";
247 let address = string.parse::<MacAddress>().unwrap();
248 assert_eq!(address.bytes(), [0x48, 0x27, 0xE2, 0x44, 0x25, 0xD8]);
249 }
250
251 #[test]
252 fn parse_invalid_length() {
253 let string = "80:FA:5B:41:10:6B:AC";
254 let address = string.parse::<MacAddress>().unwrap_err();
255 assert_eq!(MacParseError::InvalidLength, address);
256
257 let string = "80:FA:5B:41";
258 let address = string.parse::<MacAddress>().unwrap_err();
259 assert_eq!(MacParseError::InvalidLength, address);
260
261 let string = "80FA5B41";
262 let address = string.parse::<MacAddress>().unwrap_err();
263 assert_eq!(MacParseError::InvalidLength, address);
264
265 let string = "80:FÁ:5B:41:10:6B";
266 let address = string.parse::<MacAddress>().unwrap_err();
267 assert_eq!(MacParseError::InvalidLength, address);
268 }
269
270 #[test]
271 fn parse_invalid_digit() {
272 let string = "80:FA:ZZ:41:10:6B";
273 let address = string.parse::<MacAddress>().unwrap_err();
274 assert_eq!(MacParseError::InvalidDigit, address);
275 }
276
277 #[test]
278 fn parse_invalid_separator() {
279 let string = "80|FA|AA|41|10|6B";
280 let address = string.parse::<MacAddress>().unwrap_err();
281 assert_eq!(MacParseError::InvalidDigit, address);
282 }
283
284 #[cfg(feature = "serde")]
285 #[test]
286 fn serde_works() {
287 use serde::{Deserialize, Serialize};
288 use serde_test::{assert_tokens, Token};
289 let mac: MacAddress = "80:FA:5B:41:10:6B".parse().unwrap();
290
291 assert_tokens(&mac, &[Token::BorrowedStr("80:FA:5B:41:10:6B")]);
292
293 #[derive(Serialize, Deserialize)]
294 struct Test {
295 mac: MacAddress,
296 }
297
298 assert_eq!(
299 serde_json::to_string(&Test { mac }).unwrap(),
300 serde_json::to_string::<Test>(
301 &serde_json::from_str("{ \"mac\": \"80:FA:5B:41:10:6B\" }").unwrap()
302 )
303 .unwrap(),
304 );
305 }
306
307 #[cfg(feature = "serde")]
308 #[test]
309 fn serde_from_reader_works() {
310 use serde::{Deserialize, Serialize};
311 let mac: MacAddress = "80:FA:5B:41:10:6B".parse().unwrap();
312
313 #[derive(Serialize, Deserialize, PartialEq, Debug)]
314 struct Test {
315 mac: MacAddress,
316 }
317
318 assert_eq!(
319 Test { mac },
320 serde_json::from_reader(std::io::Cursor::new(r#"{ "mac": "80:FA:5B:41:10:6B" }"#))
321 .unwrap(),
322 );
323 }
324
325 #[test]
326 fn convert() {
327 for mac in MacAddressIterator::new().unwrap() {
328 let name = name_by_mac_address(&mac).unwrap().unwrap();
329 let mac2 = mac_address_by_name(&name).unwrap().unwrap();
330 assert_eq!(mac, mac2);
331 }
332 }
333}