1use super::cmp::CanonicalOrd;
8use super::iana::{Class, Rtype};
9use super::name::{ParsedDname, ToDname};
10use super::wire::{Composer, ParseError};
11use core::cmp::Ordering;
12use core::{fmt, hash};
13use octseq::octets::{Octets, OctetsFrom};
14use octseq::parse::Parser;
15
16#[derive(Clone, Copy)]
31#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
32pub struct Question<N> {
33 qname: N,
35
36 qtype: Rtype,
38
39 qclass: Class,
41}
42
43impl<N> Question<N> {
46 pub fn new(qname: N, qtype: Rtype, qclass: Class) -> Self {
48 Question {
49 qname,
50 qtype,
51 qclass,
52 }
53 }
54
55 pub fn new_in(qname: N, qtype: Rtype) -> Self {
57 Question {
58 qname,
59 qtype,
60 qclass: Class::In,
61 }
62 }
63
64 pub fn into_qname(self) -> N {
66 self.qname
67 }
68}
69
70impl<N: ToDname> Question<N> {
73 pub fn qname(&self) -> &N {
75 &self.qname
76 }
77
78 pub fn qtype(&self) -> Rtype {
80 self.qtype
81 }
82
83 pub fn qclass(&self) -> Class {
85 self.qclass
86 }
87}
88
89impl<Octs> Question<ParsedDname<Octs>> {
92 pub fn parse<'a, Src: Octets<Range<'a> = Octs> + ?Sized + 'a>(
93 parser: &mut Parser<'a, Src>,
94 ) -> Result<Self, ParseError> {
95 Ok(Question::new(
96 ParsedDname::parse(parser)?,
97 Rtype::parse(parser)?,
98 Class::parse(parser)?,
99 ))
100 }
101}
102
103impl<N: ToDname> Question<N> {
104 pub fn compose<Target: Composer + ?Sized>(
105 &self,
106 target: &mut Target,
107 ) -> Result<(), Target::AppendError> {
108 target.append_compressed_dname(&self.qname)?;
109 self.qtype.compose(target)?;
110 self.qclass.compose(target)
111 }
112}
113
114impl<N: ToDname> From<(N, Rtype, Class)> for Question<N> {
117 fn from((name, rtype, class): (N, Rtype, Class)) -> Self {
118 Question::new(name, rtype, class)
119 }
120}
121
122impl<N: ToDname> From<(N, Rtype)> for Question<N> {
123 fn from((name, rtype): (N, Rtype)) -> Self {
124 Question::new(name, rtype, Class::In)
125 }
126}
127
128impl<Name, SrcName> OctetsFrom<Question<SrcName>> for Question<Name>
131where
132 Name: OctetsFrom<SrcName>,
133{
134 type Error = Name::Error;
135
136 fn try_octets_from(
137 source: Question<SrcName>,
138 ) -> Result<Self, Self::Error> {
139 Ok(Question::new(
140 Name::try_octets_from(source.qname)?,
141 source.qtype,
142 source.qclass,
143 ))
144 }
145}
146
147impl<N, NN> PartialEq<Question<NN>> for Question<N>
150where
151 N: ToDname,
152 NN: ToDname,
153{
154 fn eq(&self, other: &Question<NN>) -> bool {
155 self.qname.name_eq(&other.qname)
156 && self.qtype == other.qtype
157 && self.qclass == other.qclass
158 }
159}
160
161impl<N: ToDname> Eq for Question<N> {}
162
163impl<N, NN> PartialOrd<Question<NN>> for Question<N>
166where
167 N: ToDname,
168 NN: ToDname,
169{
170 fn partial_cmp(&self, other: &Question<NN>) -> Option<Ordering> {
171 match self.qname.name_cmp(&other.qname) {
172 Ordering::Equal => {}
173 other => return Some(other),
174 }
175 match self.qtype.partial_cmp(&other.qtype) {
176 Some(Ordering::Equal) => {}
177 other => return other,
178 }
179 self.qclass.partial_cmp(&other.qclass)
180 }
181}
182
183impl<N, NN> CanonicalOrd<Question<NN>> for Question<N>
184where
185 N: ToDname,
186 NN: ToDname,
187{
188 fn canonical_cmp(&self, other: &Question<NN>) -> Ordering {
189 match self.qname.lowercase_composed_cmp(&other.qname) {
190 Ordering::Equal => {}
191 other => return other,
192 }
193 match self.qtype.cmp(&other.qtype) {
194 Ordering::Equal => {}
195 other => return other,
196 }
197 self.qclass.cmp(&other.qclass)
198 }
199}
200
201impl<N: ToDname> Ord for Question<N> {
202 fn cmp(&self, other: &Self) -> Ordering {
203 match self.qname.name_cmp(&other.qname) {
204 Ordering::Equal => {}
205 other => return other,
206 }
207 match self.qtype.cmp(&other.qtype) {
208 Ordering::Equal => {}
209 other => return other,
210 }
211 self.qclass.cmp(&other.qclass)
212 }
213}
214
215impl<N: hash::Hash> hash::Hash for Question<N> {
218 fn hash<H: hash::Hasher>(&self, state: &mut H) {
219 self.qname.hash(state);
220 self.qtype.hash(state);
221 self.qclass.hash(state);
222 }
223}
224
225impl<N: fmt::Display> fmt::Display for Question<N> {
228 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
229 write!(f, "{}.\t{}\t{}", self.qname, self.qtype, self.qclass)
230 }
231}
232
233impl<N: fmt::Debug> fmt::Debug for Question<N> {
234 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
235 f.debug_struct("Question")
236 .field("qname", &self.qname)
237 .field("qtype", &self.qtype)
238 .field("qclass", &self.qclass)
239 .finish()
240 }
241}
242
243pub trait ComposeQuestion {
262 fn compose_question<Target: Composer + ?Sized>(
263 &self,
264 target: &mut Target,
265 ) -> Result<(), Target::AppendError>;
266}
267
268impl<'a, Q: ComposeQuestion> ComposeQuestion for &'a Q {
269 fn compose_question<Target: Composer + ?Sized>(
270 &self,
271 target: &mut Target,
272 ) -> Result<(), Target::AppendError> {
273 (*self).compose_question(target)
274 }
275}
276
277impl<Name: ToDname> ComposeQuestion for Question<Name> {
278 fn compose_question<Target: Composer + ?Sized>(
279 &self,
280 target: &mut Target,
281 ) -> Result<(), Target::AppendError> {
282 self.compose(target)
283 }
284}
285
286impl<Name: ToDname> ComposeQuestion for (Name, Rtype, Class) {
287 fn compose_question<Target: Composer + ?Sized>(
288 &self,
289 target: &mut Target,
290 ) -> Result<(), Target::AppendError> {
291 Question::new(&self.0, self.1, self.2).compose(target)
292 }
293}
294
295impl<Name: ToDname> ComposeQuestion for (Name, Rtype) {
296 fn compose_question<Target: Composer + ?Sized>(
297 &self,
298 target: &mut Target,
299 ) -> Result<(), Target::AppendError> {
300 Question::new(&self.0, self.1, Class::In).compose(target)
301 }
302}