use super::cmp::CanonicalOrd;
use super::iana::{Class, Rtype};
use super::name::{ParsedDname, ToDname};
use super::octets::{
Compose, OctetsBuilder, OctetsFrom, OctetsRef, Parse, ParseError, Parser,
ShortBuf,
};
use core::cmp::Ordering;
use core::{fmt, hash};
#[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: ToDname> Question<N> {
pub fn qname(&self) -> &N {
&self.qname
}
pub fn qtype(&self) -> Rtype {
self.qtype
}
pub fn qclass(&self) -> Class {
self.qclass
}
}
impl<N: ToDname> From<(N, Rtype, Class)> for Question<N> {
fn from((name, rtype, class): (N, Rtype, Class)) -> Self {
Question::new(name, rtype, class)
}
}
impl<N: ToDname> From<(N, Rtype)> for Question<N> {
fn from((name, rtype): (N, Rtype)) -> Self {
Question::new(name, rtype, Class::In)
}
}
impl<Name, SrcName> OctetsFrom<Question<SrcName>> for Question<Name>
where
Name: OctetsFrom<SrcName>,
{
fn octets_from(source: Question<SrcName>) -> Result<Self, ShortBuf> {
Ok(Question::new(
Name::octets_from(source.qname)?,
source.qtype,
source.qclass,
))
}
}
impl<N, NN> PartialEq<Question<NN>> for Question<N>
where
N: ToDname,
NN: ToDname,
{
fn eq(&self, other: &Question<NN>) -> bool {
self.qname.name_eq(&other.qname)
&& self.qtype == other.qtype
&& self.qclass == other.qclass
}
}
impl<N: ToDname> Eq for Question<N> {}
impl<N, NN> PartialOrd<Question<NN>> for Question<N>
where
N: ToDname,
NN: ToDname,
{
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: ToDname,
NN: ToDname,
{
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: ToDname> 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<Ref: OctetsRef> Parse<Ref> for Question<ParsedDname<Ref>> {
fn parse(parser: &mut Parser<Ref>) -> Result<Self, ParseError> {
Ok(Question::new(
ParsedDname::parse(parser)?,
Rtype::parse(parser)?,
Class::parse(parser)?,
))
}
fn skip(parser: &mut Parser<Ref>) -> Result<(), ParseError> {
ParsedDname::skip(parser)?;
Rtype::skip(parser)?;
Class::skip(parser)?;
Ok(())
}
}
impl<N: ToDname> Compose for Question<N> {
fn compose<T: OctetsBuilder + AsMut<[u8]>>(
&self,
target: &mut T,
) -> Result<(), ShortBuf> {
target.append_all(|target| {
target.append_compressed_dname(&self.qname)?;
self.qtype.compose(target)?;
self.qclass.compose(target)
})
}
fn compose_canonical<T: OctetsBuilder + AsMut<[u8]>>(
&self,
target: &mut T,
) -> Result<(), ShortBuf> {
target.append_all(|target| {
self.qname.compose_canonical(target)?;
self.qtype.compose_canonical(target)?;
self.qclass.compose_canonical(target)
})
}
}
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 AsQuestion {
type Name: ToDname;
fn qname(&self) -> &Self::Name;
fn qtype(&self) -> Rtype;
fn qclass(&self) -> Class;
fn compose_question<T: OctetsBuilder + AsMut<[u8]>>(
&self,
target: &mut T,
) -> Result<(), ShortBuf>
where
Self::Name: Compose,
{
target.append_all(|target| {
target.append_compressed_dname(self.qname())?;
self.qtype().compose(target)?;
self.qclass().compose(target)
})
}
fn compose_question_canonical<T: OctetsBuilder + AsMut<[u8]>>(
&self,
target: &mut T,
) -> Result<(), ShortBuf>
where
Self::Name: Compose,
{
target.append_all(|target| {
self.qname().compose_canonical(target)?;
self.qtype().compose_canonical(target)?;
self.qclass().compose_canonical(target)
})
}
}
impl<'a, T: AsQuestion> AsQuestion for &'a T {
type Name = T::Name;
fn qname(&self) -> &Self::Name {
(*self).qname()
}
fn qtype(&self) -> Rtype {
(*self).qtype()
}
fn qclass(&self) -> Class {
(*self).qclass()
}
}
impl<Name: ToDname> AsQuestion for Question<Name> {
type Name = Name;
fn qname(&self) -> &Self::Name {
Self::qname(self)
}
fn qtype(&self) -> Rtype {
Self::qtype(self)
}
fn qclass(&self) -> Class {
Self::qclass(self)
}
}
impl<Name: ToDname> AsQuestion for (Name, Rtype, Class) {
type Name = Name;
fn qname(&self) -> &Self::Name {
&self.0
}
fn qtype(&self) -> Rtype {
self.1
}
fn qclass(&self) -> Class {
self.2
}
}
impl<Name: ToDname> AsQuestion for (Name, Rtype) {
type Name = Name;
fn qname(&self) -> &Self::Name {
&self.0
}
fn qtype(&self) -> Rtype {
self.1
}
fn qclass(&self) -> Class {
Class::In
}
}