domain/base/iana/
macros.rs

1//! Macros making implementing IANA types easier.
2
3/// Creates a standard IANA type wrapping an integer.
4///
5/// This adds impls for `From`, `PartialEq`, `Eq`, `PartialOrd`, `Ord`, and
6/// `Hash`.
7///
8/// For `FromStr` and `Display`, see one of the other macros in this module.
9macro_rules! int_enum {
10    ( $(#[$attr:meta])* =>
11      $ianatype:ident, $inttype:path;
12      $( $(#[$variant_attr:meta])* ( $variant:ident =>
13                                        $value:expr, $mnemonic:expr) )* ) => {
14        $(#[$attr])*
15        #[derive(Clone, Copy, Debug)]
16        pub enum $ianatype {
17            $( $(#[$variant_attr])* $variant ),*,
18
19            /// A raw value given through its integer.
20            Int($inttype)
21        }
22
23        impl $ianatype {
24            /// Returns a value from its raw integer value.
25            #[must_use]
26            pub const fn from_int(value: $inttype) -> Self {
27                match value {
28                    $( $value => $ianatype::$variant ),*,
29                    _ => $ianatype::Int(value)
30                }
31            }
32
33            /// Returns the raw integer value for a value.
34            #[must_use]
35            pub const fn to_int(self) -> $inttype {
36                match self {
37                    $( $ianatype::$variant => $value ),*,
38                    $ianatype::Int(value) => value
39                }
40            }
41
42            /// Returns a value from a well-defined mnemonic.
43            #[must_use]
44            pub fn from_mnemonic(m: &[u8]) -> Option<Self> {
45                $(
46                    if m.eq_ignore_ascii_case($mnemonic) {
47                        return Some($ianatype::$variant)
48                    }
49                )*
50                None
51            }
52
53            /// Returns the mnemonic for this value if there is one.
54            ///
55            /// This will also return a mnemonic if a well-defined variant
56            /// is hidden in a `Int` variant.
57            #[must_use]
58            pub const fn to_mnemonic(self) -> Option<&'static [u8]> {
59                match self {
60                    $( $ianatype::$variant => Some($mnemonic) ),*,
61                    $ianatype::Int(value) => {
62                        match $ianatype::from_int(value) {
63                            $ianatype::Int(_) => None,
64                            value => value.to_mnemonic()
65                        }
66                    }
67                }
68            }
69
70            pub fn parse<'a, Octs: AsRef<[u8]> + ?Sized> (
71                parser: &mut octseq::parse::Parser<'a, Octs>
72            ) -> Result<Self, $crate::base::wire::ParseError> {
73                <$inttype as $crate::base::wire::Parse<'a, Octs>>::parse(
74                    parser
75                ).map(Self::from_int)
76            }
77
78            pub const COMPOSE_LEN: u16 =
79                <$inttype as $crate::base::wire::Compose>::COMPOSE_LEN;
80
81            pub fn compose<Target: octseq::builder::OctetsBuilder + ?Sized>(
82                &self,
83                target: &mut Target
84            ) -> Result<(), Target::AppendError> {
85                $crate::base::wire::Compose::compose(&self.to_int(), target)
86            }
87        }
88
89
90        //--- From
91
92        impl From<$inttype> for $ianatype {
93            fn from(value: $inttype) -> Self {
94                $ianatype::from_int(value)
95            }
96        }
97
98        impl From<$ianatype> for $inttype {
99            fn from(value: $ianatype) -> Self {
100                value.to_int()
101            }
102        }
103
104        impl<'a> From<&'a $ianatype> for $inttype {
105            fn from(value: &'a $ianatype) -> Self {
106                value.to_int()
107            }
108        }
109
110
111        //--- PartialEq and Eq
112
113        impl PartialEq for $ianatype {
114            fn eq(&self, other: &Self) -> bool {
115                self.to_int() == other.to_int()
116            }
117        }
118
119        impl PartialEq<$inttype> for $ianatype {
120            fn eq(&self, other: &$inttype) -> bool {
121                self.to_int() == *other
122            }
123        }
124
125        impl PartialEq<$ianatype> for $inttype {
126            fn eq(&self, other: &$ianatype) -> bool {
127                *self == other.to_int()
128            }
129        }
130
131        impl Eq for $ianatype { }
132
133
134        //--- PartialOrd and Ord
135
136        impl PartialOrd for $ianatype {
137            fn partial_cmp(
138                &self, other: &Self
139            ) -> Option<core::cmp::Ordering> {
140                Some(self.cmp(other))
141            }
142        }
143
144        impl PartialOrd<$inttype> for $ianatype {
145            fn partial_cmp(
146                &self, other: &$inttype
147                ) -> Option<core::cmp::Ordering> {
148                self.to_int().partial_cmp(other)
149            }
150        }
151
152        impl PartialOrd<$ianatype> for $inttype {
153            fn partial_cmp(
154                &self, other: &$ianatype
155            ) -> Option<core::cmp::Ordering> {
156                self.partial_cmp(&other.to_int())
157            }
158        }
159
160        impl Ord for $ianatype {
161            fn cmp(&self, other: &Self) -> core::cmp::Ordering {
162                self.to_int().cmp(&other.to_int())
163            }
164        }
165
166
167        //--- Hash
168
169        impl core::hash::Hash for $ianatype {
170            fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
171                self.to_int().hash(state)
172            }
173        }
174    }
175}
176
177/*
178/// Adds impls for `FromStr` and `Display` to the type given as first argument.
179///
180/// The `FromStr` impl matches only well known mnemonics ignoring case,
181/// otherwise it returns an error of the second argument.
182///
183/// For `Display`, it will display a decimal number for values without
184/// mnemonic.
185macro_rules! int_enum_str_mnemonics_only {
186    ($ianatype:ident, $error:expr) => {
187        impl ::std::str::FromStr for $ianatype {
188            type Err = FromStrError;
189
190            fn from_str(s: &str) -> Result<Self, Self::Err> {
191                // We assume all mnemonics are always ASCII, so using
192                // the bytes representation of `s` is safe.
193                $ianatype::from_mnemonic(s.as_bytes()).ok_or(FromStrError)
194            }
195        }
196
197        impl ::std::fmt::Display for $ianatype {
198            fn fmt(&self, f: &mut ::std::fmt::Formatter)
199                   -> ::std::fmt::Result {
200                use ::std::fmt::Write;
201
202                match self.to_mnemonic() {
203                    Some(m) => {
204                        for ch in m {
205                            f.write_char(*ch as char)?
206                        }
207                        Ok(())
208                    }
209                    None => {
210                        write!(f, "{}", self.to_int())
211                    }
212                }
213            }
214        }
215
216        from_str_error!($error);
217    }
218}
219*/
220
221/// Adds impls for `FromStr` and `Display` to the type given as first argument.
222///
223/// For `FromStr`, recognizes only the decimal values. For `Display`, it will
224/// only print the decimal values.
225///
226/// If the `serde` feature is enabled, also adds implementation for
227/// `Serialize` and `Deserialize`, serializing values as their decimal values.
228macro_rules! int_enum_str_decimal {
229    ($ianatype:ident, $inttype:ident) => {
230        impl $ianatype {
231            #[must_use]
232            pub fn from_bytes(bytes: &[u8]) -> Option<Self> {
233                core::str::from_utf8(bytes)
234                    .ok()
235                    .and_then(|r| r.parse().ok().map($ianatype::from_int))
236            }
237        }
238
239        impl core::str::FromStr for $ianatype {
240            type Err = core::num::ParseIntError;
241
242            fn from_str(s: &str) -> Result<Self, Self::Err> {
243                s.parse().map($ianatype::from_int)
244            }
245        }
246
247        scan_impl!($ianatype);
248
249        impl core::fmt::Display for $ianatype {
250            fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
251                write!(f, "{}", self.to_int())
252            }
253        }
254
255        #[cfg(feature = "serde")]
256        impl serde::Serialize for $ianatype {
257            fn serialize<S: serde::Serializer>(
258                &self,
259                serializer: S,
260            ) -> Result<S::Ok, S::Error> {
261                self.to_int().serialize(serializer)
262            }
263        }
264
265        #[cfg(feature = "serde")]
266        impl<'de> serde::Deserialize<'de> for $ianatype {
267            fn deserialize<D: serde::Deserializer<'de>>(
268                deserializer: D,
269            ) -> Result<Self, D::Error> {
270                $inttype::deserialize(deserializer).map(Into::into)
271            }
272        }
273    };
274}
275
276/// Adds impls for `FromStr` and `Display` to the type given as first argument.
277///
278/// For `FromStr`, recognizes all mnemonics case-insensitively as well as a
279/// decimal number representing any value.
280///
281/// For `Display`, it will display a decimal number for values without
282/// mnemonic.
283///
284/// If the `serde` feature is enabled, also adds implementation for
285/// `Serialize` and `Deserialize`. Values will be serialized using the
286/// mnemonic if availbale or otherwise the integer value for human readable
287/// formats and the integer value for compact formats. Both mnemonics and
288/// integer values can be deserialized.
289macro_rules! int_enum_str_with_decimal {
290    ($ianatype:ident, $inttype:ident, $error:expr) => {
291        impl $ianatype {
292            #[must_use]
293            pub fn from_bytes(bytes: &[u8]) -> Option<Self> {
294                $ianatype::from_mnemonic(bytes).or_else(|| {
295                    core::str::from_utf8(bytes)
296                        .ok()
297                        .and_then(|r| r.parse().ok().map($ianatype::from_int))
298                })
299            }
300        }
301
302        impl core::str::FromStr for $ianatype {
303            type Err = FromStrError;
304
305            fn from_str(s: &str) -> Result<Self, Self::Err> {
306                // We assume all mnemonics are always ASCII, so using
307                // the bytes representation of `s` is safe.
308                match $ianatype::from_mnemonic(s.as_bytes()) {
309                    Some(res) => Ok(res),
310                    None => {
311                        if let Ok(res) = s.parse() {
312                            Ok($ianatype::from_int(res))
313                        } else {
314                            Err(FromStrError)
315                        }
316                    }
317                }
318            }
319        }
320
321        impl core::fmt::Display for $ianatype {
322            fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
323                use core::fmt::Write;
324
325                match self.to_mnemonic() {
326                    Some(m) => {
327                        for ch in m {
328                            f.write_char(*ch as char)?
329                        }
330                        Ok(())
331                    }
332                    None => {
333                        write!(f, "{}", self.to_int())
334                    }
335                }
336            }
337        }
338
339        scan_impl!($ianatype);
340
341        #[cfg(feature = "serde")]
342        impl serde::Serialize for $ianatype {
343            fn serialize<S: serde::Serializer>(
344                &self,
345                serializer: S,
346            ) -> Result<S::Ok, S::Error> {
347                if serializer.is_human_readable() {
348                    match self
349                        .to_mnemonic()
350                        .and_then(|value| core::str::from_utf8(value).ok())
351                    {
352                        Some(value) => value.serialize(serializer),
353                        None => self.to_int().serialize(serializer),
354                    }
355                } else {
356                    self.to_int().serialize(serializer)
357                }
358            }
359        }
360
361        #[cfg(feature = "serde")]
362        impl<'de> serde::Deserialize<'de> for $ianatype {
363            fn deserialize<D: serde::Deserializer<'de>>(
364                deserializer: D,
365            ) -> Result<Self, D::Error> {
366                use crate::base::serde::DeserializeNativeOrStr;
367
368                $inttype::deserialize_native_or_str(deserializer)
369            }
370        }
371
372        from_str_error!($error);
373    };
374}
375
376/// Adds impls for `FromStr` and `Display` to the type given as first argument.
377///
378/// For `FromStr` recognizes all defined mnemonics ignoring case. Additionally
379/// recognizes a value starting with the prefix given in the second argument
380/// (again, ignoring case) directly followed by a decimal number.
381///
382/// For `Display`, values without mnemonic will be written starting with the
383/// prefix directly followed by the decimal representation of the value.
384macro_rules! int_enum_str_with_prefix {
385    ($ianatype:ident, $str_prefix:expr, $u8_prefix:expr, $inttype:ident,
386     $error:expr) => {
387        impl $ianatype {
388            #[must_use]
389            pub fn from_bytes(bytes: &[u8]) -> Option<Self> {
390                $ianatype::from_mnemonic(bytes).or_else(|| {
391                    if bytes.len() <= $u8_prefix.len() {
392                        return None;
393                    }
394                    let (l, r) = bytes.split_at($u8_prefix.len());
395                    if !l.eq_ignore_ascii_case($u8_prefix) {
396                        return None;
397                    }
398                    let r = match core::str::from_utf8(r) {
399                        Ok(r) => r,
400                        Err(_) => return None,
401                    };
402                    r.parse().ok().map($ianatype::from_int)
403                })
404            }
405        }
406
407        impl core::str::FromStr for $ianatype {
408            type Err = FromStrError;
409
410            fn from_str(s: &str) -> Result<Self, Self::Err> {
411                // We assume all mnemonics are always ASCII, so using
412                // the bytes representation of `s` is safe.
413                match $ianatype::from_mnemonic(s.as_bytes()) {
414                    Some(res) => Ok(res),
415                    None => {
416                        if let Some((n, _)) =
417                            s.char_indices().nth($str_prefix.len())
418                        {
419                            let (l, r) = s.split_at(n);
420                            if l.eq_ignore_ascii_case($str_prefix) {
421                                let value = match r.parse() {
422                                    Ok(x) => x,
423                                    Err(..) => return Err(FromStrError),
424                                };
425                                Ok($ianatype::from_int(value))
426                            } else {
427                                Err(FromStrError)
428                            }
429                        } else {
430                            Err(FromStrError)
431                        }
432                    }
433                }
434            }
435        }
436
437        impl core::fmt::Display for $ianatype {
438            fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
439                use core::fmt::Write;
440
441                match self.to_mnemonic() {
442                    Some(m) => {
443                        for ch in m {
444                            f.write_char(*ch as char)?
445                        }
446                        Ok(())
447                    }
448                    None => {
449                        write!(f, "{}{}", $str_prefix, self.to_int())
450                    }
451                }
452            }
453        }
454
455        scan_impl!($ianatype);
456
457        #[cfg(feature = "serde")]
458        impl serde::Serialize for $ianatype {
459            fn serialize<S: serde::Serializer>(
460                &self,
461                serializer: S,
462            ) -> Result<S::Ok, S::Error> {
463                if serializer.is_human_readable() {
464                    serializer.collect_str(&format_args!("{}", self))
465                } else {
466                    self.to_int().serialize(serializer)
467                }
468            }
469        }
470
471        #[cfg(feature = "serde")]
472        impl<'de> serde::Deserialize<'de> for $ianatype {
473            fn deserialize<D: serde::Deserializer<'de>>(
474                deserializer: D,
475            ) -> Result<Self, D::Error> {
476                use crate::base::serde::DeserializeNativeOrStr;
477
478                $inttype::deserialize_native_or_str(deserializer)
479            }
480        }
481
482        from_str_error!($error);
483    };
484}
485
486macro_rules! scan_impl {
487    ($ianatype:ident) => {
488        impl $ianatype {
489            pub fn scan<S: $crate::base::scan::Scanner>(
490                scanner: &mut S,
491            ) -> Result<Self, S::Error> {
492                scanner.scan_ascii_str(|s| {
493                    core::str::FromStr::from_str(s).map_err(|_| {
494                        $crate::base::scan::ScannerError::custom(concat!(
495                            "expected ",
496                            stringify!($ianatype)
497                        ))
498                    })
499                })
500            }
501        }
502    };
503}
504
505macro_rules! from_str_error {
506    ($description:expr) => {
507        #[derive(Clone, Debug)]
508        pub struct FromStrError;
509
510        #[cfg(feature = "std")]
511        impl std::error::Error for FromStrError {
512            fn description(&self) -> &str {
513                $description
514            }
515        }
516
517        impl core::fmt::Display for FromStrError {
518            fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
519                $description.fmt(f)
520            }
521        }
522    };
523}