1use crate::core::MtopError;
2use std::fmt::{self, Display};
3use std::str::FromStr;
4
5#[derive(Debug, Copy, Clone, Eq, PartialEq)]
6#[repr(u16)]
7pub enum RecordType {
8 A,
9 NS,
10 CNAME,
11 SOA,
12 TXT,
13 AAAA,
14 SRV,
15 OPT,
16 Unknown(u16),
17}
18
19impl RecordType {
20 pub fn size(&self) -> usize {
21 2
22 }
23}
24
25impl From<u16> for RecordType {
26 fn from(value: u16) -> Self {
27 match value {
28 1 => Self::A,
29 2 => Self::NS,
30 5 => Self::CNAME,
31 6 => Self::SOA,
32 16 => Self::TXT,
33 28 => Self::AAAA,
34 33 => Self::SRV,
35 41 => Self::OPT,
36 v => Self::Unknown(v),
37 }
38 }
39}
40
41impl From<RecordType> for u16 {
42 fn from(value: RecordType) -> Self {
43 match value {
44 RecordType::A => 1,
45 RecordType::NS => 2,
46 RecordType::CNAME => 5,
47 RecordType::SOA => 6,
48 RecordType::TXT => 16,
49 RecordType::AAAA => 28,
50 RecordType::SRV => 33,
51 RecordType::OPT => 41,
52 RecordType::Unknown(c) => c,
53 }
54 }
55}
56
57impl Display for RecordType {
58 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
59 match self {
60 RecordType::A => write!(f, "A"),
61 RecordType::NS => write!(f, "NS"),
62 RecordType::CNAME => write!(f, "CNAME"),
63 RecordType::SOA => write!(f, "SOA"),
64 RecordType::TXT => write!(f, "TXT"),
65 RecordType::AAAA => write!(f, "AAAA"),
66 RecordType::SRV => write!(f, "SRV"),
67 RecordType::OPT => write!(f, "OPT"),
68 RecordType::Unknown(t) => write!(f, "Unknown({})", t),
69 }
70 }
71}
72
73impl FromStr for RecordType {
74 type Err = MtopError;
75
76 fn from_str(s: &str) -> Result<Self, Self::Err> {
77 let s = s.to_uppercase();
78 match s.as_ref() {
79 "A" => Ok(RecordType::A),
80 "NS" => Ok(RecordType::NS),
81 "CNAME" => Ok(RecordType::CNAME),
82 "SOA" => Ok(RecordType::SOA),
83 "TXT" => Ok(RecordType::TXT),
84 "AAAA" => Ok(RecordType::AAAA),
85 "SRV" => Ok(RecordType::SRV),
86 "OPT" => Ok(RecordType::OPT),
87 v => Err(MtopError::configuration(format!("unknown record type '{}'", v))),
88 }
89 }
90}
91
92#[derive(Debug, Copy, Clone, Eq, PartialEq)]
93#[repr(u16)]
94pub enum RecordClass {
95 INET,
96 CHAOS,
97 HESIOD,
98 NONE,
99 ANY,
100 Unknown(u16),
101}
102
103impl RecordClass {
104 pub fn size(&self) -> usize {
105 2
106 }
107}
108
109impl From<u16> for RecordClass {
110 fn from(value: u16) -> Self {
111 match value {
112 1 => Self::INET,
113 3 => Self::CHAOS,
114 4 => Self::HESIOD,
115 254 => Self::NONE,
116 255 => Self::ANY,
117 v => Self::Unknown(v),
118 }
119 }
120}
121
122impl From<RecordClass> for u16 {
123 fn from(value: RecordClass) -> Self {
124 match value {
125 RecordClass::INET => 1,
126 RecordClass::CHAOS => 3,
127 RecordClass::HESIOD => 4,
128 RecordClass::NONE => 254,
129 RecordClass::ANY => 255,
130 RecordClass::Unknown(c) => c,
131 }
132 }
133}
134
135impl Display for RecordClass {
136 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
137 match self {
138 RecordClass::INET => write!(f, "INET"),
139 RecordClass::HESIOD => write!(f, "HESIOD"),
140 RecordClass::CHAOS => write!(f, "CHAOS"),
141 RecordClass::NONE => write!(f, "NONE"),
142 RecordClass::ANY => write!(f, "ANY"),
143 RecordClass::Unknown(c) => write!(f, "Unknown({})", c),
144 }
145 }
146}
147
148impl FromStr for RecordClass {
149 type Err = MtopError;
150
151 fn from_str(s: &str) -> Result<Self, Self::Err> {
152 let s = s.to_uppercase();
153 match s.as_ref() {
154 "INET" => Ok(RecordClass::INET),
155 "HESIOD" => Ok(RecordClass::HESIOD),
156 "CHAOS" => Ok(RecordClass::CHAOS),
157 "NONE" => Ok(RecordClass::NONE),
158 "ANY" => Ok(RecordClass::ANY),
159 v => Err(MtopError::configuration(format!("unknown record class '{}'", v))),
160 }
161 }
162}
163
164#[cfg(test)]
165mod test {
166 use super::{RecordClass, RecordType};
167 use std::str::FromStr;
168
169 #[test]
170 fn test_record_type_from_u16() {
171 assert_eq!(RecordType::A, RecordType::from(1));
172 assert_eq!(RecordType::NS, RecordType::from(2));
173 assert_eq!(RecordType::CNAME, RecordType::from(5));
174 assert_eq!(RecordType::SOA, RecordType::from(6));
175 assert_eq!(RecordType::TXT, RecordType::from(16));
176 assert_eq!(RecordType::AAAA, RecordType::from(28));
177 assert_eq!(RecordType::SRV, RecordType::from(33));
178 assert_eq!(RecordType::OPT, RecordType::from(41));
179 assert_eq!(RecordType::Unknown(999), RecordType::from(999));
180 }
181
182 #[test]
183 fn test_record_type_to_u16() {
184 assert_eq!(1_u16, RecordType::A.into());
185 assert_eq!(2_u16, RecordType::NS.into());
186 assert_eq!(5_u16, RecordType::CNAME.into());
187 assert_eq!(6_u16, RecordType::SOA.into());
188 assert_eq!(16_u16, RecordType::TXT.into());
189 assert_eq!(28_u16, RecordType::AAAA.into());
190 assert_eq!(33_u16, RecordType::SRV.into());
191 assert_eq!(41_u16, RecordType::OPT.into());
192 assert_eq!(999_u16, RecordType::Unknown(999).into());
193 }
194
195 #[test]
196 fn test_record_type_display() {
197 assert_eq!("A", RecordType::A.to_string());
198 assert_eq!("NS", RecordType::NS.to_string());
199 assert_eq!("CNAME", RecordType::CNAME.to_string());
200 assert_eq!("SOA", RecordType::SOA.to_string());
201 assert_eq!("TXT", RecordType::TXT.to_string());
202 assert_eq!("AAAA", RecordType::AAAA.to_string());
203 assert_eq!("SRV", RecordType::SRV.to_string());
204 assert_eq!("OPT", RecordType::OPT.to_string());
205 assert_eq!("Unknown(999)", RecordType::Unknown(999).to_string());
206 }
207
208 #[test]
209 fn test_record_type_from_str() {
210 assert_eq!(RecordType::A, RecordType::from_str("A").unwrap());
211 assert_eq!(RecordType::NS, RecordType::from_str("NS").unwrap());
212 assert_eq!(RecordType::CNAME, RecordType::from_str("CNAME").unwrap());
213 assert_eq!(RecordType::SOA, RecordType::from_str("SOA").unwrap());
214 assert_eq!(RecordType::TXT, RecordType::from_str("TXT").unwrap());
215 assert_eq!(RecordType::AAAA, RecordType::from_str("AAAA").unwrap());
216 assert_eq!(RecordType::SRV, RecordType::from_str("SRV").unwrap());
217 assert_eq!(RecordType::OPT, RecordType::from_str("OPT").unwrap());
218 assert!(RecordType::from_str("BOGUS").is_err());
219 }
220
221 #[test]
222 fn test_record_class_from_u16() {
223 assert_eq!(RecordClass::INET, RecordClass::from(1));
224 assert_eq!(RecordClass::CHAOS, RecordClass::from(3));
225 assert_eq!(RecordClass::HESIOD, RecordClass::from(4));
226 assert_eq!(RecordClass::NONE, RecordClass::from(254));
227 assert_eq!(RecordClass::ANY, RecordClass::from(255));
228 assert_eq!(RecordClass::Unknown(512), RecordClass::from(512));
229 }
230
231 #[test]
232 fn test_record_class_to_u16() {
233 assert_eq!(1_u16, RecordClass::INET.into());
234 assert_eq!(3_u16, RecordClass::CHAOS.into());
235 assert_eq!(4_u16, RecordClass::HESIOD.into());
236 assert_eq!(254_u16, RecordClass::NONE.into());
237 assert_eq!(255_u16, RecordClass::ANY.into());
238 assert_eq!(512_u16, RecordClass::Unknown(512).into());
239 }
240
241 #[test]
242 fn test_record_class_display() {
243 assert_eq!("INET", RecordClass::INET.to_string());
244 assert_eq!("CHAOS", RecordClass::CHAOS.to_string());
245 assert_eq!("HESIOD", RecordClass::HESIOD.to_string());
246 assert_eq!("NONE", RecordClass::NONE.to_string());
247 assert_eq!("ANY", RecordClass::ANY.to_string());
248 assert_eq!("Unknown(512)", RecordClass::Unknown(512).to_string());
249 }
250
251 #[test]
252 fn test_record_class_from_str() {
253 assert_eq!(RecordClass::INET, RecordClass::from_str("INET").unwrap());
254 assert_eq!(RecordClass::CHAOS, RecordClass::from_str("CHAOS").unwrap());
255 assert_eq!(RecordClass::HESIOD, RecordClass::from_str("HESIOD").unwrap());
256 assert_eq!(RecordClass::NONE, RecordClass::from_str("NONE").unwrap());
257 assert_eq!(RecordClass::ANY, RecordClass::from_str("ANY").unwrap());
258 assert!(RecordClass::from_str("BOGUS").is_err());
259 }
260}