use super::{HEADER_SIZE, Header, QuestionRef, Ref};
use crate::error::ParseError;
#[derive(Debug, Copy, Clone)]
pub struct MessageReader<'a> {
message: &'a [u8],
header: Header,
}
impl<'a> MessageReader<'a> {
pub fn try_parse(message: &'a [u8]) -> Result<Self, ParseError> {
let (header, _rest) = Header::try_parse(message)?;
Ok(Self { message, header })
}
#[inline(always)]
pub const fn header(&self) -> &Header {
&self.header
}
pub fn questions(&self) -> Questions<'a> {
Questions {
message: self.message,
cursor: HEADER_SIZE,
remaining: self.header.question_count(),
}
}
pub fn answers(&self) -> Records<'a> {
Records::new_after_questions(self.message, &self.header, self.header.answer_count())
}
pub fn authority(&self) -> Records<'a> {
Records::new_after_answers(self.message, &self.header, self.header.authority_count())
}
pub fn additional(&self) -> Records<'a> {
Records::new_after_authority(self.message, &self.header, self.header.additional_count())
}
}
pub struct Questions<'a> {
message: &'a [u8],
cursor: usize,
remaining: u16,
}
impl<'a> Iterator for Questions<'a> {
type Item = Result<QuestionRef<'a>, ParseError>;
fn next(&mut self) -> Option<Self::Item> {
if self.remaining == 0 {
return None;
}
match QuestionRef::try_parse(self.message, self.cursor) {
Ok((q, next)) => {
self.cursor = next;
self.remaining = self.remaining.saturating_sub(1);
Some(Ok(q))
}
Err(e) => {
self.remaining = 0;
Some(Err(e))
}
}
}
}
pub struct Records<'a> {
message: &'a [u8],
cursor: usize,
remaining: u16,
}
fn skip_questions(message: &[u8], header: &Header) -> Option<usize> {
let mut cursor = HEADER_SIZE;
let mut remaining = header.question_count();
while remaining > 0 {
let (_, next) = QuestionRef::try_parse(message, cursor).ok()?;
cursor = next;
remaining = remaining.saturating_sub(1);
}
Some(cursor)
}
fn skip_records(message: &[u8], mut cursor: usize, mut count: u16) -> Option<usize> {
while count > 0 {
let (_, next) = Ref::try_parse(message, cursor).ok()?;
cursor = next;
count = count.saturating_sub(1);
}
Some(cursor)
}
impl<'a> Records<'a> {
fn new_after_questions(message: &'a [u8], header: &Header, count: u16) -> Self {
Self::at_or_empty(message, count, skip_questions(message, header))
}
fn new_after_answers(message: &'a [u8], header: &Header, count: u16) -> Self {
let cursor =
skip_questions(message, header).and_then(|c| skip_records(message, c, header.answer_count()));
Self::at_or_empty(message, count, cursor)
}
fn new_after_authority(message: &'a [u8], header: &Header, count: u16) -> Self {
let cursor = skip_questions(message, header)
.and_then(|c| skip_records(message, c, header.answer_count()))
.and_then(|c| skip_records(message, c, header.authority_count()));
Self::at_or_empty(message, count, cursor)
}
fn at_or_empty(message: &'a [u8], count: u16, cursor: Option<usize>) -> Self {
match cursor {
Some(cursor) => Self {
message,
cursor,
remaining: count,
},
None => Self {
message,
cursor: HEADER_SIZE,
remaining: 0,
},
}
}
}
impl<'a> Iterator for Records<'a> {
type Item = Result<Ref<'a>, ParseError>;
fn next(&mut self) -> Option<Self::Item> {
if self.remaining == 0 {
return None;
}
match Ref::try_parse(self.message, self.cursor) {
Ok((r, next)) => {
self.cursor = next;
self.remaining = self.remaining.saturating_sub(1);
Some(Ok(r))
}
Err(e) => {
self.remaining = 0;
Some(Err(e))
}
}
}
}
#[cfg(all(test, any(feature = "alloc", feature = "std")))]
#[allow(warnings)]
mod tests;