nonymous 0.7.0

DNS protocol and algorithm library
Documentation
//! Standalone owned name types that wrap a [`Name`](crate::view::Name) view.
#![cfg_attr(feature = "alloc", doc = "- [`NameBuf`]")]
#![cfg_attr(not(feature = "alloc"), doc = "- `NameBuf`")]
//!   (requires feature `alloc`) is backed by a
#![cfg_attr(feature = "alloc", doc = "[`Vec`](alloc::vec::Vec)`<u8>`")]
#![cfg_attr(not(feature = "alloc"), doc = "`Vec<u8>`")]
//! - [`NameBufN`]`<N>` is backed by an [`ArrayVec`](arrayvec::ArrayVec)`<u8, N>`
//! - [`NameBufMax`] is backed by an [`ArrayVec`](arrayvec::ArrayVec)`<u8, 255>`

use core::hash::Hash;

use arrayvec::CapacityError;
use paste::paste;

use crate::{
    emit::name::parse_dotted_name,
    fmt::{Format, Plain},
    view::{BorrowedView, Labels, Name as NameView, View},
};

error!(OwnedNameError, View, Emit);
#[derive(Debug, displaydoc::Display)]
#[prefix_enum_doc_attributes]
/// failed to create owned name
pub enum OwnedNameError {
    /// failed to view name: {0}
    View(crate::view::NameError),
    /// failed to emit name: {0}
    Emit(crate::emit::name::NameError),
    /// failed to extend name: {0}
    Extend(CapacityError),
}

impl From<crate::view::NameError> for OwnedNameError {
    fn from(error: crate::view::NameError) -> Self {
        Self::View(error)
    }
}

impl From<crate::emit::name::NameError> for OwnedNameError {
    fn from(error: crate::emit::name::NameError) -> Self {
        Self::Emit(error)
    }
}

impl From<CapacityError> for OwnedNameError {
    fn from(error: CapacityError) -> Self {
        Self::Extend(error)
    }
}

#[test]
#[rustfmt::skip]
fn test() -> Result<(), OwnedNameError> {
    #[cfg(feature = "alloc")]
    assert_eq!(NameBuf::from_wire(b"\x05daria\x03daz\x03cat\0")?, "daria.daz.cat.");
    #[cfg(feature = "alloc")]
    assert_eq!(NameBuf::from_dotted("daria.daz.cat.")?, "daria.daz.cat.");
    assert_eq!(NameBufMax::from_wire(b"\x05daria\x03daz\x03cat\0")?, "daria.daz.cat.");
    assert_eq!(NameBufMax::from_dotted("daria.daz.cat.")?, "daria.daz.cat.");

    Ok(())
}

/// ```
/// nonymous::name::NameBufN::<255>::from_wire("\0");
/// nonymous::name::NameBufN::<255>::from_dotted(".");
///
/// use nonymous::view::View;
/// let (name, _) = nonymous::view::Name::view(b"\0", ..)?;
/// nonymous::name::NameBufN::<255>::from(name);
/// # Ok::<_, nonymous::view::NameError>(())
/// ```
/// ```compile_fail
/// nonymous::name::NameBufN::<256>::from_wire("\0");
/// ```
/// ```compile_fail
/// nonymous::name::NameBufN::<256>::from_dotted(".");
/// ```
/// ```compile_fail
/// use nonymous::view::View;
/// let (name, _) = nonymous::view::Name::view(b"\0", ..)?;
/// nonymous::name::NameBufN::<256>::from(name);
/// # Ok::<_, nonymous::view::NameError>(())
/// ```
#[allow(dead_code)]
fn test_static_assert() {}

