nonymous/rdata/
view.rs

1use core::ops::Range;
2use core::{
3    marker::PhantomData,
4    net::{IpAddr, Ipv4Addr, Ipv6Addr},
5};
6
7use byteorder::{ByteOrder, NetworkEndian};
8
9use self::error::{RdataError, RdataResultExt, SoaError};
10use crate::view::{assert, BorrowedView, CharacterString, CharacterStrings, Name, Record, View};
11use crate::{
12    core::{Class, Serial, Ttl, Type},
13    fmt::Format,
14    rdata::class::{GenericClass, InClass},
15};
16
17macro_rules! declare_rdata {
18    ($($variant:ident($($ty:tt)+),)*) => {
19        #[non_exhaustive]
20        pub enum Rdata<'s> {
21            $($variant($($ty)+<'s>),)*
22        }
23        impl<'s> From<&Record<'s>> for Rdata<'s> {
24            fn from(record: &Record<'s>) -> Self {
25                let range = record.rdata_range();
26
27                if let Ok(result) = || -> Result<Self, ()> {Ok(match (record.class(), record.r#type()) {
28                    (_, Type::NS) => Ns::view(record.source(), range).map_err(|_| ())?.0.into(),
29                    (_, Type::CNAME) => Cname::view(record.source(), range).map_err(|_| ())?.0.into(),
30                    (_, Type::SOA) => Soa::view(record.source(), range).map_err(|_| ())?.0.into(),
31                    (_, Type::PTR) => Ptr::view(record.source(), range).map_err(|_| ())?.0.into(),
32                    (_, Type::MX) => Mx::view(record.source(), range).map_err(|_| ())?.0.into(),
33                    (_, Type::TXT) => Txt::view(record.source(), range).map_err(|_| ())?.0.into(),
34                    (_, Type::CAA) => Caa::view(record.source(), range).map_err(|_| ())?.0.into(),
35                    (Class::IN, Type::A) => A::view(record.source(), range).map_err(|_| ())?.0.into(),
36                    (Class::IN, Type::AAAA) => Aaaa::view(record.source(), range).map_err(|_| ())?.0.into(),
37                    _ => Unknown::view(record.source(), range).map_err(|_| ())?.0.into(),
38                })}() {
39                    return result;
40                }
41
42                Malformed::view(record.source(), record.rdata_range())
43                    .expect("guaranteed by impl View for Record")
44                    .0
45                    .into()
46            }
47        }
48        $(impl<'s> From<$($ty)+<'s>> for Rdata<'s> {
49            fn from(inner: $($ty)+<'s>) -> Self {
50                Self::$variant(inner)
51            }
52        })*
53        impl<'s> AsRef<dyn View<'s, Error = RdataError>> for Rdata<'s> {
54            fn as_ref(&self) -> &dyn View<'s, Error = RdataError> {
55                match self {
56                    $(Self::$variant(x) => x,)*
57                }
58            }
59        }
60        impl<'s> AsRef<dyn Format + 's> for Rdata<'s> {
61            fn as_ref(&self) -> &(dyn Format + 's) {
62                match self {
63                    $(Self::$variant(x) => x,)*
64                }
65            }
66        }
67    };
68}
69
70declare_rdata! {
71    Malformed(Malformed),
72    Unknown(Unknown),
73    Ns(Ns),
74    Cname(Cname),
75    Soa(Soa),
76    Ptr(Ptr),
77    Mx(Mx),
78    Txt(Txt),
79    Caa(Caa),
80    InAddress(A),
81    InAaaa(Aaaa),
82}
83
84/// View for RDATA of NS records. Generic across all classes.
85pub struct Ns<'s, C: GenericClass = InClass> {
86    inner: Name<'s>,
87    _class: PhantomData<C>,
88}
89
90/// View for RDATA of CNAME records. Generic across all classes.
91pub struct Cname<'s, C: GenericClass = InClass> {
92    inner: Name<'s>,
93    _class: PhantomData<C>,
94}
95
96/// View for RDATA of SOA records. Generic across all classes.
97pub struct Soa<'s, C: GenericClass = InClass> {
98    source: &'s [u8],
99    offset: usize,
100    mname_len: usize,
101    rname_len: usize,
102    _class: PhantomData<C>,
103}
104
105/// View for RDATA of PTR records. Generic across all classes.
106pub struct Ptr<'s, C: GenericClass = InClass> {
107    inner: Name<'s>,
108    _class: PhantomData<C>,
109}
110
111/// View for RDATA of MX records. Currently implemented for IN class only.
112pub struct Mx<'s, C: GenericClass = InClass> {
113    source: &'s [u8],
114    offset: usize,
115    exchange_len: usize,
116    _class: PhantomData<C>,
117}
118
119/// View for RDATA of TXT records. Currently implemented for IN class only.
120pub struct Txt<'s, C: GenericClass = InClass> {
121    source: &'s [u8],
122    offset: usize,
123    len: usize,
124    _class: PhantomData<C>,
125}
126
127/// View for RDATA of CAA records. Currently implemented for IN class only.
128pub struct Caa<'s, C: GenericClass = InClass> {
129    source: &'s [u8],
130    offset: usize,
131    tag_len: usize,
132    value_len: usize,
133    _class: PhantomData<C>,
134}
135
136/// View for the tag parts of a [`Caa`].
137pub struct CaaTag<'s, C: GenericClass = InClass> {
138    source: &'s [u8],
139    offset: usize,
140    len: usize,
141    _class: PhantomData<C>,
142}
143
144/// View for the value parts of a [`Caa`].
145pub struct CaaValue<'s, C: GenericClass = InClass> {
146    source: &'s [u8],
147    offset: usize,
148    len: usize,
149    _class: PhantomData<C>,
150}
151
152/// View for malformed RDATA of records of known type. Generic across all classes.
153pub struct Malformed<'s, C: GenericClass = InClass> {
154    inner: Unknown<'s>,
155    _class: PhantomData<C>,
156}
157
158/// View for RDATA of records of unknown type. Generic across all classes.
159#[derive(Clone)]
160pub struct Unknown<'s, C: GenericClass = InClass> {
161    source: &'s [u8],
162    offset: usize,
163    len: usize,
164    _class: PhantomData<C>,
165}
166
167impl<C: GenericClass> Ns<'_, C> {
168    pub fn name(&self) -> Name {
169        self.inner.clone()
170    }
171}
172
173impl<C: GenericClass> Cname<'_, C> {
174    pub fn name(&self) -> Name {
175        self.inner.clone()
176    }
177}
178
179impl<C: GenericClass> Soa<'_, C> {
180    pub fn mname(&self) -> Name {
181        let start = self.offset;
182        let end = start + self.mname_len;
183        Name::new_unchecked(self.source, start..end)
184    }
185
186    pub fn rname(&self) -> Name {
187        let start = self.offset + self.mname_len;
188        let end = start + self.rname_len;
189        Name::new_unchecked(self.source, start..end)
190    }
191
192    pub fn serial(&self) -> Serial {
193        let offset = self.mname_len + self.rname_len;
194
195        Serial::new(NetworkEndian::read_u32(
196            &self.source[self.offset..][offset..],
197        ))
198    }
199
200    pub fn refresh(&self) -> Ttl {
201        let offset = self.mname_len + self.rname_len + 4;
202
203        Ttl::new(NetworkEndian::read_u32(
204            &self.source[self.offset..][offset..],
205        ))
206    }
207
208    pub fn retry(&self) -> Ttl {
209        let offset = self.mname_len + self.rname_len + 8;
210
211        Ttl::new(NetworkEndian::read_u32(
212            &self.source[self.offset..][offset..],
213        ))
214    }
215
216    pub fn expire(&self) -> Ttl {
217        let offset = self.mname_len + self.rname_len + 12;
218
219        Ttl::new(NetworkEndian::read_u32(
220            &self.source[self.offset..][offset..],
221        ))
222    }
223
224    pub fn minimum(&self) -> Ttl {
225        let offset = self.mname_len + self.rname_len + 16;
226
227        Ttl::new(NetworkEndian::read_u32(
228            &self.source[self.offset..][offset..],
229        ))
230    }
231}
232
233impl<C: GenericClass> Ptr<'_, C> {
234    pub fn name(&self) -> Name {
235        self.inner.clone()
236    }
237}
238
239impl Mx<'_, InClass> {
240    pub fn preference(&self) -> u16 {
241        NetworkEndian::read_u16(&self.source[self.offset..])
242    }
243
244    pub fn exchange(&self) -> Name {
245        let start = self.offset + 2;
246        let end = start + self.exchange_len;
247        Name::new_unchecked(self.source, start..end)
248    }
249}
250
251impl Txt<'_, InClass> {
252    pub fn data(&self) -> CharacterStrings<'_> {
253        let start = self.offset;
254        let stop = self.offset + self.len;
255
256        CharacterStrings::new(self.source, start..stop)
257    }
258}
259
260impl Caa<'_, InClass> {
261    pub fn flags(&self) -> u8 {
262        self.source[self.offset]
263    }
264
265    pub fn critical(&self) -> bool {
266        self.flags() >> 7 & 1 == 1
267    }
268
269    pub fn tag(&self) -> CaaTag {
270        CaaTag {
271            source: self.source,
272            offset: self.offset + 2,
273            len: self.tag_len,
274            _class: self._class,
275        }
276    }
277
278    pub fn value(&self) -> CaaValue {
279        CaaValue {
280            source: self.source,
281            offset: self.offset + 2 + self.tag_len,
282            len: self.value_len,
283            _class: self._class,
284        }
285    }
286}
287
288impl<C: GenericClass> Malformed<'_, C> {
289    pub fn as_unknown(&self) -> Unknown {
290        self.inner.clone()
291    }
292}
293
294impl<C: GenericClass> Unknown<'_, C> {
295    pub fn as_bytes(&self) -> &[u8] {
296        &self.source[self.offset..][..self.len()]
297    }
298}
299
300impl<'s, C: GenericClass + 's> View<'s> for Ns<'s, C> {
301    type Error = RdataError;
302    fn view_range(source: &'s [u8], range: Range<usize>) -> crate::view::Result<Self, Self::Error> {
303        let (name, rest) = Name::view(source, range.clone()).into_rdata_error::<Self>()?;
304        Ok((
305            Self {
306                inner: name,
307                _class: PhantomData,
308            },
309            rest,
310        ))
311    }
312}
313impl<'s, C: GenericClass> BorrowedView<'s> for Ns<'s, C> {
314    fn source(&self) -> &'s [u8] {
315        self.inner.source()
316    }
317    fn offset(&self) -> usize {
318        self.inner.offset()
319    }
320    fn len(&self) -> usize {
321        self.inner.len()
322    }
323}
324
325impl<'s, C: GenericClass + 's> View<'s> for Cname<'s, C> {
326    type Error = RdataError;
327    fn view_range(source: &'s [u8], range: Range<usize>) -> crate::view::Result<Self, Self::Error> {
328        let (name, rest) = Name::view(source, range.clone()).into_rdata_error::<Self>()?;
329        Ok((
330            Self {
331                inner: name,
332                _class: PhantomData,
333            },
334            rest,
335        ))
336    }
337}
338impl<'s, C: GenericClass> BorrowedView<'s> for Cname<'s, C> {
339    fn source(&self) -> &'s [u8] {
340        self.inner.source()
341    }
342    fn offset(&self) -> usize {
343        self.inner.offset()
344    }
345    fn len(&self) -> usize {
346        self.inner.len()
347    }
348}
349
350impl<'s, C: GenericClass + 's> View<'s> for Soa<'s, C> {
351    type Error = RdataError;
352
353    fn view_range(source: &'s [u8], range: Range<usize>) -> crate::view::Result<Self, Self::Error> {
354        let (_, rest) = Name::view(source, range.clone())
355            .map_err(SoaError::Name)
356            .into_rdata_error::<Self>()?;
357        let mname_len = rest.start - range.start;
358
359        let (_, mut rest) = Name::view(source, rest)
360            .map_err(SoaError::Name)
361            .into_rdata_error::<Self>()?;
362        let rname_len = rest.start - range.start - mname_len;
363
364        assert(source, &rest, 20)
365            .map_err(SoaError::Bounds)
366            .into_rdata_error::<Self>()?;
367        rest.start += 20;
368
369        Ok((
370            Soa {
371                source,
372                offset: range.start,
373                mname_len,
374                rname_len,
375                _class: PhantomData,
376            },
377            rest,
378        ))
379    }
380}
381impl<'s, C: GenericClass> BorrowedView<'s> for Soa<'s, C> {
382    fn source(&self) -> &'s [u8] {
383        self.source
384    }
385    fn offset(&self) -> usize {
386        self.offset
387    }
388    fn len(&self) -> usize {
389        self.mname_len + self.rname_len + 20
390    }
391}
392
393impl<'s, C: GenericClass + 's> View<'s> for Ptr<'s, C> {
394    type Error = RdataError;
395    fn view_range(source: &'s [u8], range: Range<usize>) -> crate::view::Result<Self, Self::Error> {
396        let (name, rest) = Name::view(source, range.clone()).into_rdata_error::<Self>()?;
397        Ok((
398            Self {
399                inner: name,
400                _class: PhantomData,
401            },
402            rest,
403        ))
404    }
405}
406impl<'s, C: GenericClass> BorrowedView<'s> for Ptr<'s, C> {
407    fn source(&self) -> &'s [u8] {
408        self.inner.source()
409    }
410    fn offset(&self) -> usize {
411        self.inner.offset()
412    }
413    fn len(&self) -> usize {
414        self.inner.len()
415    }
416}
417
418impl<'s> View<'s> for Mx<'s, InClass> {
419    type Error = RdataError;
420
421    fn view_range(source: &'s [u8], range: Range<usize>) -> crate::view::Result<Self, Self::Error> {
422        let mut rest = range.clone();
423
424        assert(source, &rest, 2).into_rdata_error::<Self>()?;
425        rest.start += 2;
426
427        let (_, rest) = Name::view(source, rest)
428            .map_err(SoaError::Name)
429            .into_rdata_error::<Self>()?;
430        let exchange_len = rest.start - range.start - 2;
431
432        Ok((
433            Mx {
434                source,
435                offset: range.start,
436                exchange_len,
437                _class: PhantomData,
438            },
439            rest,
440        ))
441    }
442}
443impl<'s> BorrowedView<'s> for Mx<'s, InClass> {
444    fn source(&self) -> &'s [u8] {
445        self.source
446    }
447    fn offset(&self) -> usize {
448        self.offset
449    }
450    fn len(&self) -> usize {
451        2 + self.exchange_len
452    }
453}
454
455impl<'s> View<'s> for Txt<'s, InClass> {
456    type Error = RdataError;
457
458    fn view_range(source: &'s [u8], range: Range<usize>) -> crate::view::Result<Self, Self::Error> {
459        let mut next = range.clone();
460
461        while next.start != next.end {
462            let (_, rest) = CharacterString::view(source, next).into_rdata_error::<Self>()?;
463            next = rest;
464        }
465
466        let offset = range.start;
467        let len = next.start - offset;
468
469        Ok((
470            Txt {
471                source,
472                offset,
473                len,
474                _class: PhantomData,
475            },
476            next,
477        ))
478    }
479}
480impl<'s> BorrowedView<'s> for Txt<'s, InClass> {
481    fn source(&self) -> &'s [u8] {
482        self.source
483    }
484    fn offset(&self) -> usize {
485        self.offset
486    }
487    fn len(&self) -> usize {
488        self.len
489    }
490}
491
492impl<'s> View<'s> for Caa<'s, InClass> {
493    type Error = RdataError;
494
495    fn view_range(source: &'s [u8], range: Range<usize>) -> crate::view::Result<Self, Self::Error> {
496        let mut rest = range.clone();
497
498        assert(source, &rest, 1).into_rdata_error::<Self>()?;
499        rest.start += 1;
500
501        let (octet, mut rest) = u8::view(source, rest).into_rdata_error::<Self>()?;
502        let tag_len: usize = octet.into();
503
504        assert(source, &rest, tag_len).into_rdata_error::<Self>()?;
505        rest.start += tag_len;
506
507        // value implicitly consumes the rest of the RDATA
508        let value_len = rest.end - rest.start;
509        rest.start += value_len;
510
511        Ok((
512            Caa {
513                source,
514                offset: range.start,
515                tag_len,
516                value_len,
517                _class: PhantomData,
518            },
519            rest,
520        ))
521    }
522}
523impl<'s> BorrowedView<'s> for Caa<'s, InClass> {
524    fn source(&self) -> &'s [u8] {
525        self.source
526    }
527    fn offset(&self) -> usize {
528        self.offset
529    }
530    fn len(&self) -> usize {
531        1 + 1 + self.tag_len + self.value_len
532    }
533}
534
535impl<'s> BorrowedView<'s> for CaaTag<'s, InClass> {
536    fn source(&self) -> &'s [u8] {
537        self.source
538    }
539    fn offset(&self) -> usize {
540        self.offset
541    }
542    fn len(&self) -> usize {
543        self.len
544    }
545}
546
547impl<'s> BorrowedView<'s> for CaaValue<'s, InClass> {
548    fn source(&self) -> &'s [u8] {
549        self.source
550    }
551    fn offset(&self) -> usize {
552        self.offset
553    }
554    fn len(&self) -> usize {
555        self.len
556    }
557}
558
559impl<'s, C: GenericClass + 's> View<'s> for Malformed<'s, C> {
560    type Error = RdataError;
561
562    fn view_range(source: &'s [u8], range: Range<usize>) -> crate::view::Result<Self, Self::Error> {
563        let (inner, range) = Unknown::view(source, range)?;
564
565        Ok((
566            Malformed {
567                inner,
568                _class: PhantomData,
569            },
570            range,
571        ))
572    }
573}
574impl<'s, C: GenericClass> BorrowedView<'s> for Malformed<'s, C> {
575    fn source(&self) -> &'s [u8] {
576        self.inner.source()
577    }
578    fn offset(&self) -> usize {
579        self.inner.offset()
580    }
581    fn len(&self) -> usize {
582        self.inner.len()
583    }
584}
585
586impl<'s, C: GenericClass + 's> View<'s> for Unknown<'s, C> {
587    type Error = RdataError;
588
589    fn view_range(
590        source: &'s [u8],
591        mut range: Range<usize>,
592    ) -> crate::view::Result<Self, Self::Error> {
593        let offset = range.start;
594        let len = range.end - offset;
595
596        assert(source, &range, len).into_rdata_error::<Self>()?;
597        range.start += len;
598
599        Ok((
600            Unknown {
601                source,
602                offset,
603                len,
604                _class: PhantomData,
605            },
606            range,
607        ))
608    }
609}
610impl<'s, C: GenericClass> BorrowedView<'s> for Unknown<'s, C> {
611    fn source(&self) -> &'s [u8] {
612        self.source
613    }
614    fn offset(&self) -> usize {
615        self.offset
616    }
617    fn len(&self) -> usize {
618        self.len
619    }
620}
621
622/// View for RDATA of A records. Currently implemented for IN class only.
623pub struct A<'s, C: GenericClass = InClass> {
624    source: &'s [u8],
625    pub offset: usize,
626    _class: PhantomData<C>,
627}
628
629/// View for RDATA of AAAA records. Currently implemented for IN class only.
630pub struct Aaaa<'s, C: GenericClass = InClass> {
631    source: &'s [u8],
632    pub offset: usize,
633    _class: PhantomData<C>,
634}
635
636impl A<'_, InClass> {
637    pub fn to_ipv4_addr(&self) -> Ipv4Addr {
638        let bits = NetworkEndian::read_u32(&self.source[self.offset..][..self.len()]);
639        Ipv4Addr::from_bits(bits)
640    }
641
642    pub fn to_ip_addr(&self) -> IpAddr {
643        self.to_ipv4_addr().into()
644    }
645}
646
647impl From<A<'_, InClass>> for Ipv4Addr {
648    fn from(value: A<'_>) -> Self {
649        value.to_ipv4_addr()
650    }
651}
652
653impl From<A<'_, InClass>> for IpAddr {
654    fn from(value: A<'_>) -> Self {
655        value.to_ip_addr()
656    }
657}
658
659impl Aaaa<'_, InClass> {
660    pub fn to_ipv6_addr(&self) -> Ipv6Addr {
661        let bits = NetworkEndian::read_u128(&self.source[self.offset..][..self.len()]);
662        Ipv6Addr::from_bits(bits)
663    }
664
665    pub fn to_ip_addr(&self) -> IpAddr {
666        self.to_ipv6_addr().into()
667    }
668}
669
670impl From<Aaaa<'_, InClass>> for Ipv6Addr {
671    fn from(value: Aaaa<'_>) -> Self {
672        value.to_ipv6_addr()
673    }
674}
675
676impl From<Aaaa<'_, InClass>> for IpAddr {
677    fn from(value: Aaaa<'_>) -> Self {
678        value.to_ip_addr()
679    }
680}
681
682impl<'s> View<'s> for A<'s, InClass> {
683    type Error = RdataError;
684
685    fn view_range(
686        source: &'s [u8],
687        mut range: Range<usize>,
688    ) -> crate::view::Result<Self, Self::Error> {
689        let offset = range.start;
690
691        assert(source, &range, 4).into_rdata_error::<Self>()?;
692        range.start += 4;
693
694        Ok((
695            A {
696                source,
697                offset,
698                _class: PhantomData,
699            },
700            range,
701        ))
702    }
703}
704impl<'s> BorrowedView<'s> for A<'s, InClass> {
705    fn source(&self) -> &'s [u8] {
706        self.source
707    }
708    fn offset(&self) -> usize {
709        self.offset
710    }
711    fn len(&self) -> usize {
712        4
713    }
714}
715
716impl<'s> View<'s> for Aaaa<'s, InClass> {
717    type Error = RdataError;
718
719    fn view_range(
720        source: &'s [u8],
721        mut range: Range<usize>,
722    ) -> crate::view::Result<Self, Self::Error> {
723        let offset = range.start;
724
725        assert(source, &range, 16).into_rdata_error::<Self>()?;
726        range.start += 16;
727
728        Ok((
729            Aaaa {
730                source,
731                offset,
732                _class: PhantomData,
733            },
734            range,
735        ))
736    }
737}
738impl<'s> BorrowedView<'s> for Aaaa<'s, InClass> {
739    fn source(&self) -> &'s [u8] {
740        self.source
741    }
742    fn offset(&self) -> usize {
743        self.offset
744    }
745    fn len(&self) -> usize {
746        16
747    }
748}
749
750pub mod error {
751    use crate::view::{BoundsError, NameError};
752
753    error!(TypedRdataError, View);
754    #[derive(Debug, displaydoc::Display)]
755    pub enum TypedRdataError {
756        /// RDATA view error: {0}
757        View(RdataError),
758        /// wrong CLASS or TYPE
759        WrongClassOrType,
760    }
761
762    #[cfg(feature = "eyre")]
763    error!(RdataError([s: s.as_ref()], __));
764    #[cfg(not(feature = "eyre"))]
765    error!(RdataError);
766    #[derive(Debug, displaydoc::Display)]
767    /// failed to view RDATA ({1})
768    pub struct RdataError(RdataError0, &'static str);
769
770    #[cfg(feature = "eyre")]
771    type RdataError0 = eyre::Report;
772    #[cfg(not(feature = "eyre"))]
773    type RdataError0 = ();
774
775    error!(SoaError, Bounds, Name);
776    /// failed to view SOA RDATA
777    #[derive(Debug, displaydoc::Display)]
778    #[prefix_enum_doc_attributes]
779    pub enum SoaError {
780        /// eof while viewing SERIAL or REFRESH or RETRY or EXPIRE or MINIMUM
781        Bounds(BoundsError),
782
783        /// SOA RDATA has malformed MNAME or RNAME
784        Name(NameError),
785    }
786
787    impl RdataError {
788        #[cfg(feature = "eyre")]
789        fn new<E: 'static + Send + Sync + core::error::Error>(error: E, ty: &'static str) -> Self {
790            Self(error.into(), ty)
791        }
792        #[cfg(not(feature = "eyre"))]
793        fn new<E: 'static + Send + Sync + core::error::Error>(_: E, ty: &'static str) -> Self {
794            Self((), ty)
795        }
796    }
797
798    pub(crate) trait RdataResultExt<T> {
799        fn into_rdata_error<V>(self) -> Result<T, RdataError>;
800    }
801
802    #[cfg(feature = "eyre")]
803    impl<T, E: 'static + Send + Sync + core::error::Error> RdataResultExt<T> for Result<T, E> {
804        fn into_rdata_error<V>(self) -> Result<T, RdataError> {
805            self.map_err(|e| RdataError::new(e, core::any::type_name::<V>()))
806        }
807    }
808    #[cfg(not(feature = "eyre"))]
809    impl<T, E: 'static + Send + Sync + core::error::Error> RdataResultExt<T> for Result<T, E> {
810        fn into_rdata_error<V>(self) -> Result<T, RdataError> {
811            self.map_err(|e| RdataError::new(e, core::any::type_name::<V>()))
812        }
813    }
814}
815
816#[cfg(test)]
817mod test {
818    use crate::{
819        core::Serial,
820        rdata::{
821            class::InClass,
822            view::{Caa, Mx, Soa},
823        },
824        view::{BorrowedView, View},
825    };
826
827    declare_any_error!(AnyError);
828
829    #[test]
830    fn soa() -> Result<(), AnyError> {
831        let soa = b"\x02ns\x07example\0\x0Ahostmaster\xC0\x03\0\0\0\x01\0\0\0\x02\0\0\0\x03\0\0\0\x04\0\0\0\x05";
832        let (soa, _) = Soa::<InClass>::view(soa, ..)?;
833        assert_eq!(soa.mname(), "ns.example.");
834        assert_eq!(soa.mname().len(), 12);
835        assert_eq!(soa.rname(), "hostmaster.example.");
836        assert_eq!(soa.rname().len(), 13);
837        assert_eq!(soa.serial(), Serial::new(1));
838        assert_eq!(soa.refresh().value(), 2);
839        assert_eq!(soa.retry().value(), 3);
840        assert_eq!(soa.expire().value(), 4);
841        assert_eq!(soa.minimum().value(), 5);
842        Ok(())
843    }
844
845    #[test]
846    fn mx() -> Result<(), AnyError> {
847        let mx = b"\xAA\x55\x04mail\x07example\0";
848        let (mx, _) = Mx::view(mx, ..)?;
849        assert_eq!(mx.preference(), 0xAA55);
850        assert_eq!(mx.exchange(), "mail.example.");
851        assert_eq!(mx.exchange().len(), 14);
852        Ok(())
853    }
854
855    #[test]
856    fn caa() -> Result<(), AnyError> {
857        let caa = b"\x80\x05issueletsencrypt.org";
858        let (caa, _) = Caa::view(caa, ..)?;
859        assert_eq!(caa.critical(), true);
860        assert_eq!(caa.tag().as_bytes(), b"issue");
861        assert_eq!(caa.value().as_bytes(), b"letsencrypt.org");
862        Ok(())
863    }
864}