eui48/
lib.rs

1// Copyright 2016 Andrew Baumhauer <andy@baumhauer.us>
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9//! Represent and parse IEEE EUI-48 Media Access Control addresses
10//! The IEEE claims trademarks on the names EUI-48 and EUI-64, in which EUI is an
11//! abbreviation for Extended Unique Identifier.
12
13#![doc(
14    html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
15    html_favicon_url = "https://www.rust-lang.org/favicon.ico",
16    html_root_url = "https://doc.rust-lang.org/eui48/"
17)]
18
19extern crate regex;
20#[cfg(feature = "rustc-serialize")]
21extern crate rustc_serialize;
22#[cfg(feature = "serde")]
23extern crate serde;
24#[cfg(feature = "serde_json")]
25extern crate serde_json;
26
27use std::default::Default;
28use std::error::Error;
29use std::fmt;
30use std::str::FromStr;
31
32use regex::Regex;
33#[cfg(feature = "rustc-serialize")]
34use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
35#[cfg(feature = "serde")]
36use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
37
38/// A 48-bit (6 byte) buffer containing the EUI address
39pub const EUI48LEN: usize = 6;
40pub type Eui48 = [u8; EUI48LEN];
41
42/// A 64-bit (8 byte) buffer containing the EUI address
43pub const EUI64LEN: usize = 8;
44pub type Eui64 = [u8; EUI64LEN];
45
46/// A MAC address (EUI-48)
47#[repr(C)]
48#[derive(Copy, Clone, Hash, Eq, PartialEq, Ord, PartialOrd)]
49pub struct MacAddress {
50    /// The 48-bit number stored in 6 bytes
51    eui: Eui48,
52}
53
54#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
55/// Format to display MacAddress
56pub enum MacAddressFormat {
57    /// Use - notaion
58    Canonical,
59    /// Use : notation
60    HexString,
61    /// Use . notation
62    DotNotation,
63    /// Use 0x notation
64    Hexadecimal,
65}
66
67#[derive(PartialEq, Eq, Copy, Clone, Debug, Ord, PartialOrd, Hash)]
68/// Parsing errors
69pub enum ParseError {
70    /// Length is incorrect (should be 11 to 17)
71    InvalidLength(usize),
72    /// The input string is invalid, usize bytes were found, and we put up to 6 bytes into Eui48
73    InvalidByteCount(usize, Eui48),
74}
75
76impl MacAddress {
77    /// Create a new MacAddress from `[u8; 6]`.
78    pub const fn new(eui: Eui48) -> MacAddress {
79        MacAddress { eui }
80    }
81
82    /// Create a new MacAddress from a byte slice.
83    ///
84    /// Returns an error (without any description) if the slice doesn't have the proper length.
85    pub fn from_bytes(bytes: &[u8]) -> Result<Self, ParseError> {
86        if bytes.len() != EUI48LEN {
87            return Err(ParseError::InvalidLength(bytes.len()));
88        }
89        let mut input: [u8; EUI48LEN] = Default::default();
90        input[..EUI48LEN].clone_from_slice(&bytes[..EUI48LEN]);
91        Ok(Self::new(input))
92    }
93
94    /// Returns empty EUI-48 address
95    pub fn nil() -> MacAddress {
96        MacAddress { eui: [0; EUI48LEN] }
97    }
98
99    /// Returns 'ff:ff:ff:ff:ff:ff', a MAC broadcast address
100    pub fn broadcast() -> MacAddress {
101        MacAddress {
102            eui: [0xFF; EUI48LEN],
103        }
104    }
105
106    /// Returns true if the address is '00:00:00:00:00:00'
107    pub fn is_nil(&self) -> bool {
108        self.eui.iter().all(|&b| b == 0)
109    }
110
111    /// Returns true if the address is 'ff:ff:ff:ff:ff:ff'
112    pub fn is_broadcast(&self) -> bool {
113        self.eui.iter().all(|&b| b == 0xFF)
114    }
115
116    /// Returns true if bit 1 of Y is 0 in address 'xY:xx:xx:xx:xx:xx'
117    pub fn is_unicast(&self) -> bool {
118        self.eui[0] & 1 == 0
119    }
120
121    /// Returns true if bit 1 of Y is 1 in address 'xY:xx:xx:xx:xx:xx'
122    pub fn is_multicast(&self) -> bool {
123        self.eui[0] & 1 == 1
124    }
125
126    /// Returns true if bit 2 of Y is 0 in address 'xY:xx:xx:xx:xx:xx'
127    pub fn is_universal(&self) -> bool {
128        self.eui[0] & 1 << 1 == 0
129    }
130
131    /// Returns true if bit 2 of Y is 1 in address 'xY:xx:xx:xx:xx:xx'
132    pub fn is_local(&self) -> bool {
133        self.eui[0] & 1 << 1 == 2
134    }
135
136    /// Returns a String representation in the format '00-00-00-00-00-00'
137    pub fn to_canonical(&self) -> String {
138        format!(
139            "{:02x}-{:02x}-{:02x}-{:02x}-{:02x}-{:02x}",
140            self.eui[0], self.eui[1], self.eui[2], self.eui[3], self.eui[4], self.eui[5]
141        )
142    }
143
144    /// Returns a String representation in the format '00:00:00:00:00:00'
145    pub fn to_hex_string(&self) -> String {
146        format!(
147            "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
148            self.eui[0], self.eui[1], self.eui[2], self.eui[3], self.eui[4], self.eui[5]
149        )
150    }
151
152    /// Returns a String representation in the format '0000.0000.0000'
153    pub fn to_dot_string(&self) -> String {
154        format!(
155            "{:02x}{:02x}.{:02x}{:02x}.{:02x}{:02x}",
156            self.eui[0], self.eui[1], self.eui[2], self.eui[3], self.eui[4], self.eui[5]
157        )
158    }
159
160    /// Returns a String representation in the format '0x000000000000'
161    pub fn to_hexadecimal(&self) -> String {
162        format!(
163            "0x{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}",
164            self.eui[0], self.eui[1], self.eui[2], self.eui[3], self.eui[4], self.eui[5]
165        )
166    }
167
168    /// Returns a String representation in the EUI-64 interface ID format '0000:00ff:fe00:0000'
169    pub fn to_interfaceid(&self) -> String {
170        format!(
171            "{:02x}{:02x}:{:02x}ff:fe{:02x}:{:02x}{:02x}",
172            (self.eui[0] ^ 0x02),
173            self.eui[1],
174            self.eui[2],
175            self.eui[3],
176            self.eui[4],
177            self.eui[5]
178        )
179    }
180
181    /// Returns a String representation in the IPv6 link local format 'fe80::0000:00ff:fe00:0000'
182    pub fn to_link_local(&self) -> String {
183        format!(
184            "fe80::{:02x}{:02x}:{:02x}ff:fe{:02x}:{:02x}{:02x}",
185            (self.eui[0] ^ 0x02),
186            self.eui[1],
187            self.eui[2],
188            self.eui[3],
189            self.eui[4],
190            self.eui[5]
191        )
192    }
193
194    /// Returns a String in the format selected by fmt
195    pub fn to_string(&self, fmt: MacAddressFormat) -> String {
196        match fmt {
197            MacAddressFormat::Canonical => self.to_canonical(),
198            MacAddressFormat::HexString => self.to_hex_string(),
199            MacAddressFormat::DotNotation => self.to_dot_string(),
200            MacAddressFormat::Hexadecimal => self.to_hexadecimal(),
201        }
202    }
203
204    /// Parses a String representation from any format supported
205    pub fn parse_str(s: &str) -> Result<MacAddress, ParseError> {
206        let re = Regex::new("(0x)?([0-9a-fA-F]{1,2})[:.-]?").unwrap();
207        let mut eui: Eui48 = [0; EUI48LEN];
208
209        match s.len() {
210            11..=17 => {}
211            _ => {
212                return Err(ParseError::InvalidLength(s.len()));
213            }
214        }
215
216        let mut i = 0;
217        for caps in re.captures_iter(s) {
218            // Fill the array and keep counting for InvalidByteCount
219            if i < EUI48LEN {
220                let matched_byte = caps.get(2).unwrap().as_str();
221                eui[i] = u8::from_str_radix(matched_byte, 16).unwrap();
222            }
223            i += 1;
224        }
225
226        if i != EUI48LEN {
227            return Err(ParseError::InvalidByteCount(i, eui));
228        }
229
230        Ok(MacAddress::new(eui))
231    }
232
233    /// Return the internal structure as a slice of bytes
234    pub fn as_bytes(&self) -> &[u8] {
235        &self.eui
236    }
237
238    /// Returns an array in Eui48. Works as an inverse function of new()
239    pub fn to_array(&self) -> Eui48 {
240        self.eui
241    }
242
243    /// Returns Display MacAddressFormat, determined at compile time.
244    pub fn get_display_format() -> MacAddressFormat {
245        if cfg!(feature = "disp_hexstring") {
246            MacAddressFormat::HexString
247        } else {
248            MacAddressFormat::Canonical
249        }
250    }
251}
252
253impl FromStr for MacAddress {
254    type Err = ParseError;
255    /// Create a MacAddress from String
256    fn from_str(us: &str) -> Result<MacAddress, ParseError> {
257        MacAddress::parse_str(us)
258    }
259}
260
261impl Default for MacAddress {
262    /// Create a Default MacAddress (00-00-00-00-00-00)
263    fn default() -> MacAddress {
264        MacAddress::nil()
265    }
266}
267
268impl fmt::Debug for MacAddress {
269    /// Debug format for MacAddress is HexString notation
270    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
271        write!(
272            f,
273            "MacAddress(\"{}\")",
274            self.to_string(MacAddressFormat::HexString)
275        )
276    }
277}
278
279impl fmt::Display for MacAddress {
280    /// Display format is canonical format (00-00-00-00-00-00) by default
281    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
282        let disp_fmt = MacAddress::get_display_format();
283        write!(f, "{}", self.to_string(disp_fmt))
284    }
285}
286
287impl fmt::Display for ParseError {
288    /// Human readable error strings for ParseError enum
289    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
290        match *self {
291            ParseError::InvalidLength(found) => write!(
292                f,
293                "Invalid length; expecting 11 to 17 chars, found {}",
294                found
295            ),
296            ParseError::InvalidByteCount(found, eui) => write!(
297                f,
298                "Invalid byte count; Matched `{}` bytes ({:?})",
299                found,
300                &eui[..found]
301            ),
302        }
303    }
304}
305
306impl Error for ParseError {
307    /// Human readable description for ParseError enum
308    fn description(&self) -> &str {
309        "MacAddress parse error"
310    }
311}
312
313#[cfg(feature = "rustc-serialize")]
314impl Encodable for MacAddress {
315    /// Encode a MacAddress using the default format
316    fn encode<E: Encoder>(&self, e: &mut E) -> Result<(), E::Error> {
317        let disp_fmt = MacAddress::get_display_format();
318        e.emit_str(&self.to_string(disp_fmt))
319    }
320}
321
322#[cfg(feature = "rustc-serialize")]
323impl Decodable for MacAddress {
324    /// Decode a MacAddress from a string in canonical form
325    fn decode<D: Decoder>(d: &mut D) -> Result<MacAddress, D::Error> {
326        let string = d.read_str()?;
327        string.parse().map_err(|err| d.error(&format!("{}", err)))
328    }
329}
330
331#[cfg(all(feature = "serde", not(feature = "serde_bytes")))]
332impl Serialize for MacAddress {
333    /// Serialize a MacAddress in the default format using the serde crate
334    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
335        let disp_fmt = MacAddress::get_display_format();
336        serializer.serialize_str(&self.to_string(disp_fmt))
337    }
338}
339
340#[cfg(feature = "serde_bytes")]
341impl Serialize for MacAddress {
342    /// Serialize a MacAddress as raw bytes using the serde crate
343    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
344        serializer.serialize_bytes(self.as_bytes())
345    }
346}
347
348#[cfg(all(feature = "serde", not(feature = "serde_bytes")))]
349impl<'de> Deserialize<'de> for MacAddress {
350    /// Deserialize a MacAddress from canonical form using the serde crate
351    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
352        struct MacAddressVisitor;
353        impl<'de> de::Visitor<'de> for MacAddressVisitor {
354            type Value = MacAddress;
355
356            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
357                write!(formatter, "a string representation of a MAC address")
358            }
359
360            fn visit_str<E: de::Error>(self, value: &str) -> Result<Self::Value, E> {
361                value.parse().map_err(|err| E::custom(&format!("{}", err)))
362            }
363        }
364        deserializer.deserialize_str(MacAddressVisitor)
365    }
366}
367
368#[cfg(feature = "serde_bytes")]
369impl<'de> Deserialize<'de> for MacAddress {
370    /// Deserialize a MacAddress from raw bytes using the serde crate
371    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
372        struct MacAddressVisitor;
373        impl<'de> de::Visitor<'de> for MacAddressVisitor {
374            type Value = MacAddress;
375
376            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
377                write!(formatter, "6-element byte array")
378            }
379
380            fn visit_bytes<E: de::Error>(self, value: &[u8]) -> Result<Self::Value, E> {
381                MacAddress::from_bytes(value).map_err(|_| E::invalid_length(value.len(), &self))
382            }
383        }
384        deserializer.deserialize_bytes(MacAddressVisitor)
385    }
386}
387
388// ************** TESTS BEGIN HERE ***************
389#[cfg(test)]
390mod tests {
391    use super::{Eui48, MacAddress, MacAddressFormat, ParseError};
392
393    #[test]
394    fn test_new() {
395        let eui: Eui48 = [0x12, 0x34, 0x56, 0xAB, 0xCD, 0xEF];
396        let mac = MacAddress::new(eui);
397
398        assert!(mac.eui[0..5] == eui[0..5]);
399    }
400
401    #[test]
402    fn test_from_bytes() {
403        assert_eq!(
404            "12:34:56:ab:cd:ef",
405            MacAddress::from_bytes(&[0x12, 0x34, 0x56, 0xAB, 0xCD, 0xEF])
406                .unwrap()
407                .to_hex_string()
408        );
409        assert!(MacAddress::from_bytes(&[0x12, 0x34, 0x56, 0xAB, 0xCD]).is_err());
410    }
411
412    #[test]
413    fn test_nil() {
414        let nil = MacAddress::nil();
415        let not_nil = MacAddress::broadcast();
416        assert_eq!("00:00:00:00:00:00", nil.to_hex_string());
417        assert!(nil.is_nil());
418        assert!(!not_nil.is_nil());
419    }
420
421    #[test]
422    fn test_default() {
423        let default = MacAddress::default();
424        assert!(default.is_nil());
425    }
426
427    #[test]
428    fn test_broadcast() {
429        let broadcast = MacAddress::broadcast();
430        let not_broadcast = MacAddress::nil();
431        assert_eq!("ff:ff:ff:ff:ff:ff", broadcast.to_hex_string());
432        assert!(broadcast.is_broadcast());
433        assert!(!not_broadcast.is_broadcast());
434    }
435
436    #[test]
437    fn test_is_nil() {
438        let nil = MacAddress::nil();
439        let not_nil = MacAddress::parse_str("01:00:5E:AB:CD:EF").unwrap();
440        assert!(nil.is_nil());
441        assert!(!not_nil.is_nil());
442    }
443
444    #[test]
445    fn test_is_broadcast() {
446        let broadcast = MacAddress::broadcast();
447        let not_broadcast = MacAddress::parse_str("01:00:5E:AB:CD:EF").unwrap();
448        assert!(broadcast.is_broadcast());
449        assert!(!not_broadcast.is_broadcast());
450    }
451
452    #[test]
453    fn test_is_unicast() {
454        let mac_u = MacAddress::parse_str("FE:00:5E:AB:CD:EF").unwrap();
455        let mac_m = MacAddress::parse_str("01:00:5E:AB:CD:EF").unwrap();
456        assert!(mac_u.is_unicast());
457        assert!(!mac_m.is_unicast());
458        assert_eq!("fe:00:5e:ab:cd:ef", mac_u.to_hex_string()); // Catch modifying first octet
459        let mac = MacAddress::parse_str("FF:00:5E:AB:CD:EF").unwrap();
460        assert!(!mac.is_unicast());
461        assert_eq!("ff:00:5e:ab:cd:ef", mac.to_hex_string()); // Catch modifying first octet
462        assert!(MacAddress::nil().is_unicast());
463        assert!(!MacAddress::broadcast().is_unicast());
464    }
465
466    #[test]
467    fn test_is_multicast() {
468        let mac_u = MacAddress::parse_str("FE:00:5E:AB:CD:EF").unwrap();
469        let mac_m = MacAddress::parse_str("01:00:5E:AB:CD:EF").unwrap();
470        assert!(!mac_u.is_multicast());
471        assert!(mac_m.is_multicast());
472        assert!(!MacAddress::nil().is_multicast());
473        assert_eq!("01:00:5e:ab:cd:ef", mac_m.to_hex_string()); // Catch modifying first octet
474        let mac = MacAddress::parse_str("F0:00:5E:AB:CD:EF").unwrap();
475        assert!(!mac.is_multicast());
476        assert_eq!("f0:00:5e:ab:cd:ef", mac.to_hex_string()); // Catch modifying first octet
477        assert!(MacAddress::broadcast().is_multicast());
478    }
479
480    #[test]
481    fn test_is_universal() {
482        let universal = MacAddress::parse_str("11:24:56:AB:CD:EF").unwrap();
483        let not_universal = MacAddress::parse_str("12:24:56:AB:CD:EF").unwrap();
484        assert!(universal.is_universal());
485        assert!(!not_universal.is_universal());
486        assert_eq!("11:24:56:ab:cd:ef", universal.to_hex_string()); // Catch modifying first octet
487    }
488
489    #[test]
490    fn test_is_local() {
491        let local = MacAddress::parse_str("06:34:56:AB:CD:EF").unwrap();
492        let not_local = MacAddress::parse_str("00:34:56:AB:CD:EF").unwrap();
493        assert!(local.is_local());
494        assert!(!not_local.is_local());
495        assert_eq!("06:34:56:ab:cd:ef", local.to_hex_string()); // Catch modifying first octet
496    }
497
498    #[test]
499    fn test_to_canonical() {
500        let eui: Eui48 = [0x12, 0x34, 0x56, 0xAB, 0xCD, 0xEF];
501        let mac = MacAddress::new(eui);
502        assert_eq!("12-34-56-ab-cd-ef", mac.to_canonical());
503    }
504
505    #[test]
506    fn test_to_hex_string() {
507        let eui: Eui48 = [0x12, 0x34, 0x56, 0xAB, 0xCD, 0xEF];
508        let mac = MacAddress::new(eui);
509        assert_eq!("12:34:56:ab:cd:ef", mac.to_hex_string());
510    }
511
512    #[test]
513    fn test_to_dot_string() {
514        let eui: Eui48 = [0x12, 0x34, 0x56, 0xAB, 0xCD, 0xEF];
515        let mac = MacAddress::new(eui);
516        assert_eq!("1234.56ab.cdef", mac.to_dot_string());
517    }
518
519    #[test]
520    fn test_to_hexadecimal() {
521        let eui: Eui48 = [0x12, 0x34, 0x56, 0xAB, 0xCD, 0xEF];
522        let mac = MacAddress::new(eui);
523        assert_eq!("0x123456abcdef", mac.to_hexadecimal());
524    }
525
526    #[test]
527    fn test_to_interfaceid() {
528        let eui: Eui48 = [0x12, 0x34, 0x56, 0xAB, 0xCD, 0xEF];
529        let mac = MacAddress::new(eui);
530        assert_eq!("1034:56ff:feab:cdef", mac.to_interfaceid());
531    }
532
533    #[test]
534    fn test_to_link_local() {
535        let eui: Eui48 = [0x12, 0x34, 0x56, 0xAB, 0xCD, 0xEF];
536        let mac = MacAddress::new(eui);
537        assert_eq!("fe80::1034:56ff:feab:cdef", mac.to_link_local());
538    }
539
540    #[test]
541    fn test_to_string() {
542        let eui: Eui48 = [0x12, 0x34, 0x56, 0xAB, 0xCD, 0xEF];
543        let mac = MacAddress::new(eui);
544        assert_eq!(
545            "0x123456abcdef",
546            mac.to_string(MacAddressFormat::Hexadecimal)
547        );
548        assert_eq!(
549            "1234.56ab.cdef",
550            mac.to_string(MacAddressFormat::DotNotation)
551        );
552        assert_eq!(
553            "12:34:56:ab:cd:ef",
554            mac.to_string(MacAddressFormat::HexString)
555        );
556        assert_eq!(
557            "12-34-56-ab-cd-ef",
558            mac.to_string(MacAddressFormat::Canonical)
559        );
560    }
561
562    #[test]
563    fn test_parse_str() {
564        use super::ParseError::*;
565
566        assert_eq!(
567            "0x123456abcdef",
568            MacAddress::parse_str("0x123456ABCDEF")
569                .unwrap()
570                .to_hexadecimal()
571        );
572        assert_eq!(
573            "1234.56ab.cdef",
574            MacAddress::parse_str("1234.56AB.CDEF")
575                .unwrap()
576                .to_dot_string()
577        );
578        assert_eq!(
579            "12:34:56:ab:cd:ef",
580            MacAddress::parse_str("12:34:56:AB:CD:EF")
581                .unwrap()
582                .to_hex_string()
583        );
584        assert_eq!(
585            "12-34-56-ab-cd-ef",
586            MacAddress::parse_str("12-34-56-AB-CD-EF")
587                .unwrap()
588                .to_canonical()
589        );
590        assert_eq!(
591            "12-34-56-78-90-0a",
592            MacAddress::parse_str("0x1234567890A")
593                .unwrap()
594                .to_canonical()
595        );
596        assert_eq!(
597            "12-34-56-ab-cd-ef",
598            MacAddress::parse_str("123456ABCDEF")
599                .unwrap()
600                .to_canonical()
601        );
602        assert_eq!(
603            "00-00-00-00-00-00",
604            MacAddress::parse_str("!0x00000000000")
605                .unwrap()
606                .to_canonical()
607        );
608        assert_eq!(
609            "00-00-00-00-00-00",
610            MacAddress::parse_str("0x00000000000!")
611                .unwrap()
612                .to_canonical()
613        );
614        // Test error parsing
615        assert_eq!(MacAddress::parse_str(""), Err(InvalidLength(0)));
616        assert_eq!(MacAddress::parse_str("0"), Err(InvalidLength(1)));
617        assert_eq!(
618            MacAddress::parse_str("1234567890ABCD"),
619            Err(InvalidByteCount(7, [0x12, 0x34, 0x56, 0x78, 0x90, 0xAB]))
620        );
621        assert_eq!(
622            MacAddress::parse_str("1234567890ABCDEF"),
623            Err(InvalidByteCount(8, [0x12, 0x34, 0x56, 0x78, 0x90, 0xAB]))
624        );
625        assert_eq!(
626            MacAddress::parse_str("01234567890ABCDEF"),
627            Err(InvalidByteCount(9, [0x01, 0x23, 0x45, 0x67, 0x89, 0x0A]))
628        );
629        assert_eq!(
630            MacAddress::parse_str("0x1234567890ABCDE"),
631            Err(InvalidByteCount(8, [0x12, 0x34, 0x56, 0x78, 0x90, 0xAB]))
632        );
633        assert_eq!(
634            MacAddress::parse_str("0x00:01:02:03:"),
635            Err(InvalidByteCount(4, [0, 1, 2, 3, 0, 0]))
636        );
637        assert_eq!(
638            MacAddress::parse_str("0x00:01:02:03:04:"),
639            Err(InvalidByteCount(5, [0, 1, 2, 3, 4, 0]))
640        );
641        assert_eq!(
642            MacAddress::parse_str("::::::::::::::"),
643            Err(InvalidByteCount(0, [0, 0, 0, 0, 0, 0]))
644        );
645        assert_eq!(
646            MacAddress::parse_str(":::::::::::::::::"),
647            Err(InvalidByteCount(0, [0, 0, 0, 0, 0, 0]))
648        );
649        assert_eq!(
650            MacAddress::parse_str("0x0x0x0x0x0x0x"),
651            Err(InvalidByteCount(4, [0, 0, 0, 0, 0, 0]))
652        );
653    }
654
655    #[test]
656    fn test_as_bytes() {
657        let mac = MacAddress::broadcast();
658        let bytes = mac.as_bytes();
659
660        assert!(bytes.len() == 6);
661        assert!(bytes.iter().all(|&b| b == 0xFF));
662    }
663
664    #[test]
665    fn test_compare() {
666        let m1 = MacAddress::nil();
667        let m2 = MacAddress::broadcast();
668        assert_eq!(m1, m1);
669        assert_eq!(m2, m2);
670        assert_ne!(m1, m2);
671        assert_ne!(m2, m1);
672    }
673
674    #[test]
675    fn test_clone() {
676        let m1 = MacAddress::parse_str("12:34:56:AB:CD:EF").unwrap();
677        let m2 = m1;
678        assert_eq!(m1, m1);
679        assert_eq!(m2, m2);
680        assert_eq!(m1, m2);
681        assert_eq!(m2, m1);
682    }
683
684    #[test]
685    fn test_serialize() {
686        use rustc_serialize::json;
687
688        let mac = MacAddress::parse_str("12:34:56:AB:CD:EF").unwrap();
689        // Format returned is base on compile time of feature(disp_hexstring)
690        if cfg!(feature = "disp_hexstring") {
691            assert_eq!("\"12:34:56:ab:cd:ef\"", json::encode(&mac).unwrap());
692        } else {
693            assert_eq!("\"12-34-56-ab-cd-ef\"", json::encode(&mac).unwrap());
694        }
695    }
696
697    #[test]
698    fn test_deserialize() {
699        use rustc_serialize::json;
700
701        let mac = MacAddress::parse_str("12:34:56:AB:CD:EF").unwrap();
702
703        if cfg!(feature = "disp_hexstring") {
704            let d = "\"12:34:56:AB:CD:EF\"";
705            assert_eq!(mac, json::decode(&d).unwrap());
706        } else {
707            let d = "\"12-34-56-AB-CD-EF\"";
708            assert_eq!(mac, json::decode(&d).unwrap());
709        }
710    }
711
712    #[test]
713    fn test_serialize_roundtrip() {
714        use rustc_serialize::json;
715
716        let m1 = MacAddress::parse_str("12:34:56:AB:CD:EF").unwrap();
717        let s = json::encode(&m1).unwrap();
718        let m2 = json::decode(&s).unwrap();
719        assert_eq!(m1, m2);
720    }
721
722    #[test]
723    fn test_fmt_debug() {
724        let mac = MacAddress::parse_str("12:34:56:AB:CD:EF").unwrap();
725        assert_eq!(
726            "MacAddress(\"12:34:56:ab:cd:ef\")".to_owned(),
727            format!("{:?}", mac)
728        );
729    }
730
731    #[test]
732    fn test_fmt() {
733        let mac = MacAddress::parse_str("0x123456ABCDEF").unwrap();
734        match MacAddress::get_display_format() {
735            MacAddressFormat::HexString => {
736                assert_eq!("12:34:56:ab:cd:ef".to_owned(), format!("{}", mac))
737            }
738            _ => assert_eq!("12-34-56-ab-cd-ef".to_owned(), format!("{}", mac)),
739        };
740    }
741
742    #[test]
743    fn test_fmt_parse_errors() {
744        assert_eq!(
745            "Err(InvalidByteCount(7, [18, 52, 86, 171, 205, 239]))".to_owned(),
746            format!("{:?}", MacAddress::parse_str("123456ABCDEF1"))
747        );
748        assert_eq!(
749            "Err(InvalidLength(19))",
750            format!("{:?}", MacAddress::parse_str("12##45#67#89#AB#C#D"))
751        );
752    }
753
754    #[test]
755    #[cfg(feature = "serde_json")]
756    fn test_serde_json_serialize() {
757        use serde_json;
758        let serialized =
759            serde_json::to_string(&MacAddress::parse_str("12:34:56:AB:CD:EF").unwrap()).unwrap();
760        if cfg!(feature = "disp_hexstring") {
761            assert_eq!("\"12:34:56:ab:cd:ef\"", serialized);
762        } else {
763            assert_eq!("\"12-34-56-ab-cd-ef\"", serialized);
764        }
765    }
766
767    #[test]
768    #[cfg(feature = "serde_json")]
769    fn test_serde_json_deserialize() {
770        use serde_json;
771        let mac = MacAddress::parse_str("12:34:56:AB:CD:EF").unwrap();
772        let deserialized: MacAddress = serde_json::from_str("\"12-34-56-AB-CD-EF\"").unwrap();
773        assert_eq!(deserialized, mac);
774    }
775
776    #[test]
777    #[should_panic(expected = "Invalid length; expecting 11 to 17 chars, found 2")]
778    #[cfg(feature = "serde_json")]
779    fn test_serde_json_deserialize_panic() {
780        let _should_panic: MacAddress = serde_json::from_str("\"12\"").unwrap();
781    }
782
783    #[test]
784    #[cfg(feature = "serde_bytes")]
785    fn test_serde_bytes_serialization_roundtrip() {
786        use bincode;
787        let mac = MacAddress::parse_str("12:34:56:AB:CD:EF").unwrap();
788        let mut buffer = Vec::new();
789        bincode::serialize_into(&mut buffer, &mac).unwrap();
790        let deserialized: MacAddress = bincode::deserialize_from(&*buffer).unwrap();
791        assert_eq!(deserialized, mac);
792    }
793
794    #[test]
795    fn test_macaddressformat_derive() {
796        assert_eq!(MacAddressFormat::HexString, MacAddressFormat::HexString);
797        assert_ne!(MacAddressFormat::HexString, MacAddressFormat::Canonical);
798    }
799
800    #[test]
801    fn test_parseerror_fmt() {
802        assert_eq!(
803            "Invalid length; expecting 11 to 17 chars, found 2".to_owned(),
804            format!("{}", ParseError::InvalidLength(2))
805        );
806        assert_eq!(
807            "Invalid length; expecting 11 to 17 chars, found 2".to_owned(),
808            ParseError::InvalidLength(2).to_string()
809        );
810    }
811
812    #[test]
813    fn test_to_array() {
814        let eui: Eui48 = [0x12, 0x34, 0x56, 0xAB, 0xCD, 0xEF];
815        let mac = MacAddress::new(eui);
816        assert_eq!(eui, MacAddress::new(eui).to_array());
817        assert_eq!(mac, MacAddress::new(mac.to_array()));
818    }
819}