mirror of
https://github.com/NLnetLabs/domain.git
synced 2026-08-17 21:15:41 +02:00
Merge branch 'main' into new-zonefile
This commit is contained in:
+5
-5
@@ -43,7 +43,7 @@ jiff = { version = "0.2.1", default-features = false }
|
||||
arbitrary = { version = "1.4.1", optional = true, features = ["derive"] }
|
||||
bumpalo = { version = "3.12", optional = true }
|
||||
constant_time_eq = { version = "0.4.2", optional = true }
|
||||
octseq = { version = "0.6.0", default-features = false }
|
||||
octseq = { version = "0.6.1", default-features = false }
|
||||
rand = { version = "0.10.1", optional = true }
|
||||
arc-swap = { version = "1.7.0", optional = true }
|
||||
bytes = { version = "1.2", optional = true, default-features = false }
|
||||
@@ -60,7 +60,7 @@ proc-macro2 = { version = "1.0.69", optional = true } # Force proc-macro2 to
|
||||
ring = { version = "0.17.2", optional = true }
|
||||
rustversion = { version = "1", optional = true }
|
||||
secrecy = { version = "0.10", optional = true }
|
||||
serde = { version = "1.0.130", optional = true, features = ["derive"] }
|
||||
serde = { version = "1.0.130", optional = true, default-features = false, features = ["derive"] }
|
||||
siphasher = { version = "1", optional = true }
|
||||
smallvec = { version = "1.3", optional = true }
|
||||
tokio = { version = "1.33", optional = true, features = ["io-util", "macros", "net", "time", "sync", "rt-multi-thread" ] }
|
||||
@@ -73,15 +73,15 @@ tracing-subscriber = { version = "0.3.18", optional = true, features = ["env-fil
|
||||
default = ["std", "rand"]
|
||||
|
||||
# Support for libraries
|
||||
alloc = ["jiff/alloc"]
|
||||
alloc = ["jiff/alloc", "octseq/alloc", "serde?/alloc"]
|
||||
bumpalo = ["dep:bumpalo", "std"]
|
||||
bytes = ["dep:bytes", "octseq/bytes"]
|
||||
chrono = ["dep:chrono"]
|
||||
heapless = ["dep:heapless", "octseq/heapless"]
|
||||
rand = ["dep:rand"]
|
||||
serde = ["std", "dep:serde", "octseq/serde"]
|
||||
serde = ["dep:serde", "octseq/serde"]
|
||||
smallvec = ["dep:smallvec", "octseq/smallvec"]
|
||||
std = ["alloc", "dep:hashbrown", "bumpalo?/std", "bytes?/std", "octseq/std", "jiff/std"]
|
||||
std = ["alloc", "dep:hashbrown", "bumpalo?/std", "bytes?/std", "octseq/std", "jiff/std", "serde?/std"]
|
||||
tracing = ["dep:log", "dep:tracing"]
|
||||
|
||||
# Cryptographic backends
|
||||
|
||||
@@ -8,10 +8,16 @@ New
|
||||
|
||||
Improvements
|
||||
|
||||
* Implemented `to_mnemonic_str` for `Rcode` and `OptCode` ([#668] and [#648]
|
||||
by [@rossmacarthur])
|
||||
|
||||
Bug fixes
|
||||
|
||||
Other changes
|
||||
|
||||
[#648]: https://github.com/NLnetLabs/domain/pull/648
|
||||
[#668]: https://github.com/NLnetLabs/domain/pull/668
|
||||
[@rossmacarthur]: https://github.com/rossmacarthur
|
||||
|
||||
## 0.12.1
|
||||
|
||||
|
||||
+81
-40
@@ -19,6 +19,7 @@
|
||||
// bits of the wrapped integer.
|
||||
|
||||
use core::fmt;
|
||||
use core::str;
|
||||
use core::str::FromStr;
|
||||
|
||||
//------------ Rcode ---------------------------------------------------------
|
||||
@@ -195,18 +196,27 @@ impl Rcode {
|
||||
/// Returns the mnemonic for this value if there is one.
|
||||
#[must_use]
|
||||
pub const fn to_mnemonic(self) -> Option<&'static [u8]> {
|
||||
match self.to_mnemonic_str() {
|
||||
Some(m) => Some(m.as_bytes()),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the mnemonic as a `&str` for this value if there is one
|
||||
#[must_use]
|
||||
pub const fn to_mnemonic_str(self) -> Option<&'static str> {
|
||||
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"NOTAUTH"),
|
||||
Rcode::NOTZONE => Some(b"NOTZONE"),
|
||||
Rcode::NOERROR => Some("NOERROR"),
|
||||
Rcode::FORMERR => Some("FORMERR"),
|
||||
Rcode::SERVFAIL => Some("SERVFAIL"),
|
||||
Rcode::NXDOMAIN => Some("NXDOMAIN"),
|
||||
Rcode::NOTIMP => Some("NOTIMP"),
|
||||
Rcode::REFUSED => Some("REFUSED"),
|
||||
Rcode::YXDOMAIN => Some("YXDOMAIN"),
|
||||
Rcode::YXRRSET => Some("YXRRSET"),
|
||||
Rcode::NXRRSET => Some("NXRRSET"),
|
||||
Rcode::NOTAUTH => Some("NOTAUTH"),
|
||||
Rcode::NOTZONE => Some("NOTZONE"),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@@ -255,10 +265,7 @@ impl From<Rcode> for u8 {
|
||||
|
||||
impl fmt::Display for Rcode {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self
|
||||
.to_mnemonic()
|
||||
.and_then(|bytes| core::str::from_utf8(bytes).ok())
|
||||
{
|
||||
match self.to_mnemonic_str() {
|
||||
Some(mnemonic) => f.write_str(mnemonic),
|
||||
None => self.0.fmt(f),
|
||||
}
|
||||
@@ -267,10 +274,7 @@ impl fmt::Display for Rcode {
|
||||
|
||||
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())
|
||||
{
|
||||
match self.to_mnemonic_str() {
|
||||
Some(mnemonic) => write!(f, "Rcode::{}", mnemonic),
|
||||
None => f.debug_tuple("Rcode").field(&self.0).finish(),
|
||||
}
|
||||
@@ -575,20 +579,29 @@ impl OptRcode {
|
||||
/// Returns the mnemonic for this value if there is one.
|
||||
#[must_use]
|
||||
pub const fn to_mnemonic(self) -> Option<&'static [u8]> {
|
||||
match self.to_mnemonic_str() {
|
||||
Some(m) => Some(m.as_bytes()),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the mnemonic as a `&str` for this value if there is one
|
||||
#[must_use]
|
||||
pub const fn to_mnemonic_str(self) -> Option<&'static str> {
|
||||
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"NOTAUTH"),
|
||||
OptRcode::NOTZONE => Some(b"NOTZONE"),
|
||||
OptRcode::BADVERS => Some(b"BADVERS"),
|
||||
OptRcode::BADCOOKIE => Some(b"BADCOOKIE"),
|
||||
OptRcode::NOERROR => Some("NOERROR"),
|
||||
OptRcode::FORMERR => Some("FORMERR"),
|
||||
OptRcode::SERVFAIL => Some("SERVFAIL"),
|
||||
OptRcode::NXDOMAIN => Some("NXDOMAIN"),
|
||||
OptRcode::NOTIMP => Some("NOTIMP"),
|
||||
OptRcode::REFUSED => Some("REFUSED"),
|
||||
OptRcode::YXDOMAIN => Some("YXDOMAIN"),
|
||||
OptRcode::YXRRSET => Some("YXRRSET"),
|
||||
OptRcode::NXRRSET => Some("NXRRSET"),
|
||||
OptRcode::NOTAUTH => Some("NOTAUTH"),
|
||||
OptRcode::NOTZONE => Some("NOTZONE"),
|
||||
OptRcode::BADVERS => Some("BADVERS"),
|
||||
OptRcode::BADCOOKIE => Some("BADCOOKIE"),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@@ -645,10 +658,7 @@ impl From<Rcode> for OptRcode {
|
||||
|
||||
impl fmt::Display for OptRcode {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self
|
||||
.to_mnemonic()
|
||||
.and_then(|bytes| core::str::from_utf8(bytes).ok())
|
||||
{
|
||||
match self.to_mnemonic_str() {
|
||||
Some(mnemonic) => f.write_str(mnemonic),
|
||||
None => self.0.fmt(f),
|
||||
}
|
||||
@@ -657,10 +667,7 @@ impl fmt::Display for OptRcode {
|
||||
|
||||
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())
|
||||
{
|
||||
match self.to_mnemonic_str() {
|
||||
Some(mnemonic) => write!(f, "Rcode::{}", mnemonic),
|
||||
None => f.debug_tuple("Rcode").field(&self.0).finish(),
|
||||
}
|
||||
@@ -973,6 +980,22 @@ mod test {
|
||||
assert!("#$%!@".parse::<Rcode>().is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rcode_tomnemonicstr() {
|
||||
assert_eq!(Rcode::NOERROR.to_mnemonic_str(), Some("NOERROR"));
|
||||
assert_eq!(Rcode::FORMERR.to_mnemonic_str(), Some("FORMERR"));
|
||||
assert_eq!(Rcode::SERVFAIL.to_mnemonic_str(), Some("SERVFAIL"));
|
||||
assert_eq!(Rcode::NXDOMAIN.to_mnemonic_str(), Some("NXDOMAIN"));
|
||||
assert_eq!(Rcode::NOTIMP.to_mnemonic_str(), Some("NOTIMP"));
|
||||
assert_eq!(Rcode::REFUSED.to_mnemonic_str(), Some("REFUSED"));
|
||||
assert_eq!(Rcode::YXDOMAIN.to_mnemonic_str(), Some("YXDOMAIN"));
|
||||
assert_eq!(Rcode::YXRRSET.to_mnemonic_str(), Some("YXRRSET"));
|
||||
assert_eq!(Rcode::NXRRSET.to_mnemonic_str(), Some("NXRRSET"));
|
||||
assert_eq!(Rcode::NOTAUTH.to_mnemonic_str(), Some("NOTAUTH"));
|
||||
assert_eq!(Rcode::NOTZONE.to_mnemonic_str(), Some("NOTZONE"));
|
||||
assert_eq!(Rcode(42).to_mnemonic_str(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn optrcode_fromstr() {
|
||||
assert_eq!(Ok(OptRcode::NOERROR), "NOERROR".parse());
|
||||
@@ -991,6 +1014,24 @@ mod test {
|
||||
assert!("#$%!@".parse::<Rcode>().is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn optrcode_tomnemonicstr() {
|
||||
assert_eq!(OptRcode::NOERROR.to_mnemonic_str(), Some("NOERROR"));
|
||||
assert_eq!(OptRcode::FORMERR.to_mnemonic_str(), Some("FORMERR"));
|
||||
assert_eq!(OptRcode::SERVFAIL.to_mnemonic_str(), Some("SERVFAIL"));
|
||||
assert_eq!(OptRcode::NXDOMAIN.to_mnemonic_str(), Some("NXDOMAIN"));
|
||||
assert_eq!(OptRcode::NOTIMP.to_mnemonic_str(), Some("NOTIMP"));
|
||||
assert_eq!(OptRcode::REFUSED.to_mnemonic_str(), Some("REFUSED"));
|
||||
assert_eq!(OptRcode::YXDOMAIN.to_mnemonic_str(), Some("YXDOMAIN"));
|
||||
assert_eq!(OptRcode::YXRRSET.to_mnemonic_str(), Some("YXRRSET"));
|
||||
assert_eq!(OptRcode::NXRRSET.to_mnemonic_str(), Some("NXRRSET"));
|
||||
assert_eq!(OptRcode::NOTAUTH.to_mnemonic_str(), Some("NOTAUTH"));
|
||||
assert_eq!(OptRcode::NOTZONE.to_mnemonic_str(), Some("NOTZONE"));
|
||||
assert_eq!(OptRcode::BADVERS.to_mnemonic_str(), Some("BADVERS"));
|
||||
assert_eq!(OptRcode::BADCOOKIE.to_mnemonic_str(), Some("BADCOOKIE"));
|
||||
assert_eq!(OptRcode(42).to_mnemonic_str(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn optrcode_isext() {
|
||||
assert!(!OptRcode::NOERROR.is_ext());
|
||||
|
||||
@@ -80,6 +80,12 @@ use octseq::parse::Parser;
|
||||
#[cfg_attr(not(feature = "zonefile"), doc = "zonefile")]
|
||||
/// module for that.
|
||||
///
|
||||
/// # Equality, Ordering, and Hashing
|
||||
///
|
||||
/// The implementations for the standard equality, ordering, and hashing
|
||||
/// traits ignore the TTL. That is, two records with the same owner, class,
|
||||
/// type, and record data are considered identical even if their TTLs differ.
|
||||
///
|
||||
/// [`new`]: #method.new
|
||||
/// [`Message`]: ../message/struct.Message.html
|
||||
/// [`MessageBuilder`]: ../message_builder/struct.MessageBuilder.html
|
||||
@@ -300,6 +306,9 @@ where
|
||||
}
|
||||
|
||||
//--- PartialEq and Eq
|
||||
//
|
||||
// These impls ignore the TTL. This may have been a poor choice, but we
|
||||
// keep it for now and reconsider with the re-write of base.
|
||||
|
||||
impl<N, NN, D, DD> PartialEq<Record<NN, DD>> for Record<N, D>
|
||||
where
|
||||
|
||||
@@ -26,7 +26,7 @@ use crate::new::zonefile::scanner::{Scan, ScanError, Scanner};
|
||||
|
||||
use super::{
|
||||
CanonicalName, Label, LabelBuf, LabelIter, LabelParseError,
|
||||
NameCompressor,
|
||||
NameCompressor, RevNameBuf,
|
||||
};
|
||||
|
||||
//----------- Name -----------------------------------------------------------
|
||||
@@ -103,6 +103,11 @@ impl Name {
|
||||
// SAFETY: A 'Name' always contains valid encoded labels.
|
||||
unsafe { LabelIter::new_unchecked(self.as_bytes()) }
|
||||
}
|
||||
|
||||
/// Covert &[`Name`] into [`RevNameBuf`]
|
||||
pub fn to_revname(&self) -> RevNameBuf {
|
||||
NameBuf::copy_from(self).into()
|
||||
}
|
||||
}
|
||||
|
||||
//--- Canonical operations
|
||||
@@ -995,10 +1000,25 @@ impl fmt::Display for NameParseError {
|
||||
}
|
||||
}
|
||||
|
||||
// -- Convert from old Name to new::base::NameBuf ----------------------------
|
||||
|
||||
/// Upgrade a [`crate::base::Name`] into a
|
||||
/// [`crate::new::base::name::NameBuf`].
|
||||
impl<Octs> From<&crate::base::Name<Octs>> for NameBuf
|
||||
where
|
||||
Octs: AsRef<[u8]> + ?Sized,
|
||||
{
|
||||
fn from(value: &crate::base::Name<Octs>) -> Self {
|
||||
NameBuf::parse_bytes(value.as_slice())
|
||||
.expect("Tried to upgrade invalid name")
|
||||
}
|
||||
}
|
||||
|
||||
//============ Unit tests ====================================================
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
#[cfg(feature = "zonefile")]
|
||||
#[test]
|
||||
fn scan() {
|
||||
@@ -1051,4 +1071,23 @@ mod test {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_upgrade_name_to_namebuf() {
|
||||
let old_name =
|
||||
crate::base::Name::from_slice(b"\x07example\x03com\x00")
|
||||
.expect("Invalid name");
|
||||
|
||||
let new_name: NameBuf = old_name.into();
|
||||
assert_eq!(old_name.as_slice(), new_name.as_bytes())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_revname() {
|
||||
let namebuf: NameBuf = "example.com".parse().unwrap();
|
||||
assert_eq!(
|
||||
namebuf.to_revname().as_bytes(),
|
||||
b"\x00\x03com\x07example",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,6 +115,11 @@ impl RevName {
|
||||
// SAFETY: A 'RevName' always contains valid encoded labels.
|
||||
unsafe { LabelIter::new_unchecked(self.as_bytes()) }
|
||||
}
|
||||
|
||||
/// Covert &[`RevName`] into [`NameBuf`]
|
||||
pub fn to_name(&self) -> NameBuf {
|
||||
RevNameBuf::copy_from(self).into()
|
||||
}
|
||||
}
|
||||
|
||||
//--- Building in DNS messages
|
||||
@@ -777,10 +782,30 @@ impl<'a> serde::Deserialize<'a> for std::boxed::Box<RevName> {
|
||||
}
|
||||
}
|
||||
|
||||
// -- Convert from old Name to new::base::RevNameBuf -------------------------
|
||||
|
||||
/// Upgrade a [`crate::base::Name`] into a
|
||||
/// [`crate::new::base::name::reversed::RevNameBuf`].
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// The [`crate::base::Name`] slice has to contain a valid domain.
|
||||
impl<Octs> From<&crate::base::Name<Octs>> for RevNameBuf
|
||||
where
|
||||
Octs: AsRef<[u8]> + ?Sized,
|
||||
{
|
||||
fn from(value: &crate::base::Name<Octs>) -> Self {
|
||||
RevNameBuf::parse_bytes(value.as_slice())
|
||||
.expect("Tried to upgrade invalid name")
|
||||
}
|
||||
}
|
||||
|
||||
//============ Unit tests ====================================================
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[cfg(feature = "zonefile")]
|
||||
#[test]
|
||||
fn scan() {
|
||||
@@ -830,4 +855,26 @@ mod test {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "alloc")]
|
||||
fn test_upgrade_revnamebuf() {
|
||||
let old_name =
|
||||
crate::base::Name::from_slice(b"\x07example\x03com\x00")
|
||||
.expect("Invalid name");
|
||||
|
||||
let new_name: RevNameBuf = old_name.into();
|
||||
let mut buf = alloc::vec![0; new_name.built_bytes_size()];
|
||||
new_name.build_bytes(&mut buf).unwrap();
|
||||
assert_eq!(old_name.as_slice(), buf);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_name() {
|
||||
let revnamebuf: RevNameBuf = "example.com".parse().unwrap();
|
||||
assert_eq!(
|
||||
revnamebuf.to_name().as_bytes(),
|
||||
b"\x07example\x03com\x00",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+124
-1
@@ -506,6 +506,53 @@ impl Scan<'_> for RType {
|
||||
Err(ScanError::Custom("unrecognized record type"))
|
||||
}
|
||||
}
|
||||
/// Format an [`RType`] in a human-readable way
|
||||
///
|
||||
/// Return the mnemonic of [`RType`]. If [`RType`] is unknown, then the
|
||||
/// returned string contains the type in the unknown format as defined in
|
||||
/// Section 5 in [RFC3597].
|
||||
///
|
||||
/// The mnemonics are consolidated by [IANA].
|
||||
///
|
||||
/// ```
|
||||
/// # use domain::new::base::RType;
|
||||
/// // Known RType with mnemonic
|
||||
/// assert_eq!("A", format!("{}", RType::A));
|
||||
/// // Unknown RType
|
||||
/// assert_eq!("TYPE265", format!("{}", RType::from(265)));
|
||||
/// ```
|
||||
///
|
||||
/// [RFC3597]: https://datatracker.ietf.org/doc/html/rfc3597#section-5
|
||||
/// [IANA]: https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml
|
||||
impl fmt::Display for RType {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str(match *self {
|
||||
Self::A => "A",
|
||||
Self::NS => "NS",
|
||||
Self::CNAME => "CNAME",
|
||||
Self::SOA => "SOA",
|
||||
Self::PTR => "PTR",
|
||||
Self::HINFO => "HINFO",
|
||||
Self::MX => "MX",
|
||||
Self::TXT => "TXT",
|
||||
Self::RP => "RP",
|
||||
Self::AAAA => "AAAA",
|
||||
Self::DNAME => "DNAME",
|
||||
Self::OPT => "OPT",
|
||||
Self::DS => "DS",
|
||||
Self::RRSIG => "RRSIG",
|
||||
Self::NSEC => "NSEC",
|
||||
Self::DNSKEY => "DNSKEY",
|
||||
Self::NSEC3 => "NSEC3",
|
||||
Self::NSEC3PARAM => "NSEC3PARAM",
|
||||
Self::CDS => "CDS",
|
||||
Self::CDNSKEY => "CDNSKEY",
|
||||
Self::ZONEMD => "ZONEMD",
|
||||
Self::TSIG => "TSIG",
|
||||
_ => return write!(f, "TYPE{}", self.code),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
//----------- RClass ---------------------------------------------------------
|
||||
|
||||
@@ -549,6 +596,22 @@ impl RClass {
|
||||
pub const CH: Self = Self::new(3);
|
||||
}
|
||||
|
||||
//--- Conversion to and from 'u16'
|
||||
|
||||
impl From<u16> for RClass {
|
||||
fn from(value: u16) -> Self {
|
||||
Self {
|
||||
code: U16::new(value),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RClass> for u16 {
|
||||
fn from(value: RClass) -> Self {
|
||||
value.code.get()
|
||||
}
|
||||
}
|
||||
|
||||
//--- Formatting
|
||||
|
||||
impl fmt::Debug for RClass {
|
||||
@@ -594,6 +657,34 @@ impl Scan<'_> for RClass {
|
||||
}
|
||||
}
|
||||
|
||||
/// Format an [`RClass`] in a human-readable way
|
||||
///
|
||||
/// Return the mnemonic of [`RClass`]. If [`RClass`] is unknown, then the
|
||||
/// returned string contains the class in the unknown format as defined in
|
||||
/// Section 5 in [RFC3597].
|
||||
///
|
||||
/// The mnemonics are consolidated by [IANA].
|
||||
///
|
||||
/// ```
|
||||
/// # use domain::new::base::RClass;
|
||||
/// // Known RClass with mnemonic
|
||||
/// assert_eq!("IN", format!("{}", RClass::IN));
|
||||
/// // Unknown RClass
|
||||
/// assert_eq!("CLASS42", format!("{}", RClass::from(42)));
|
||||
/// ```
|
||||
///
|
||||
/// [RFC3597]: https://datatracker.ietf.org/doc/html/rfc3597#section-5
|
||||
/// [IANA]: https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml
|
||||
impl fmt::Display for RClass {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str(match *self {
|
||||
Self::IN => "IN",
|
||||
Self::CH => "CH",
|
||||
_ => return write!(f, "CLASS{}", self.code),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
//----------- TTL ------------------------------------------------------------
|
||||
|
||||
/// How long a record can be cached.
|
||||
@@ -854,7 +945,7 @@ impl Clone for alloc::boxed::Box<UnparsedRecordData> {
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::{RClass, RType, Record, UnparsedRecordData, TTL};
|
||||
use super::{RClass, RType, Record, TTL, UnparsedRecordData};
|
||||
|
||||
use crate::new::base::{
|
||||
name::Name,
|
||||
@@ -958,4 +1049,36 @@ mod test {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rclass_from() {
|
||||
let rclass: RClass = 1.into();
|
||||
assert_eq!(rclass, RClass::IN);
|
||||
|
||||
let number: u16 = rclass.into();
|
||||
assert_eq!(number, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rtype_from() {
|
||||
let rtype: RType = 6.into();
|
||||
assert_eq!(rtype, RType::SOA);
|
||||
|
||||
let number: u16 = rtype.into();
|
||||
assert_eq!(number, 6);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rclass_display() {
|
||||
assert_eq!("IN", format!("{}", RClass::IN));
|
||||
assert_eq!("CH", format!("{}", RClass::CH));
|
||||
assert_eq!("CLASS42", format!("{}", RClass::from(42)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rtype_display() {
|
||||
assert_eq!("A", format!("{}", RType::A));
|
||||
assert_eq!("MX", format!("{}", RType::MX));
|
||||
assert_eq!("TYPE265", format!("{}", RType::from(265)));
|
||||
}
|
||||
}
|
||||
|
||||
+33
-2
@@ -13,7 +13,16 @@ use super::wire::U32;
|
||||
|
||||
//----------- Serial ---------------------------------------------------------
|
||||
|
||||
/// A serial number.
|
||||
/// Serial number arithmetic.
|
||||
///
|
||||
/// [`Serial`] implements the "Serial number arithmetic" defined in [RFC1982]
|
||||
/// with a `SERIAL_BITS` value of 32.
|
||||
///
|
||||
/// [`Serial`] should not be used interchangably or be confused with the SOA
|
||||
/// Serial Number, which is a [`Serial`] but not the sole user of "Serial
|
||||
/// number arithmetic".
|
||||
///
|
||||
/// [RFC1982]: https://datatracker.ietf.org/doc/html/rfc1982
|
||||
#[derive(
|
||||
Copy,
|
||||
Clone,
|
||||
@@ -30,11 +39,23 @@ use super::wire::U32;
|
||||
UnsizedCopy,
|
||||
)]
|
||||
#[repr(transparent)]
|
||||
pub struct Serial(U32);
|
||||
pub struct Serial(pub U32);
|
||||
|
||||
//--- Construction
|
||||
|
||||
impl Serial {
|
||||
/// Construct a new [`Serial`]
|
||||
#[must_use]
|
||||
pub const fn new(value: u32) -> Self {
|
||||
Serial(U32::new(value))
|
||||
}
|
||||
|
||||
/// Get [`u32`] value of [`Serial`]
|
||||
#[must_use]
|
||||
pub const fn get(&self) -> u32 {
|
||||
self.0.get()
|
||||
}
|
||||
|
||||
/// Measure the current time (in seconds) in serial number space.
|
||||
#[cfg(feature = "std")]
|
||||
pub fn unix_time() -> Self {
|
||||
@@ -56,9 +77,15 @@ impl Serial {
|
||||
/// instead of a [`u32`] because it is easier to understand and implement
|
||||
/// a non-negative check versus the upper range check.
|
||||
///
|
||||
/// Section 7 in [RFC1982] states, in particular for the SOA Serial
|
||||
/// Number, but for any Serial number using a "SERIAL_BITS" value of 32:
|
||||
/// "The maximum defined increment is 2147483647 (2^31 - 1)."
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the number is negative.
|
||||
///
|
||||
/// [RFC1982]: https://datatracker.ietf.org/doc/html/rfc1982#section-7
|
||||
pub fn inc(self, num: i32) -> Self {
|
||||
assert!(num >= 0, "Cannot subtract from a `Serial`");
|
||||
self.0.get().wrapping_add_signed(num).into()
|
||||
@@ -68,6 +95,10 @@ impl Serial {
|
||||
//--- Ordering
|
||||
|
||||
impl PartialOrd for Serial {
|
||||
/// The comparison of Serial Number values is defined in Section 3.2
|
||||
/// [RFC1982].
|
||||
///
|
||||
/// [RFC1982]: https://datatracker.ietf.org/doc/html/rfc1982#section-3.2
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
let (lhs, rhs) = (self.0.get(), other.0.get());
|
||||
|
||||
|
||||
@@ -52,8 +52,7 @@ macro_rules! define_int {
|
||||
}
|
||||
|
||||
/// Overwrite this value with an integer.
|
||||
// TODO: Make 'const' at MSRV 1.83.0.
|
||||
pub fn set(&mut self, value: $base) {
|
||||
pub const fn set(&mut self, value: $base) {
|
||||
*self = Self::new(value)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user