1use super::cmp::CanonicalOrd;
8use super::iana::{Class, Rtype};
9use super::name;
10use super::name::{ParsedName, ToName};
11use super::wire::{Composer, ParseError};
12use core::cmp::Ordering;
13use core::str::FromStr;
14use core::{fmt, hash};
15use octseq::builder::ShortBuf;
16use octseq::octets::{Octets, OctetsFrom};
17use octseq::parse::Parser;
18
19#[derive(Clone, Copy)]
34#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
35pub struct Question<N> {
36 qname: N,
38
39 qtype: Rtype,
41
42 qclass: Class,
44}
45
46impl<N> Question<N> {
49 pub fn new(qname: N, qtype: Rtype, qclass: Class) -> Self {
51 Question {
52 qname,
53 qtype,
54 qclass,
55 }
56 }
57
58 pub fn new_in(qname: N, qtype: Rtype) -> Self {
60 Question {
61 qname,
62 qtype,
63 qclass: Class::IN,
64 }
65 }
66
67 pub fn into_qname(self) -> N {
69 self.qname
70 }
71}
72
73impl<N: ToName> Question<N> {
76 pub fn qname(&self) -> &N {
78 &self.qname
79 }
80
81 pub fn qtype(&self) -> Rtype {
83 self.qtype
84 }
85
86 pub fn qclass(&self) -> Class {
88 self.qclass
89 }
90}
91
92impl<Octs> Question<ParsedName<Octs>> {
95 pub fn parse<'a, Src: Octets<Range<'a> = Octs> + ?Sized + 'a>(
96 parser: &mut Parser<'a, Src>,
97 ) -> Result<Self, ParseError> {
98 Ok(Question::new(
99 ParsedName::parse(parser)?,
100 Rtype::parse(parser)?,
101 Class::parse(parser)?,
102 ))
103 }
104}
105
106impl<N: ToName> Question<N> {
107 pub fn compose<Target: Composer + ?Sized>(
108 &self,
109 target: &mut Target,
110 ) -> Result<(), Target::AppendError> {
111 target.append_compressed_name(&self.qname)?;
112 self.qtype.compose(target)?;
113 self.qclass.compose(target)
114 }
115}
116
117impl<N: ToName> From<(N, Rtype, Class)> for Question<N> {
120 fn from((name, rtype, class): (N, Rtype, Class)) -> Self {
121 Question::new(name, rtype, class)
122 }
123}
124
125impl<N: ToName> From<(N, Rtype)> for Question<N> {
126 fn from((name, rtype): (N, Rtype)) -> Self {
127 Question::new(name, rtype, Class::IN)
128 }
129}
130
131impl<N: FromStr<Err = name::FromStrError>> FromStr for Question<N> {
132 type Err = FromStrError;
133
134 fn from_str(s: &str) -> Result<Self, Self::Err> {
142 let mut s = s.split_whitespace();
143
144 let qname = match s.next() {
145 Some(qname) => qname,
146 None => return Err(PresentationErrorEnum::MissingQname.into()),
147 };
148 let qname = N::from_str(qname)?;
149 let class_or_qtype = match s.next() {
150 Some(value) => value,
151 None => {
152 return Err(PresentationErrorEnum::MissingClassAndQtype.into())
153 }
154 };
155 let res = match Class::from_str(class_or_qtype) {
156 Ok(class) => {
157 let qtype = match s.next() {
158 Some(qtype) => qtype,
159 None => {
160 return Err(PresentationErrorEnum::MissingQtype.into())
161 }
162 };
163 match Rtype::from_str(qtype) {
164 Ok(qtype) => Self::new(qname, qtype, class),
165 Err(_) => {
166 return Err(PresentationErrorEnum::BadQtype.into())
167 }
168 }
169 }
170 Err(_) => {
171 let qtype = match Rtype::from_str(class_or_qtype) {
172 Ok(qtype) => qtype,
173 Err(_) => {
174 return Err(PresentationErrorEnum::BadQtype.into())
175 }
176 };
177 let class = match s.next() {
178 Some(class) => class,
179 None => return Ok(Self::new(qname, qtype, Class::IN)),
180 };
181 match Class::from_str(class) {
182 Ok(class) => Self::new(qname, qtype, class),
183 Err(_) => {
184 return Err(PresentationErrorEnum::BadClass.into())
185 }
186 }
187 }
188 };
189 if s.next().is_some() {
190 return Err(PresentationErrorEnum::TrailingData.into());
191 }
192 Ok(res)
193 }
194}
195
196impl<Name, SrcName> OctetsFrom<Question<SrcName>> for Question<Name>
199where
200 Name: OctetsFrom<SrcName>,
201{
202 type Error = Name::Error;
203
204 fn try_octets_from(
205 source: Question<SrcName>,
206 ) -> Result<Self, Self::Error> {
207 Ok(Question::new(
208 Name::try_octets_from(source.qname)?,
209 source.qtype,
210 source.qclass,
211 ))
212 }
213}
214
215impl<N, NN> PartialEq<Question<NN>> for Question<N>
218where
219 N: ToName,
220 NN: ToName,
221{
222 fn eq(&self, other: &Question<NN>) -> bool {
223 self.qname.name_eq(&other.qname)
224 && self.qtype == other.qtype
225 && self.qclass == other.qclass
226 }
227}
228
229impl<N: ToName> Eq for Question<N> {}
230
231impl<N, NN> PartialOrd<Question<NN>> for Question<N>
234where
235 N: ToName,
236 NN: ToName,
237{
238 fn partial_cmp(&self, other: &Question<NN>) -> Option<Ordering> {
239 match self.qname.name_cmp(&other.qname) {
240 Ordering::Equal => {}
241 other => return Some(other),
242 }
243 match self.qtype.partial_cmp(&other.qtype) {
244 Some(Ordering::Equal) => {}
245 other => return other,
246 }
247 self.qclass.partial_cmp(&other.qclass)
248 }
249}
250
251impl<N, NN> CanonicalOrd<Question<NN>> for Question<N>
252where
253 N: ToName,
254 NN: ToName,
255{
256 fn canonical_cmp(&self, other: &Question<NN>) -> Ordering {
257 match self.qname.lowercase_composed_cmp(&other.qname) {
258 Ordering::Equal => {}
259 other => return other,
260 }
261 match self.qtype.cmp(&other.qtype) {
262 Ordering::Equal => {}
263 other => return other,
264 }
265 self.qclass.cmp(&other.qclass)
266 }
267}
268
269impl<N: ToName> Ord for Question<N> {
270 fn cmp(&self, other: &Self) -> Ordering {
271 match self.qname.name_cmp(&other.qname) {
272 Ordering::Equal => {}
273 other => return other,
274 }
275 match self.qtype.cmp(&other.qtype) {
276 Ordering::Equal => {}
277 other => return other,
278 }
279 self.qclass.cmp(&other.qclass)
280 }
281}
282
283impl<N: hash::Hash> hash::Hash for Question<N> {
286 fn hash<H: hash::Hasher>(&self, state: &mut H) {
287 self.qname.hash(state);
288 self.qtype.hash(state);
289 self.qclass.hash(state);
290 }
291}
292
293impl<N: fmt::Display> fmt::Display for Question<N> {
296 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
297 write!(f, "{}.\t{}\t{}", self.qname, self.qtype, self.qclass)
298 }
299}
300
301impl<N: fmt::Debug> fmt::Debug for Question<N> {
302 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
303 f.debug_struct("Question")
304 .field("qname", &self.qname)
305 .field("qtype", &self.qtype)
306 .field("qclass", &self.qclass)
307 .finish()
308 }
309}
310
311pub trait ComposeQuestion {
328 fn compose_question<Target: Composer + ?Sized>(
329 &self,
330 target: &mut Target,
331 ) -> Result<(), Target::AppendError>;
332}
333
334impl<Q: ComposeQuestion> ComposeQuestion for &Q {
335 fn compose_question<Target: Composer + ?Sized>(
336 &self,
337 target: &mut Target,
338 ) -> Result<(), Target::AppendError> {
339 (*self).compose_question(target)
340 }
341}
342
343impl<Name: ToName> ComposeQuestion for Question<Name> {
344 fn compose_question<Target: Composer + ?Sized>(
345 &self,
346 target: &mut Target,
347 ) -> Result<(), Target::AppendError> {
348 self.compose(target)
349 }
350}
351
352impl<Name: ToName> ComposeQuestion for (Name, Rtype, Class) {
353 fn compose_question<Target: Composer + ?Sized>(
354 &self,
355 target: &mut Target,
356 ) -> Result<(), Target::AppendError> {
357 Question::new(&self.0, self.1, self.2).compose(target)
358 }
359}
360
361impl<Name: ToName> ComposeQuestion for (Name, Rtype) {
362 fn compose_question<Target: Composer + ?Sized>(
363 &self,
364 target: &mut Target,
365 ) -> Result<(), Target::AppendError> {
366 Question::new(&self.0, self.1, Class::IN).compose(target)
367 }
368}
369
370#[derive(Clone, Copy, Debug, Eq, PartialEq)]
373pub enum FromStrError {
374 Presentation(PresentationError),
376
377 ShortBuf,
379}
380
381impl From<name::FromStrError> for FromStrError {
384 fn from(err: name::FromStrError) -> FromStrError {
385 match err {
386 name::FromStrError::Presentation(err) => {
387 Self::Presentation(err.into())
388 }
389 name::FromStrError::ShortBuf => Self::ShortBuf,
390 }
391 }
392}
393
394impl From<PresentationErrorEnum> for FromStrError {
395 fn from(err: PresentationErrorEnum) -> Self {
396 Self::Presentation(err.into())
397 }
398}
399
400impl fmt::Display for FromStrError {
403 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
404 match *self {
405 FromStrError::Presentation(err) => err.fmt(f),
406 FromStrError::ShortBuf => ShortBuf.fmt(f),
407 }
408 }
409}
410
411#[cfg(feature = "std")]
412impl std::error::Error for FromStrError {}
413
414#[derive(Clone, Copy, Debug, Eq, PartialEq)]
418pub struct PresentationError(PresentationErrorEnum);
419
420#[derive(Clone, Copy, Debug, Eq, PartialEq)]
421enum PresentationErrorEnum {
422 BadName(name::PresentationError),
423 MissingQname,
424 MissingClassAndQtype,
425 MissingQtype,
426 BadClass,
427 BadQtype,
428 TrailingData,
429}
430
431impl From<PresentationErrorEnum> for PresentationError {
434 fn from(err: PresentationErrorEnum) -> Self {
435 Self(err)
436 }
437}
438
439impl From<name::PresentationError> for PresentationError {
440 fn from(err: name::PresentationError) -> Self {
441 Self(PresentationErrorEnum::BadName(err))
442 }
443}
444
445impl fmt::Display for PresentationError {
448 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
449 match self.0 {
450 PresentationErrorEnum::BadName(err) => err.fmt(f),
451 PresentationErrorEnum::MissingQname => {
452 f.write_str("missing qname")
453 }
454 PresentationErrorEnum::MissingClassAndQtype => {
455 f.write_str("missing class and qtype")
456 }
457 PresentationErrorEnum::MissingQtype => {
458 f.write_str("missing qtype")
459 }
460 PresentationErrorEnum::BadClass => f.write_str("invalid class"),
461 PresentationErrorEnum::BadQtype => f.write_str("invalid qtype"),
462 PresentationErrorEnum::TrailingData => {
463 f.write_str("trailing data")
464 }
465 }
466 }
467}
468
469#[cfg(feature = "std")]
470impl std::error::Error for PresentationError {}