1 use core::{fmt, str::FromStr};
2 
3 use crate::parser;
4 
5 /// MAC address in *EUI-64* format.
6 #[repr(C)]
7 #[derive(Debug, Default, Hash, Eq, PartialEq, Ord, PartialOrd, Copy, Clone)]
8 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
9 pub struct MacAddr8([u8; 8]);
10 
11 impl MacAddr8 {
12     /// Creates a new `MacAddr8` address from the bytes.
13     ///
14     /// ## Example
15     ///
16     /// ```rust
17     /// # use macaddr::MacAddr8;
18     /// let addr = MacAddr8::new(0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF);
19     /// ```
20     #[allow(clippy::many_single_char_names, clippy::too_many_arguments)]
new(a: u8, b: u8, c: u8, d: u8, e: u8, f: u8, g: u8, h: u8) -> MacAddr821     pub const fn new(a: u8, b: u8, c: u8, d: u8, e: u8, f: u8, g: u8, h: u8) -> MacAddr8 {
22         MacAddr8([a, b, c, d, e, f, g, h])
23     }
24 
25     /// Create a new nil `MacAddr8`.
26     ///
27     /// ## Example
28     ///
29     /// ```rust
30     /// # use macaddr::MacAddr8;
31     /// let addr = MacAddr8::nil();
32     /// assert!(addr.is_nil());
33     /// ```
nil() -> MacAddr834     pub const fn nil() -> MacAddr8 {
35         MacAddr8([0x00; 8])
36     }
37 
38     /// Create a new broadcast `MacAddr8`.
39     ///
40     /// ## Example
41     ///
42     /// ```rust
43     /// # use macaddr::MacAddr8;
44     /// let addr = MacAddr8::broadcast();
45     /// assert!(addr.is_broadcast());
46     /// ```
broadcast() -> MacAddr847     pub const fn broadcast() -> MacAddr8 {
48         MacAddr8([0xFF; 8])
49     }
50 
51     /// Returns `true` if the address is nil.
52     ///
53     /// ## Example
54     ///
55     /// ```rust
56     /// # use macaddr::MacAddr8;
57     /// let addr = MacAddr8::new(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
58     ///
59     /// assert_eq!(addr.is_nil(), true);
60     /// ```
61     #[allow(clippy::trivially_copy_pass_by_ref)]
is_nil(&self) -> bool62     pub fn is_nil(&self) -> bool {
63         self.0.iter().all(|&b| b == 0)
64     }
65 
66     /// Returns `true` if the address is broadcast.
67     ///
68     /// ## Example
69     ///
70     /// ```rust
71     /// # use macaddr::MacAddr8;
72     /// let addr = MacAddr8::new(0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF);
73     ///
74     /// assert_eq!(addr.is_broadcast(), true);
75     /// ```
76     #[allow(clippy::trivially_copy_pass_by_ref)]
is_broadcast(&self) -> bool77     pub fn is_broadcast(&self) -> bool {
78         self.0.iter().all(|&b| b == 0xFF)
79     }
80 
81     /// Returns `true` if the address is unicast.
82     ///
83     /// ## Example
84     ///
85     /// ```rust
86     /// # use macaddr::MacAddr8;
87     /// let addr = MacAddr8::new(0x00, 0x01, 0x44, 0x55, 0x66, 0x77, 0xCD, 0xEF);
88     ///
89     /// assert_eq!(addr.is_unicast(), true);
90     /// ```
91     #[allow(clippy::trivially_copy_pass_by_ref)]
is_unicast(&self) -> bool92     pub const fn is_unicast(&self) -> bool {
93         self.0[0] & 1 == 0
94     }
95 
96     /// Returns `true` if the address is multicast.
97     ///
98     /// ## Example
99     ///
100     /// ```rust
101     /// # use macaddr::MacAddr8;
102     /// let addr = MacAddr8::new(0x01, 0x00, 0x0C, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC);
103     ///
104     /// assert_eq!(addr.is_multicast(), true);
105     /// ```
106     #[allow(clippy::trivially_copy_pass_by_ref)]
is_multicast(&self) -> bool107     pub const fn is_multicast(&self) -> bool {
108         self.0[0] & 1 == 1
109     }
110 
111     /// Returns `true` if the address is universally administered address (UAA).
112     ///
113     /// ## Example
114     ///
115     /// ```rust
116     /// # use macaddr::MacAddr8;
117     /// let addr = MacAddr8::new(0x01, 0x00, 0x0C, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC);
118     ///
119     /// assert_eq!(addr.is_universal(), true);
120     /// ```
121     #[allow(clippy::trivially_copy_pass_by_ref)]
is_universal(&self) -> bool122     pub const fn is_universal(&self) -> bool {
123         self.0[0] & 1 << 1 == 0
124     }
125 
126     /// Returns `true` if the address is locally administered (LAA).
127     ///
128     /// ## Example
129     ///
130     /// ```rust
131     /// # use macaddr::MacAddr8;
132     /// let addr = MacAddr8::new(0x02, 0x00, 0x0C, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC);
133     ///
134     /// assert_eq!(addr.is_local(), true);
135     /// ```
136     #[allow(clippy::trivially_copy_pass_by_ref)]
is_local(&self) -> bool137     pub const fn is_local(&self) -> bool {
138         self.0[0] & 1 << 1 == 2
139     }
140 
141     /// Converts a `MacAddr8` address to a byte slice.
142     ///
143     /// ## Example
144     ///
145     /// ```rust
146     /// # use macaddr::MacAddr8;
147     /// let addr = MacAddr8::new(0xAC, 0xDE, 0x48, 0x23, 0x45, 0x67, 0x89, 0xAB);
148     ///
149     /// assert_eq!(addr.as_bytes(), &[0xAC, 0xDE, 0x48, 0x23, 0x45, 0x67, 0x89, 0xAB]);
150     /// ```
as_bytes(&self) -> &[u8]151     pub fn as_bytes(&self) -> &[u8] {
152         &self.0
153     }
154 
155     /// Consumes a `MacAddr8` address and returns raw bytes.
156     ///
157     /// ## Example
158     ///
159     /// ```rust
160     /// # use macaddr::MacAddr8;
161     /// let addr = MacAddr8::new(0xAC, 0xDE, 0x48, 0x23, 0x45, 0x67, 0x89, 0xAB);
162     ///
163     /// assert_eq!(addr.into_array(), [0xAC, 0xDE, 0x48, 0x23, 0x45, 0x67, 0x89, 0xAB]);
164     /// ```
into_array(self) -> [u8; 8]165     pub const fn into_array(self) -> [u8; 8] {
166         self.0
167     }
168 }
169 
170 impl FromStr for MacAddr8 {
171     type Err = parser::ParseError;
172 
from_str(s: &str) -> Result<Self, Self::Err>173     fn from_str(s: &str) -> Result<Self, Self::Err> {
174         parser::Parser::new(s).read_v8_addr()
175     }
176 }
177 
178 impl From<[u8; 8]> for MacAddr8 {
from(bytes: [u8; 8]) -> Self179     fn from(bytes: [u8; 8]) -> Self {
180         MacAddr8(bytes)
181     }
182 }
183 
184 impl AsRef<[u8]> for MacAddr8 {
as_ref(&self) -> &[u8]185     fn as_ref(&self) -> &[u8] {
186         &self.0
187     }
188 }
189 
190 impl AsMut<[u8]> for MacAddr8 {
as_mut(&mut self) -> &mut [u8]191     fn as_mut(&mut self) -> &mut [u8] {
192         &mut self.0
193     }
194 }
195 
196 /// `MacAddr8` can be displayed in different formats.
197 ///
198 /// # Example
199 ///
200 /// ```
201 /// # use macaddr::MacAddr8;
202 /// let addr = MacAddr8::new(0xab, 0x0d, 0xef, 0x12, 0x34, 0x56, 0x78, 0x9A);
203 ///
204 /// assert_eq!(&format!("{}",    addr), "AB:0D:EF:12:34:56:78:9A");
205 /// assert_eq!(&format!("{:-}",  addr), "AB-0D-EF-12-34-56-78-9A");
206 /// assert_eq!(&format!("{:#}",  addr), "AB0D.EF12.3456.789A");
207 /// ```
208 impl fmt::Display for MacAddr8 {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result209     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
210         if f.sign_minus() {
211             f.write_fmt(format_args!(
212                 "{:02X}-{:02X}-{:02X}-{:02X}-{:02X}-{:02X}-{:02X}-{:02X}",
213                 self.0[0], self.0[1], self.0[2], self.0[3], self.0[4], self.0[5], self.0[6], self.0[7],
214             ))
215         } else if f.alternate() {
216             f.write_fmt(format_args!(
217                 "{:02X}{:02X}.{:02X}{:02X}.{:02X}{:02X}.{:02X}{:02X}",
218                 self.0[0], self.0[1], self.0[2], self.0[3], self.0[4], self.0[5], self.0[6], self.0[7],
219             ))
220         } else {
221             f.write_fmt(format_args!(
222                 "{:02X}:{:02X}:{:02X}:{:02X}:{:02X}:{:02X}:{:02X}:{:02X}",
223                 self.0[0], self.0[1], self.0[2], self.0[3], self.0[4], self.0[5], self.0[6], self.0[7],
224             ))
225         }
226     }
227 }
228