nonymous 0.7.0

DNS protocol and algorithm library
Documentation
use core::marker::PhantomData;
use core::ops::Range;

use byteorder::{ByteOrder, NetworkEndian};

use crate::core::{Class, Ttl, Type};
use crate::emit::message::{MessageBuilder, RecordStep};
use crate::emit::name::{NameBuilder, NameError};
use crate::emit::{Buffer, Builder, ChildBuilder, GrowError, PushBuilder, Sink};

error!(RecordError, Grow, Name);
/// failed to emit record
#[derive(Debug, displaydoc::Display)]
#[prefix_enum_doc_attributes]
pub enum RecordError {
    /// not enough space
    Grow(GrowError),

    /// error while emitting name
    Name(NameError),

    /// RDLENGTH would overflow
    RdataTooLong,
}

/// # Down transitions
/// * [`name`][`RecordBuilder::name`]
/// # Step transitions
/// (see [`NameBuilder`])
/// # Up transitions
/// * [`finish`][`RecordBuilder::finish`]
#[must_use]
pub struct RecordBuilder<'b, P, Q> {
    buffer: PhantomData<&'b mut dyn Buffer>,
    parent: P,
    step: Q,
}

pub struct RecordName;
pub struct RecordData(pub Range<usize>);

impl<'b, P: Builder<'b>, Q> ChildBuilder<'b, P> for RecordBuilder<'b, P, Q> {
    fn parent(&mut self) -> &mut P {
        &mut self.parent
    }
}

impl<'b, P: Builder<'b>> PushBuilder<'b, P> for RecordBuilder<'b, P, RecordName> {
    type Error = RecordError;
    fn push(parent: P) -> Result<Self, RecordError> {
        Ok(Self {
            buffer: PhantomData,
            parent,
            step: RecordName,
        })
    }
}

impl<'b, P: Builder<'b>> TryFrom<RecordBuilder<'b, P, RecordName>>
    for RecordBuilder<'b, P, RecordData>
{
    type Error = RecordError;

    fn try_from(mut builder: RecordBuilder<'b, P, RecordName>) -> Result<Self, Self::Error> {
        let step = RecordData(builder.sink().grow_range(10).map_err(RecordError::Grow)?);
        let RecordBuilder {
            buffer,
            parent,
            step: _,
        } = builder;

        Ok(RecordBuilder {
            buffer,
            parent,
            step,
        })
    }
}

builder! {
    <'b, P> RecordBuilder {
        Builder [Q];
        @ <P> RecordData:
            pub fn r#type(mut self, r#type: Type) -> Self = {
                self.set_u16(0, r#type.value())
            }

            pub fn class(mut self, class: Class) -> Self = {
                self.set_u16(2, class.value())
            }

            pub fn ttl(mut self, ttl: Ttl) -> Self = {
                self.set_u32(4, ttl.value())
            }

            pub fn push_rdata(mut self, rdata: &[u8]) -> Result<Self, RecordError> = {
                let offset = self.step.0.start + 8;
                let delta = rdata
                    .len()
                    .try_into()
                    .map_err(|_| RecordError::RdataTooLong)?;
                let rdlength = NetworkEndian::read_u16(&self.sink().inner()[offset..]);
                let rdlength = rdlength
                    .checked_add(delta)
                    .ok_or_else(|| RecordError::RdataTooLong)?;
                NetworkEndian::write_u16(&mut self.sink().inner_mut()[offset..], rdlength);

                self.sink()
                    .grow_mut(rdata.len())
                    .map_err(RecordError::Grow)?
                    .copy_from_slice(rdata);

                Ok(self)
            }

            pub fn build_rdata[T: PushBuilder<'b, Self>](mut self) -> Result<T, T::Error> = {
                T::push(self)
            }

            fn set_u16(mut self, offset: usize, value: u16) -> Self = {
                let offset = self.step.0.start + offset;
                NetworkEndian::write_u16(&mut self.sink().inner_mut()[offset..], value);

                self
            }

            fn set_u32(mut self, offset: usize, value: u32) -> Self = {
                let offset = self.step.0.start + offset;
                NetworkEndian::write_u32(&mut self.sink().inner_mut()[offset..], value);

                self
            }
    }
}

impl<'b, P: Builder<'b>> RecordBuilder<'b, P, RecordData> {
    pub(in crate::emit) fn name_len(&self) -> usize {
        self.step.0.start
    }
}

/// <h2>Down transitions</h2>
impl<__> RecordBuilder<'_, __, __> {}
builder! {
    <'b, P> RecordBuilder {
        @ <P> RecordName:
            /// Start building the record’s NAME.
            pub fn name(mut self) = [push NameBuilder | RecordError::Name] { self }
    }
}

// Helpers for step transitions
builder! {
    <'b, P> RecordBuilder {
        @ <MessageBuilder<'b, P, Q>> RecordName [Q: RecordStep]:
            pub(in crate::emit) fn try_into_rdata(mut self) = [try_into RecordData | RecordError] { self }
    }
}

/// <h2>Up transitions</h2>
impl<__> RecordBuilder<'_, __, __> {}
builder! {
    <'b, P> RecordBuilder {
        @ <MessageBuilder<'b, P, Q>> RecordData [Q: RecordStep]:
            /// Finish building the record and return to the message’s last section.
            pub fn finish(mut self) -> Result<MessageBuilder<'b, P, Q>, RecordError> = {
                let rdlength = self.sink().inner().len() - self.step.0.end;
                let rdlength = rdlength.try_into().map_err(|_| RecordError::RdataTooLong)?;
                self = self.set_u16(8, rdlength);

                Ok(self.parent)
            }
    }
}