nonymous/
core.rs

1//! Primitive protocol types: [`Type`], [`Class`], [`Opcode`], [`Rcode`], [`Serial`], [`Ttl`].
2
3pub use crate::core::error::{
4    ClassFromStrError, OpcodeRangeError, RcodeRangeError, SerialAdditionError, TtlFromStrError,
5    TypeFromStrError,
6};
7
8pub(crate) mod r#const;
9
10use self::r#const::{CLASS, RCODE, TYPE};
11
12use core::{fmt::Debug, str::FromStr};
13
14pub mod error {
15    error!(OpcodeRangeError);
16    #[derive(Debug, displaydoc::Display)]
17    /// OPCODE out of range: {0}
18    pub struct OpcodeRangeError(pub(crate) u8);
19
20    error!(RcodeRangeError);
21    #[derive(Debug, displaydoc::Display)]
22    /// RCODE out of range
23    pub enum RcodeRangeError {
24        /// extended RCODE exceeds 12 bits: {0}
25        Extended(u16),
26
27        /// non-extended RCODE exceeds 4 bits: {0}
28        Basic(u8),
29    }
30
31    error!(TypeFromStrError);
32    #[derive(Debug, displaydoc::Display)]
33    /// unrecognised TYPE name
34    pub struct TypeFromStrError;
35
36    error!(ClassFromStrError);
37    #[derive(Debug, displaydoc::Display)]
38    /// unrecognised CLASS name
39    pub struct ClassFromStrError;
40
41    error!(TtlFromStrError);
42    #[derive(Debug, displaydoc::Display)]
43    /// failed to parse TTL value
44    pub struct TtlFromStrError;
45
46    error!(SerialAdditionError);
47    #[derive(Debug, displaydoc::Display, PartialEq)]
48    /// serial number addition overflow
49    pub struct SerialAdditionError;
50}
51
52#[derive(Debug, Clone, PartialEq)]
53pub struct Opcode(u8);
54
55#[derive(Debug, Clone, PartialEq)]
56pub struct Rcode(u16);
57
58#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
59pub struct Type(u16);
60
61#[derive(Debug, Clone, PartialEq)]
62pub struct Class(u16);
63
64#[derive(Debug, Clone)]
65pub struct Ttl(u32);
66
67#[derive(Debug, Clone, PartialEq)]
68pub struct Serial(u32);
69
70impl Opcode {
71    pub const fn new(value: u8) -> Result<Self, OpcodeRangeError> {
72        if value > 0xF {
73            return Err(OpcodeRangeError(value));
74        }
75
76        Ok(Self(value))
77    }
78
79    pub const fn value(&self) -> u8 {
80        self.0
81    }
82}
83
84impl Rcode {
85    pub const fn new(value: u16) -> Result<Self, RcodeRangeError> {
86        if value > 0xFFF {
87            return Err(RcodeRangeError::Extended(value));
88        }
89
90        Ok(Self(value))
91    }
92
93    pub const fn from_basic_extended(
94        basic_part: u8,
95        extended_part: u8,
96    ) -> Result<Self, RcodeRangeError> {
97        if basic_part > 0xF {
98            return Err(RcodeRangeError::Basic(basic_part));
99        }
100
101        // TODO use u16::from() once it becomes const
102        let basic_part = basic_part as u16;
103        let extended_part = extended_part as u16;
104
105        Self::new(extended_part << 4 | basic_part)
106    }
107
108    pub const fn name_opt(&self) -> Option<&'static str> {
109        RCODE.to_name_opt(self.value())
110    }
111
112    pub const fn name_tsig(&self) -> Option<&'static str> {
113        RCODE.to_name_tsig(self.value())
114    }
115
116    pub const fn value(&self) -> u16 {
117        self.0
118    }
119
120    pub const fn basic_part(&self) -> u8 {
121        // FIXME rust#87852
122        // (self.0 & 0xF).try_into().expect("guaranteed by Self::of")
123        (self.0 & 0xF) as u8
124    }
125
126    pub const fn extended_part(&self) -> u8 {
127        // FIXME rust#87852
128        // (self.0 >> 4).try_into().expect("guaranteed by Self::of")
129        (self.0 >> 4) as u8
130    }
131}
132
133impl Type {
134    pub const fn new(value: u16) -> Self {
135        Self(value)
136    }
137
138    pub const fn value(&self) -> u16 {
139        self.0
140    }
141}
142
143impl Class {
144    pub const fn new(value: u16) -> Self {
145        Self(value)
146    }
147
148    pub const fn value(&self) -> u16 {
149        self.0
150    }
151}
152
153impl Ttl {
154    pub const fn new(value: u32) -> Self {
155        Self(value)
156    }
157
158    pub const fn value(&self) -> u32 {
159        self.0
160    }
161
162    pub(crate) const fn edns_version(&self) -> u8 {
163        (self.value() >> 16 & 0xFF) as _
164    }
165}
166
167impl Serial {
168    pub const fn new(value: u32) -> Self {
169        Self(value)
170    }
171
172    pub const fn value(&self) -> u32 {
173        self.0
174    }
175}
176
177impl FromStr for Type {
178    type Err = TypeFromStrError;
179
180    fn from_str(source: &str) -> Result<Self, Self::Err> {
181        if let Some(value) = TYPE.to_number(&source) {
182            return Ok(Type(value));
183        }
184
185        // RFC 3597 § 5
186        if source.starts_with("TYPE") {
187            return Ok(Type(
188                u16::from_str_radix(&source[4..], 10).map_err(|_| TypeFromStrError)?,
189            ));
190        }
191
192        Err(TypeFromStrError)
193    }
194}
195
196impl FromStr for Class {
197    type Err = ClassFromStrError;
198
199    fn from_str(source: &str) -> Result<Self, Self::Err> {
200        if let Some(value) = CLASS.to_number(&source) {
201            return Ok(Class(value));
202        }
203
204        // RFC 3597 § 5
205        if source.starts_with("CLASS") {
206            return Ok(Class(
207                u16::from_str_radix(&source[5..], 10).map_err(|_| ClassFromStrError)?,
208            ));
209        }
210
211        Err(ClassFromStrError)
212    }
213}
214
215#[cfg(all(test, feature = "bench"))]
216mod bench {
217    mod parse {
218        extern crate test;
219
220        use test::Bencher;
221
222        use crate::core::{Class, Type};
223
224        #[bench]
225        fn r#type(bencher: &mut Bencher) {
226            bencher.iter(|| "cname".parse::<Type>());
227        }
228
229        #[bench]
230        fn class(bencher: &mut Bencher) {
231            bencher.iter(|| "hs".parse::<Class>());
232        }
233    }
234}