From e86a8e2fe32bac4bca38684bfa2bfe0ff14dd20a Mon Sep 17 00:00:00 2001 From: Martin Hoffmann Date: Wed, 24 Apr 2024 17:34:59 +0200 Subject: [PATCH] Improve Debug impl for IANA types. (#298) This PR changes the Debug implementation of the types in base::iana to show a value as the constant if it matches it. E.g., Class(1) is now shown as Class::IN. --- src/base/iana/class.rs | 10 ++++ src/base/iana/macros.rs | 25 ++++++++- src/base/iana/rcode.rs | 112 ++++++++++++++++++++++++++++------------ 3 files changed, 114 insertions(+), 33 deletions(-) diff --git a/src/base/iana/class.rs b/src/base/iana/class.rs index 6e7a8089..51caef05 100644 --- a/src/base/iana/class.rs +++ b/src/base/iana/class.rs @@ -62,6 +62,7 @@ int_enum_str_with_prefix!(Class, "CLASS", b"CLASS", u16, "unknown class"); #[cfg(test)] mod test { + #[cfg(feature = "serde")] #[test] fn ser_de() { @@ -73,4 +74,13 @@ mod test { assert_tokens(&Class::IN.compact(), &[Token::U16(1)]); assert_tokens(&Class(5).compact(), &[Token::U16(5)]); } + + #[cfg(feature = "std")] + #[test] + fn debug() { + use super::Class; + + assert_eq!(format!("{:?}", Class::IN), "Class::IN"); + assert_eq!(format!("{:?}", Class(69)), "Class(69)"); + } } diff --git a/src/base/iana/macros.rs b/src/base/iana/macros.rs index 9a1b1fb3..93c1c2b2 100644 --- a/src/base/iana/macros.rs +++ b/src/base/iana/macros.rs @@ -12,7 +12,7 @@ macro_rules! int_enum { $( $(#[$variant_attr:meta])* ( $variant:ident => $value:expr, $mnemonic:expr) )* ) => { $(#[$attr])* - #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] + #[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct $ianatype($inttype); impl $ianatype { @@ -101,6 +101,29 @@ macro_rules! int_enum { value.to_int() } } + + //--- Debug + + impl core::fmt::Debug for $ianatype { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + match self.to_mnemonic().and_then(|bytes| { + core::str::from_utf8(bytes).ok() + }) { + Some(mnemonic) => { + write!( + f, + concat!(stringify!($ianatype), "::{}"), + mnemonic + ) + } + None => { + f.debug_tuple(stringify!($ianatype)) + .field(&self.0) + .finish() + } + } + } + } } } diff --git a/src/base/iana/rcode.rs b/src/base/iana/rcode.rs index 96a83c53..e9d1525b 100644 --- a/src/base/iana/rcode.rs +++ b/src/base/iana/rcode.rs @@ -45,7 +45,7 @@ use core::fmt; /// [IANA DNS RCODEs]: http://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-6 /// [RFC 1035]: https://tools.ietf.org/html/rfc1035 /// [RFC 2671]: https://tools.ietf.org/html/rfc2671 -#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +#[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct Rcode(u8); impl Rcode { @@ -193,6 +193,25 @@ impl Rcode { pub const fn to_int(self) -> u8 { self.0 } + + /// Returns the mnemonic for this value if there is one. + #[must_use] + pub const fn to_mnemonic(self) -> Option<&'static [u8]> { + match self { + Rcode::NOERROR => Some(b"NOERROR"), + Rcode::FORMERR => Some(b"FORMERR"), + Rcode::SERVFAIL => Some(b"SERVFAIL"), + Rcode::NXDOMAIN => Some(b"NXDOMAIN"), + Rcode::NOTIMP => Some(b"NOTIMP"), + Rcode::REFUSED => Some(b"REFUSED"), + Rcode::YXDOMAIN => Some(b"YXDOMAIN"), + Rcode::YXRRSET => Some(b"YXRRSET"), + Rcode::NXRRSET => Some(b"NXRRSET"), + Rcode::NOTAUTH => Some(b"NOAUTH"), + Rcode::NOTZONE => Some(b"NOTZONE"), + _ => None, + } + } } //--- TryFrom and From @@ -211,23 +230,28 @@ impl From for u8 { } } -//--- Display +//--- Display and Debug impl fmt::Display for Rcode { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - Rcode::NOERROR => "NOERROR".fmt(f), - Rcode::FORMERR => "FORMERR".fmt(f), - Rcode::SERVFAIL => "SERVFAIL".fmt(f), - Rcode::NXDOMAIN => "NXDOMAIN".fmt(f), - Rcode::NOTIMP => "NOTIMP".fmt(f), - Rcode::REFUSED => "REFUSED".fmt(f), - Rcode::YXDOMAIN => "YXDOMAIN".fmt(f), - Rcode::YXRRSET => "YXRRSET".fmt(f), - Rcode::NXRRSET => "NXRRSET".fmt(f), - Rcode::NOTAUTH => "NOAUTH".fmt(f), - Rcode::NOTZONE => "NOTZONE".fmt(f), - _ => self.0.fmt(f), + match self + .to_mnemonic() + .and_then(|bytes| core::str::from_utf8(bytes).ok()) + { + Some(mnemonic) => f.write_str(mnemonic), + None => self.0.fmt(f), + } + } +} + +impl fmt::Debug for Rcode { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self + .to_mnemonic() + .and_then(|bytes| core::str::from_utf8(bytes).ok()) + { + Some(mnemonic) => write!(f, "Rcode::{}", mnemonic), + None => f.debug_tuple("Rcode").field(&self.0).finish(), } } } @@ -314,7 +338,7 @@ impl<'de> serde::Deserialize<'de> for Rcode { /// [RFC 2845]: https://tools.ietf.org/html/rfc2845 /// [RFC 2930]: https://tools.ietf.org/html/rfc2930 /// [RFC 6891]: https://tools.ietf.org/html/rfc6891 -#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +#[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct OptRcode(u16); impl OptRcode { @@ -514,6 +538,27 @@ impl OptRcode { pub fn ext(self) -> u8 { self.to_parts().1 } + + /// Returns the mnemonic for this value if there is one. + #[must_use] + pub const fn to_mnemonic(self) -> Option<&'static [u8]> { + match self { + OptRcode::NOERROR => Some(b"NOERROR"), + OptRcode::FORMERR => Some(b"FORMERR"), + OptRcode::SERVFAIL => Some(b"SERVFAIL"), + OptRcode::NXDOMAIN => Some(b"NXDOMAIN"), + OptRcode::NOTIMP => Some(b"NOTIMP"), + OptRcode::REFUSED => Some(b"REFUSED"), + OptRcode::YXDOMAIN => Some(b"YXDOMAIN"), + OptRcode::YXRRSET => Some(b"YXRRSET"), + OptRcode::NXRRSET => Some(b"NXRRSET"), + OptRcode::NOTAUTH => Some(b"NOAUTH"), + OptRcode::NOTZONE => Some(b"NOTZONE"), + OptRcode::BADVERS => Some(b"BADVERS"), + OptRcode::BADCOOKIE => Some(b"BADCOOKIE"), + _ => None, + } + } } //--- TryFrom and From @@ -538,25 +583,28 @@ impl From for OptRcode { } } -//--- Display +//--- Display and Debug impl fmt::Display for OptRcode { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - OptRcode::NOERROR => "NOERROR".fmt(f), - OptRcode::FORMERR => "FORMERR".fmt(f), - OptRcode::SERVFAIL => "SERVFAIL".fmt(f), - OptRcode::NXDOMAIN => "NXDOMAIN".fmt(f), - OptRcode::NOTIMP => "NOTIMP".fmt(f), - OptRcode::REFUSED => "REFUSED".fmt(f), - OptRcode::YXDOMAIN => "YXDOMAIN".fmt(f), - OptRcode::YXRRSET => "YXRRSET".fmt(f), - OptRcode::NXRRSET => "NXRRSET".fmt(f), - OptRcode::NOTAUTH => "NOAUTH".fmt(f), - OptRcode::NOTZONE => "NOTZONE".fmt(f), - OptRcode::BADVERS => "BADVER".fmt(f), - OptRcode::BADCOOKIE => "BADCOOKIE".fmt(f), - _ => self.0.fmt(f), + match self + .to_mnemonic() + .and_then(|bytes| core::str::from_utf8(bytes).ok()) + { + Some(mnemonic) => f.write_str(mnemonic), + None => self.0.fmt(f), + } + } +} + +impl fmt::Debug for OptRcode { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self + .to_mnemonic() + .and_then(|bytes| core::str::from_utf8(bytes).ok()) + { + Some(mnemonic) => write!(f, "Rcode::{}", mnemonic), + None => f.debug_tuple("Rcode").field(&self.0).finish(), } } }