nonymous 0.7.0

DNS protocol and algorithm library
Documentation
//! Primitive protocol types: [`Type`], [`Class`], [`Opcode`], [`Rcode`], [`Serial`], [`Ttl`].

pub use crate::core::error::{
    ClassFromStrError, OpcodeRangeError, RcodeRangeError, SerialAdditionError, TtlFromStrError,
    TypeFromStrError,
};

pub(crate) mod r#const;

use self::r#const::{CLASS, RCODE, TYPE};

use core::{fmt::Debug, str::FromStr};

pub mod error {
    error!(OpcodeRangeError);
    #[derive(Debug, displaydoc::Display)]
    /// OPCODE out of range: {0}
    pub struct OpcodeRangeError(pub(crate) u8);

    error!(RcodeRangeError);
    #[derive(Debug, displaydoc::Display)]
    /// RCODE out of range
    pub enum RcodeRangeError {
        /// extended RCODE exceeds 12 bits: {0}
        Extended(u16),

        /// non-extended RCODE exceeds 4 bits: {0}
        Basic(u8),
    }

    error!(TypeFromStrError);
    #[derive(Debug, displaydoc::Display)]
    /// unrecognised TYPE name
    pub struct TypeFromStrError;

    error!(ClassFromStrError);
    #[derive(Debug, displaydoc::Display)]
    /// unrecognised CLASS name
    pub struct ClassFromStrError;

    error!(TtlFromStrError);
    #[derive(Debug, displaydoc::Display)]
    /// failed to parse TTL value
    pub struct TtlFromStrError;

    error!(SerialAdditionError);
    #[derive(Debug, displaydoc::Display, PartialEq)]
    /// serial number addition overflow
    pub struct SerialAdditionError;
}

#[derive(Debug, Clone, PartialEq)]
pub struct Opcode(u8);

#[derive(Debug, Clone, PartialEq)]
pub struct Rcode(u16);

#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Type(u16);

#[derive(Debug, Clone, PartialEq)]
pub struct Class(u16);

#[derive(Debug, Clone)]
pub struct Ttl(u32);

#[derive(Debug, Clone, PartialEq)]
pub struct Serial(u32);

impl Opcode {
    pub const fn new(value: u8) -> Result<Self, OpcodeRangeError> {
        if value > 0xF {
            return Err(OpcodeRangeError(value));
        }

        Ok(Self(value))
    }

    pub const fn value(&self) -> u8 {
        self.0
    }
}

impl Rcode {
    pub const fn new(value: u16) -> Result<Self, RcodeRangeError> {
        if value > 0xFFF {
            return Err(RcodeRangeError::Extended(value));
        }

        Ok(Self(value))
    }

    pub const fn from_basic_extended(
        basic_part: u8,
        extended_part: u8,
    ) -> Result<Self, RcodeRangeError> {
        if basic_part > 0xF {
            return Err(RcodeRangeError::Basic(basic_part));
        }

        // TODO use u16::from() once it becomes const
        let basic_part = basic_part as u16;
        let extended_part = extended_part as u16;

        Self::new(extended_part << 4 | basic_part)
    }

    pub const fn name_opt(&self) -> Option<&'static str> {
        RCODE.to_name_opt(self.value())
    }

    pub const fn name_tsig(&self) -> Option<&'static str> {
        RCODE.to_name_tsig(self.value())
    }

    pub const fn value(&self) -> u16 {
        self.0
    }

    pub const fn basic_part(&self) -> u8 {
        // FIXME rust#87852
        // (self.0 & 0xF).try_into().expect("guaranteed by Self::of")
        (self.0 & 0xF) as u8
    }

    pub const fn extended_part(&self) -> u8 {
        // FIXME rust#87852
        // (self.0 >> 4).try_into().expect("guaranteed by Self::of")
        (self.0 >> 4) as u8
    }
}

impl Type {
    pub const fn new(value: u16) -> Self {
        Self(value)
    }

    pub const fn value(&self) -> u16 {
        self.0
    }
}

impl Class {
    pub const fn new(value: u16) -> Self {
        Self(value)
    }

    pub const fn value(&self) -> u16 {
        self.0
    }
}

impl Ttl {
    pub const fn new(value: u32) -> Self {
        Self(value)
    }

    pub const fn value(&self) -> u32 {
        self.0
    }

    pub(crate) const fn edns_version(&self) -> u8 {
        (self.value() >> 16 & 0xFF) as _
    }
}

impl Serial {
    pub const fn new(value: u32) -> Self {
        Self(value)
    }

    pub const fn value(&self) -> u32 {
        self.0
    }
}

impl FromStr for Type {
    type Err = TypeFromStrError;

    fn from_str(source: &str) -> Result<Self, Self::Err> {
        if let Some(value) = TYPE.to_number(&source) {
            return Ok(Type(value));
        }

        // RFC 3597 § 5
        if source.starts_with("TYPE") {
            return Ok(Type(
                u16::from_str_radix(&source[4..], 10).map_err(|_| TypeFromStrError)?,
            ));
        }

        Err(TypeFromStrError)
    }
}

impl FromStr for Class {
    type Err = ClassFromStrError;

    fn from_str(source: &str) -> Result<Self, Self::Err> {
        if let Some(value) = CLASS.to_number(&source) {
            return Ok(Class(value));
        }

        // RFC 3597 § 5
        if source.starts_with("CLASS") {
            return Ok(Class(
                u16::from_str_radix(&source[5..], 10).map_err(|_| ClassFromStrError)?,
            ));
        }

        Err(ClassFromStrError)
    }
}

#[cfg(all(test, feature = "bench"))]
mod bench {
    mod parse {
        extern crate test;

        use test::Bencher;

        use crate::core::{Class, Type};

        #[bench]
        fn r#type(bencher: &mut Bencher) {
            bencher.iter(|| "cname".parse::<Type>());
        }

        #[bench]
        fn class(bencher: &mut Bencher) {
            bencher.iter(|| "hs".parse::<Class>());
        }
    }
}