1#[derive(Debug, Copy, Clone)]
14pub enum Registry {
15 Mal,
17 Mam,
19 Mas,
21 Cid,
24 Iab,
26}
27
28#[derive(Debug, Clone)]
30pub struct OuiData {
31 registry: Registry,
32 oui: &'static str,
33 organization: &'static str,
34}
35
36impl OuiData {
37 pub fn registry(&self) -> Registry {
39 self.registry
40 }
41
42 pub fn oui(&self) -> &'static str {
45 self.oui
46 }
47
48 pub fn organization(&self) -> &'static str {
50 self.organization
51 }
52}
53
54pub fn lookup(mac: &str) -> Option<&'static OuiData> {
56 if mac.chars().all(|c| matches!(c, '0'..='9' | 'A'..='F')) {
57 return lookup_prefix(mac);
58 }
59
60 let mac = mac.to_uppercase().replace([':', '-'], "");
61 lookup_prefix(&mac)
62}
63
64fn lookup_prefix(mac: &str) -> Option<&'static OuiData> {
65 let mut result: Option<&'static OuiData> = None;
66 if mac.len() >= 9 {
67 result = OUI_ENTRIES.get(&mac[..9]);
68 }
69 if mac.len() >= 7 {
70 result = result.or_else(|| OUI_ENTRIES.get(&mac[..7]));
71 }
72 if mac.len() >= 6 {
73 result = result.or_else(|| OUI_ENTRIES.get(&mac[..6]));
74 }
75
76 result
77}
78
79include!(concat!(env!("OUT_DIR"), "/oui.rs"));
80
81#[cfg(test)]
82mod test {
83 use super::*;
84
85 #[test]
86 fn should_lookup_entry_from_oui() {
87 let record = lookup("00:00:00:00:00:00").unwrap();
88 assert_eq!(record.organization(), "XEROX CORPORATION");
89 }
90
91 #[test]
92 fn should_accept_mac_with_dashes() {
93 let record = lookup("00-00-00-00-00-00").unwrap();
94 assert_eq!(record.organization(), "XEROX CORPORATION");
95 }
96
97 #[test]
98 fn should_ignore_case() {
99 let record = lookup("50:a6:d8:00:00:00").unwrap();
100 assert_eq!(record.organization(), "Apple, Inc.");
101 }
102
103 #[test]
104 fn should_ignore_colons() {
105 let record = lookup("50A6D8000000").unwrap();
106 assert_eq!(record.organization(), "Apple, Inc.");
107 }
108
109 #[test]
110 fn should_accept_prefix() {
111 let record = lookup("50A6D8").unwrap();
112 assert_eq!(record.organization(), "Apple, Inc.");
113 }
114
115 #[test]
116 fn should_lookup_entry_from_oui28() {
117 let record = lookup("B8:4C:87:40:00:00").unwrap();
118 assert_eq!(record.organization(), "Blum Novotest GmbH");
119 }
120
121 #[test]
122 fn should_lookup_entry_from_oui36() {
123 let record = lookup("8C:1F:64:AF:A0:00").unwrap();
124 assert_eq!(record.organization(), "DATA ELECTRONIC DEVICES, INC");
125 }
126
127 #[test]
128 fn should_lookup_entry_from_cid() {
129 let record = lookup("EA:27:01:00:00:00").unwrap();
130 assert_eq!(record.organization(), "ACCE Technology Corp.");
131 }
132
133 #[test]
134 fn should_lookup_entry_from_iab() {
135 let record = lookup("40:D8:55:0D:70:00").unwrap();
136 assert_eq!(record.organization(), "Avant Technologies");
137 }
138}