macro_rules! define_owned_name_type {
    {
        $(#[$meta:meta])*
        $name:ident$([const $N:ident: usize])?($($buffer:tt)+) {
            fn try_from_slice($source:ident) {
                $try_from_slice:expr
            }
            fn extend_from_slice($result:ident, $label:ident) {
                $extend_from_slice:expr
            }
        }
    } => {paste!{
        $(#[$meta])*
        #[derive(Clone)]
        pub struct $name$(<const $N: usize = 255>)?($($buffer)+);

        impl$(<const $N: usize>)? $name$(<$N>)? {
            const fn static_assert() {
                $(
                    let _ = AssertValidN::<$N>::CHECK;
                    struct AssertValidN<const $N: usize>;
                    impl<const $N: usize> AssertValidN<$N> {
                        const CHECK: () = if $N > 255 {
                            panic!("`N` must not be greater than 255")
                        };
                    }
                )?
            }

            pub fn from_wire($source: impl AsRef<[u8]>) -> Result<Self, OwnedNameError> {
                Self::static_assert();
                let $source = $source.as_ref();
                let $source = $try_from_slice?;
                let _validation = NameView::view(&$source, ..)?;
                Ok(Self($source))
            }

            pub fn from_dotted(input: &str) -> Result<Self, OwnedNameError> {
                Self::static_assert();
                let mut $result: $($buffer)+ = Default::default();
                let mut input = input.as_bytes();
                let mut $label = [0u8; 63];
                while let Some(($label, rest)) = parse_dotted_name(&mut $label, input)? {
                    $result.push($label.len() as u8);
                    $extend_from_slice?;
                    input = rest;
                }
                $result.push(0);
                Self::from_wire($result)
            }

            pub fn view<'this>(&'this self) -> NameView<'this> {
                NameView::new_unchecked(&self.0, 0..self.0.len())
            }

            pub fn as_bytes(&self) -> &[u8] {
                self.view().as_bytes()
            }

            pub fn labels_with_null<'this>(&'this self) -> Labels<'this> {
                self.view().labels_with_null()
            }

            pub fn labels_not_null<'this>(&'this self) -> Labels<'this> {
                self.view().labels_not_null()
            }

            pub fn is_subdomain_of(&self, other: &NameView<'_>) -> bool {
                self.view().is_subdomain_of(other)
            }

            pub fn subdomain_distance(&self, other: &NameView<'_>) -> Option<usize> {
                self.view().subdomain_distance(other)
            }

            pub fn parent(&self) -> Option<NameView<'_>> {
                self.view().parent()
            }

            pub fn truncate(&self, labels_not_null: usize) -> NameView<'_> {
                self.view().truncate(labels_not_null)
            }
        }

        impl$(<const $N: usize>)? From<NameView<'_>> for $name$(<$N>)? {
            fn from(view: NameView<'_>) -> Self {
                Self::static_assert();
                let mut $result: $($buffer)+ = Default::default();
                for $label in view.labels_not_null().flat_map(|$label| $label.value()) {
                    $result.push($label.len() as u8);
                    $extend_from_slice.expect("guaranteed by NameView");
                }
                $result.push(0);
                Self::from_wire($result).expect("guaranteed by this function")
            }
        }

        impl$(<const $N: usize>)? core::fmt::Debug for $name$(<$N>)? {
            fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
                Plain(self).fmt(f)
            }
        }

        impl$(<const $N: usize>)? Format for $name$(<$N>)? {
            fn fmt(&self, w: &mut crate::fmt::Wrapper) -> core::fmt::Result {
                self.view().fmt(w)
            }
        }

        impl$(<const $N: usize>)? Hash for $name$(<$N>)? {
            fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
                self.view().hash(state)
            }
        }

        impl$(<const $N: usize>)? Ord for $name$(<$N>)? {
            fn cmp(&self, other: &Self) -> core::cmp::Ordering {
                self.labels_with_null().cmp(other.labels_with_null())
            }
        }

        impl$(<const $N: usize>)? PartialOrd for $name$(<$N>)? {
            fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
                Some(self.cmp(other))
            }
        }

        impl$(<const $N: usize>)? Eq for $name$(<$N>)? {}

        impl$(<const $N: usize>)? PartialEq for $name$(<$N>)? {
            fn eq(&self, other: &Self) -> bool {
                self.view().eq(&other.view())
            }
        }

        impl$(<const $N: usize>)? PartialEq<crate::view::Name<'_>> for $name$(<$N>)? {
            fn eq(&self, other: &crate::view::Name) -> bool {
                self.view().eq(other)
            }
        }

        impl$(<const $N: usize>)? PartialEq<[u8]> for $name$(<$N>)? {
            fn eq(&self, other: &[u8]) -> bool {
                self.view().eq(other)
            }
        }

        impl$(<const $N: usize>)? PartialEq<&[u8]> for $name$(<$N>)? {
            fn eq(&self, other: &&[u8]) -> bool {
                self.view().eq(other)
            }
        }

        impl<$(const $N: usize,)? const __N: usize> PartialEq<[u8; __N]> for $name$(<$N>)? {
            fn eq(&self, other: &[u8; __N]) -> bool {
                self.view().eq(other)
            }
        }

        impl<$(const $N: usize,)? const __N: usize> PartialEq<&[u8; __N]> for $name$(<$N>)? {
            fn eq(&self, other: &&[u8; __N]) -> bool {
                self.view().eq(other)
            }
        }

        impl$(<const $N: usize>)? PartialEq<str> for $name$(<$N>)? {
            fn eq(&self, other: &str) -> bool {
                self.view().eq(other)
            }
        }

        impl$(<const $N: usize>)? PartialEq<&str> for $name$(<$N>)? {
            fn eq(&self, other: &&str) -> bool {
                self.view().eq(other)
            }
        }

        #[cfg(feature = "alloc")]
        impl$(<const $N: usize>)? PartialEq<alloc::string::String> for $name$(<$N>)? {
            fn eq(&self, other: &alloc::string::String) -> bool {
                self.view().eq(other)
            }
        }

        #[cfg(feature = "alloc")]
        impl$(<const $N: usize>)? PartialEq<&alloc::string::String> for $name$(<$N>)? {
            fn eq(&self, other: &&alloc::string::String) -> bool {
                self.view().eq(other)
            }
        }
    }};
}

#[cfg(feature = "alloc")]
define_owned_name_type!(
    /// Owned name of any length up to the maximum allowed by DNS (255 octets).
    NameBuf(alloc::vec::Vec<u8>) {
        fn try_from_slice(source) {
            Ok::<_, OwnedNameError>(alloc::vec::Vec::from(source))
        }
        fn extend_from_slice(result, label) {
            Ok::<_, OwnedNameError>(result.extend_from_slice(label))
        }
    }
);

/// Owned name of any length up to the maximum allowed by DNS (255 octets).
pub type NameBufMax = NameBufN<255>;
define_owned_name_type!(
    /// Owned name of any length up to `<const N: usize>` octets.
    NameBufN[const N: usize](arrayvec::ArrayVec<u8, N>) {
        fn try_from_slice(source) {
            arrayvec::ArrayVec::try_from(source)
        }
        fn extend_from_slice(result, label) {
            result.try_extend_from_slice(label)
        }
    }
);