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);
#[derive(Debug, displaydoc::Display)]
#[prefix_enum_doc_attributes]
pub enum RecordError {
Grow(GrowError),
Name(NameError),
RdataTooLong,
}
#[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
}
}
impl<__> RecordBuilder<'_, __, __> {}
builder! {
<'b, P> RecordBuilder {
@ <P> RecordName:
pub fn name(mut self) = [push NameBuilder | RecordError::Name] { self }
}
}
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 }
}
}
impl<__> RecordBuilder<'_, __, __> {}
builder! {
<'b, P> RecordBuilder {
@ <MessageBuilder<'b, P, Q>> RecordData [Q: RecordStep]:
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)
}
}
}