use super::cmp::CanonicalOrd;
use super::iana::{Class, Rtype};
use super::name;
use super::name::{ParsedName, ToName};
use super::wire::{Composer, ParseError};
use core::cmp::Ordering;
use core::str::FromStr;
use core::{fmt, hash};
use octseq::builder::ShortBuf;
use octseq::octets::{Octets, OctetsFrom};
use octseq::parse::Parser;
#[derive(Clone, Copy)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Question<N> {
qname: N,
qtype: Rtype,
qclass: Class,
}
impl<N> Question<N> {
pub fn new(qname: N, qtype: Rtype, qclass: Class) -> Self {
Question {
qname,
qtype,
qclass,
}
}
pub fn new_in(qname: N, qtype: Rtype) -> Self {
Question {
qname,
qtype,
qclass: Class::IN,
}
}
pub fn into_qname(self) -> N {
self.qname
}
}
impl<N: ToName> Question<N> {
pub fn qname(&self) -> &N {
&self.qname
}
pub fn qtype(&self) -> Rtype {
self.qtype
}
pub fn qclass(&self) -> Class {
self.qclass
}
}
impl<Octs> Question<ParsedName<Octs>> {
pub fn parse<'a, Src: Octets<Range<'a> = Octs> + ?Sized + 'a>(
parser: &mut Parser<'a, Src>,
) -> Result<Self, ParseError> {
Ok(Question::new(
ParsedName::parse(parser)?,
Rtype::parse(parser)?,
Class::parse(parser)?,
))
}
}
impl<N: ToName> Question<N> {
pub fn compose<Target: Composer + ?Sized>(
&self,
target: &mut Target,
) -> Result<(), Target::AppendError> {
target.append_compressed_name(&self.qname)?;
self.qtype.compose(target)?;
self.qclass.compose(target)
}
}
impl<N: ToName> From<(N, Rtype, Class)> for Question<N> {
fn from((name, rtype, class): (N, Rtype, Class)) -> Self {
Question::new(name, rtype, class)
}
}
impl<N: ToName> From<(N, Rtype)> for Question<N> {
fn from((name, rtype): (N, Rtype)) -> Self {
Question::new(name, rtype, Class::IN)
}
}
impl<N: FromStr<Err = name::FromStrError>> FromStr for Question<N> {
type Err = FromStrError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut s = s.split_whitespace();
let qname = match s.next() {
Some(qname) => qname,
None => return Err(PresentationErrorEnum::MissingQname.into()),
};
let qname = N::from_str(qname)?;
let class_or_qtype = match s.next() {
Some(value) => value,
None => {
return Err(PresentationErrorEnum::MissingClassAndQtype.into())
}
};
let res = match Class::from_str(class_or_qtype) {
Ok(class) => {
let qtype = match s.next() {
Some(qtype) => qtype,
None => {
return Err(PresentationErrorEnum::MissingQtype.into())
}
};
match Rtype::from_str(qtype) {
Ok(qtype) => Self::new(qname, qtype, class),
Err(_) => {
return Err(PresentationErrorEnum::BadQtype.into())
}
}
}
Err(_) => {
let qtype = match Rtype::from_str(class_or_qtype) {
Ok(qtype) => qtype,
Err(_) => {
return Err(PresentationErrorEnum::BadQtype.into())
}
};
let class = match s.next() {
Some(class) => class,
None => return Ok(Self::new(qname, qtype, Class::IN)),
};
match Class::from_str(class) {
Ok(class) => Self::new(qname, qtype, class),
Err(_) => {
return Err(PresentationErrorEnum::BadClass.into())
}
}
}
};
if s.next().is_some() {
return Err(PresentationErrorEnum::TrailingData.into());
}
Ok(res)
}
}
impl<Name, SrcName> OctetsFrom<Question<SrcName>> for Question<Name>
where
Name: OctetsFrom<SrcName>,
{
type Error = Name::Error;
fn try_octets_from(
source: Question<SrcName>,
) -> Result<Self, Self::Error> {
Ok(Question::new(
Name::try_octets_from(source.qname)?,
source.qtype,
source.qclass,
))
}
}
impl<N, NN> PartialEq<Question<NN>> for Question<N>
where
N: ToName,
NN: ToName,
{
fn eq(&self, other: &Question<NN>) -> bool {
self.qname.name_eq(&other.qname)
&& self.qtype == other.qtype
&& self.qclass == other.qclass
}
}
impl<N: ToName> Eq for Question<N> {}
impl<N, NN> PartialOrd<Question<NN>> for Question<N>
where
N: ToName,
NN: ToName,
{
fn partial_cmp(&self, other: &Question<NN>) -> Option<Ordering> {
match self.qname.name_cmp(&other.qname) {
Ordering::Equal => {}
other => return Some(other),
}
match self.qtype.partial_cmp(&other.qtype) {
Some(Ordering::Equal) => {}
other => return other,
}
self.qclass.partial_cmp(&other.qclass)
}
}
impl<N, NN> CanonicalOrd<Question<NN>> for Question<N>
where
N: ToName,
NN: ToName,
{
fn canonical_cmp(&self, other: &Question<NN>) -> Ordering {
match self.qname.lowercase_composed_cmp(&other.qname) {
Ordering::Equal => {}
other => return other,
}
match self.qtype.cmp(&other.qtype) {
Ordering::Equal => {}
other => return other,
}
self.qclass.cmp(&other.qclass)
}
}
impl<N: ToName> Ord for Question<N> {
fn cmp(&self, other: &Self) -> Ordering {
match self.qname.name_cmp(&other.qname) {
Ordering::Equal => {}
other => return other,
}
match self.qtype.cmp(&other.qtype) {
Ordering::Equal => {}
other => return other,
}
self.qclass.cmp(&other.qclass)
}
}
impl<N: hash::Hash> hash::Hash for Question<N> {
fn hash<H: hash::Hasher>(&self, state: &mut H) {
self.qname.hash(state);
self.qtype.hash(state);
self.qclass.hash(state);
}
}
impl<N: fmt::Display> fmt::Display for Question<N> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}.\t{}\t{}", self.qname, self.qtype, self.qclass)
}
}
impl<N: fmt::Debug> fmt::Debug for Question<N> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Question")
.field("qname", &self.qname)
.field("qtype", &self.qtype)
.field("qclass", &self.qclass)
.finish()
}
}
pub trait ComposeQuestion {
fn compose_question<Target: Composer + ?Sized>(
&self,
target: &mut Target,
) -> Result<(), Target::AppendError>;
}
impl<'a, Q: ComposeQuestion> ComposeQuestion for &'a Q {
fn compose_question<Target: Composer + ?Sized>(
&self,
target: &mut Target,
) -> Result<(), Target::AppendError> {
(*self).compose_question(target)
}
}
impl<Name: ToName> ComposeQuestion for Question<Name> {
fn compose_question<Target: Composer + ?Sized>(
&self,
target: &mut Target,
) -> Result<(), Target::AppendError> {
self.compose(target)
}
}
impl<Name: ToName> ComposeQuestion for (Name, Rtype, Class) {
fn compose_question<Target: Composer + ?Sized>(
&self,
target: &mut Target,
) -> Result<(), Target::AppendError> {
Question::new(&self.0, self.1, self.2).compose(target)
}
}
impl<Name: ToName> ComposeQuestion for (Name, Rtype) {
fn compose_question<Target: Composer + ?Sized>(
&self,
target: &mut Target,
) -> Result<(), Target::AppendError> {
Question::new(&self.0, self.1, Class::IN).compose(target)
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum FromStrError {
Presentation(PresentationError),
ShortBuf,
}
impl From<name::FromStrError> for FromStrError {
fn from(err: name::FromStrError) -> FromStrError {
match err {
name::FromStrError::Presentation(err) => {
Self::Presentation(err.into())
}
name::FromStrError::ShortBuf => Self::ShortBuf,
}
}
}
impl From<PresentationErrorEnum> for FromStrError {
fn from(err: PresentationErrorEnum) -> Self {
Self::Presentation(err.into())
}
}
impl fmt::Display for FromStrError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
FromStrError::Presentation(err) => err.fmt(f),
FromStrError::ShortBuf => ShortBuf.fmt(f),
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for FromStrError {}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct PresentationError(PresentationErrorEnum);
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum PresentationErrorEnum {
BadName(name::PresentationError),
MissingQname,
MissingClassAndQtype,
MissingQtype,
BadClass,
BadQtype,
TrailingData,
}
impl From<PresentationErrorEnum> for PresentationError {
fn from(err: PresentationErrorEnum) -> Self {
Self(err)
}
}
impl From<name::PresentationError> for PresentationError {
fn from(err: name::PresentationError) -> Self {
Self(PresentationErrorEnum::BadName(err))
}
}
impl fmt::Display for PresentationError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.0 {
PresentationErrorEnum::BadName(err) => err.fmt(f),
PresentationErrorEnum::MissingQname => {
f.write_str("missing qname")
}
PresentationErrorEnum::MissingClassAndQtype => {
f.write_str("missing class and qtype")
}
PresentationErrorEnum::MissingQtype => {
f.write_str("missing qtype")
}
PresentationErrorEnum::BadClass => f.write_str("invalid class"),
PresentationErrorEnum::BadQtype => f.write_str("invalid qtype"),
PresentationErrorEnum::TrailingData => {
f.write_str("trailing data")
}
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for PresentationError {}