1use crate::base::charstr::CharStr;
8use crate::base::cmp::CanonicalOrd;
9use crate::base::iana::Rtype;
10use crate::base::name::{FlattenInto, ParsedDname, ToDname};
11use crate::base::net::Ipv4Addr;
12use crate::base::rdata::{
13 ComposeRecordData, LongRecordData, ParseRecordData, RecordData,
14};
15use crate::base::scan::{Scan, Scanner, ScannerError, Symbol};
16use crate::base::serial::Serial;
17use crate::base::wire::{Compose, Composer, FormError, Parse, ParseError};
18use crate::base::Ttl;
19#[cfg(feature = "bytes")]
20use bytes::BytesMut;
21use core::cmp::Ordering;
22use core::convert::{Infallible, TryFrom};
23use core::str::FromStr;
24use core::{fmt, hash, str};
25use octseq::builder::{
26 infallible, EmptyBuilder, FreezeBuilder, FromBuilder, OctetsBuilder,
27 ShortBuf,
28};
29use octseq::octets::{Octets, OctetsFrom, OctetsInto};
30use octseq::parse::Parser;
31#[cfg(feature = "serde")]
32use octseq::serde::{DeserializeOctets, SerializeOctets};
33
34#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
44#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
45pub struct A {
46 addr: Ipv4Addr,
47}
48
49impl A {
50 #[must_use]
52 pub fn new(addr: Ipv4Addr) -> A {
53 A { addr }
54 }
55
56 #[must_use]
58 pub fn from_octets(a: u8, b: u8, c: u8, d: u8) -> A {
59 A::new(Ipv4Addr::new(a, b, c, d))
60 }
61
62 #[must_use]
63 pub fn addr(&self) -> Ipv4Addr {
64 self.addr
65 }
66 pub fn set_addr(&mut self, addr: Ipv4Addr) {
67 self.addr = addr
68 }
69
70 pub(super) fn convert_octets<E>(self) -> Result<Self, E> {
71 Ok(self)
72 }
73
74 pub(super) fn flatten<E>(self) -> Result<Self, E> {
75 Ok(self)
76 }
77
78 pub fn parse<Octs: AsRef<[u8]> + ?Sized>(
79 parser: &mut Parser<Octs>,
80 ) -> Result<Self, ParseError> {
81 Ipv4Addr::parse(parser).map(Self::new)
82 }
83
84 pub fn scan<S: Scanner>(scanner: &mut S) -> Result<Self, S::Error> {
85 let token = scanner.scan_octets()?;
86 let token = str::from_utf8(token.as_ref())
87 .map_err(|_| S::Error::custom("expected IPv4 address"))?;
88 A::from_str(token)
89 .map_err(|_| S::Error::custom("expected IPv4 address"))
90 }
91}
92
93impl OctetsFrom<A> for A {
96 type Error = Infallible;
97
98 fn try_octets_from(source: A) -> Result<Self, Self::Error> {
99 Ok(source)
100 }
101}
102
103impl From<Ipv4Addr> for A {
106 fn from(addr: Ipv4Addr) -> Self {
107 Self::new(addr)
108 }
109}
110
111impl From<A> for Ipv4Addr {
112 fn from(a: A) -> Self {
113 a.addr
114 }
115}
116
117impl FromStr for A {
118 type Err = <Ipv4Addr as FromStr>::Err;
119
120 fn from_str(s: &str) -> Result<Self, Self::Err> {
121 Ipv4Addr::from_str(s).map(A::new)
122 }
123}
124
125impl CanonicalOrd for A {
128 fn canonical_cmp(&self, other: &Self) -> Ordering {
129 self.cmp(other)
130 }
131}
132
133impl RecordData for A {
136 fn rtype(&self) -> Rtype {
137 Rtype::A
138 }
139}
140
141impl<'a, Octs: AsRef<[u8]> + ?Sized> ParseRecordData<'a, Octs> for A {
142 fn parse_rdata(
143 rtype: Rtype,
144 parser: &mut Parser<'a, Octs>,
145 ) -> Result<Option<Self>, ParseError> {
146 if rtype == Rtype::A {
147 Self::parse(parser).map(Some)
148 } else {
149 Ok(None)
150 }
151 }
152}
153
154impl ComposeRecordData for A {
155 fn rdlen(&self, _compress: bool) -> Option<u16> {
156 Some(4)
157 }
158
159 fn compose_rdata<Target: Composer + ?Sized>(
160 &self,
161 target: &mut Target,
162 ) -> Result<(), Target::AppendError> {
163 target.append_slice(&self.addr.octets())
164 }
165
166 fn compose_canonical_rdata<Target: Composer + ?Sized>(
167 &self,
168 target: &mut Target,
169 ) -> Result<(), Target::AppendError> {
170 self.compose_rdata(target)
171 }
172}
173
174impl fmt::Display for A {
177 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
178 self.addr.fmt(f)
179 }
180}
181
182impl AsRef<Ipv4Addr> for A {
185 fn as_ref(&self) -> &Ipv4Addr {
186 &self.addr
187 }
188}
189
190impl AsMut<Ipv4Addr> for A {
191 fn as_mut(&mut self) -> &mut Ipv4Addr {
192 &mut self.addr
193 }
194}
195
196dname_type_well_known! {
199 (Cname, Cname, cname, into_cname)
206}
207
208#[derive(Clone)]
217#[cfg_attr(
218 feature = "serde",
219 derive(serde::Serialize, serde::Deserialize),
220 serde(bound(
221 serialize = "Octs: AsRef<[u8]> + octseq::serde::SerializeOctets",
222 deserialize = "Octs: \
223 FromBuilder \
224 + octseq::serde::DeserializeOctets<'de>, \
225 <Octs as FromBuilder>::Builder: AsRef<[u8]> + EmptyBuilder ",
226 ))
227)]
228pub struct Hinfo<Octs> {
229 cpu: CharStr<Octs>,
230 os: CharStr<Octs>,
231}
232
233impl<Octs> Hinfo<Octs> {
234 pub fn new(cpu: CharStr<Octs>, os: CharStr<Octs>) -> Self {
236 Hinfo { cpu, os }
237 }
238
239 pub fn cpu(&self) -> &CharStr<Octs> {
241 &self.cpu
242 }
243
244 pub fn os(&self) -> &CharStr<Octs> {
246 &self.os
247 }
248
249 pub(super) fn convert_octets<Target: OctetsFrom<Octs>>(
250 self,
251 ) -> Result<Hinfo<Target>, Target::Error> {
252 Ok(Hinfo::new(
253 self.cpu.try_octets_into()?,
254 self.os.try_octets_into()?,
255 ))
256 }
257
258 pub(super) fn flatten<Target: OctetsFrom<Octs>>(
259 self,
260 ) -> Result<Hinfo<Target>, Target::Error> {
261 self.convert_octets()
262 }
263
264 pub fn parse<'a, Src: Octets<Range<'a> = Octs> + ?Sized>(
265 parser: &mut Parser<'a, Src>,
266 ) -> Result<Self, ParseError> {
267 Ok(Self::new(CharStr::parse(parser)?, CharStr::parse(parser)?))
268 }
269
270 pub fn scan<S: Scanner<Octets = Octs>>(
271 scanner: &mut S,
272 ) -> Result<Self, S::Error> {
273 Ok(Self::new(scanner.scan_charstr()?, scanner.scan_charstr()?))
274 }
275}
276
277impl<Octs, SrcOcts> OctetsFrom<Hinfo<SrcOcts>> for Hinfo<Octs>
280where
281 Octs: OctetsFrom<SrcOcts>,
282{
283 type Error = Octs::Error;
284
285 fn try_octets_from(source: Hinfo<SrcOcts>) -> Result<Self, Self::Error> {
286 Ok(Hinfo::new(
287 CharStr::try_octets_from(source.cpu)?,
288 CharStr::try_octets_from(source.os)?,
289 ))
290 }
291}
292
293impl<Octs, Other> PartialEq<Hinfo<Other>> for Hinfo<Octs>
296where
297 Octs: AsRef<[u8]>,
298 Other: AsRef<[u8]>,
299{
300 fn eq(&self, other: &Hinfo<Other>) -> bool {
301 self.cpu.eq(&other.cpu) && self.os.eq(&other.os)
302 }
303}
304
305impl<Octs: AsRef<[u8]>> Eq for Hinfo<Octs> {}
306
307impl<Octs, Other> PartialOrd<Hinfo<Other>> for Hinfo<Octs>
310where
311 Octs: AsRef<[u8]>,
312 Other: AsRef<[u8]>,
313{
314 fn partial_cmp(&self, other: &Hinfo<Other>) -> Option<Ordering> {
315 match self.cpu.partial_cmp(&other.cpu) {
316 Some(Ordering::Equal) => {}
317 other => return other,
318 }
319 self.os.partial_cmp(&other.os)
320 }
321}
322
323impl<Octs, Other> CanonicalOrd<Hinfo<Other>> for Hinfo<Octs>
324where
325 Octs: AsRef<[u8]>,
326 Other: AsRef<[u8]>,
327{
328 fn canonical_cmp(&self, other: &Hinfo<Other>) -> Ordering {
329 match self.cpu.canonical_cmp(&other.cpu) {
330 Ordering::Equal => {}
331 other => return other,
332 }
333 self.os.canonical_cmp(&other.os)
334 }
335}
336
337impl<Octs: AsRef<[u8]>> Ord for Hinfo<Octs> {
338 fn cmp(&self, other: &Self) -> Ordering {
339 match self.cpu.cmp(&other.cpu) {
340 Ordering::Equal => {}
341 other => return other,
342 }
343 self.os.cmp(&other.os)
344 }
345}
346
347impl<Octs: AsRef<[u8]>> hash::Hash for Hinfo<Octs> {
350 fn hash<H: hash::Hasher>(&self, state: &mut H) {
351 self.cpu.hash(state);
352 self.os.hash(state);
353 }
354}
355
356impl<Octs> RecordData for Hinfo<Octs> {
359 fn rtype(&self) -> Rtype {
360 Rtype::Hinfo
361 }
362}
363
364impl<'a, Octs> ParseRecordData<'a, Octs> for Hinfo<Octs::Range<'a>>
365where
366 Octs: Octets + ?Sized,
367{
368 fn parse_rdata(
369 rtype: Rtype,
370 parser: &mut Parser<'a, Octs>,
371 ) -> Result<Option<Self>, ParseError> {
372 if rtype == Rtype::Hinfo {
373 Self::parse(parser).map(Some)
374 } else {
375 Ok(None)
376 }
377 }
378}
379
380impl<Octs: AsRef<[u8]>> ComposeRecordData for Hinfo<Octs> {
381 fn rdlen(&self, _compress: bool) -> Option<u16> {
382 Some(self.cpu.compose_len() + self.os.compose_len())
383 }
384
385 fn compose_rdata<Target: Composer + ?Sized>(
386 &self,
387 target: &mut Target,
388 ) -> Result<(), Target::AppendError> {
389 self.cpu.compose(target)?;
390 self.os.compose(target)
391 }
392
393 fn compose_canonical_rdata<Target: Composer + ?Sized>(
394 &self,
395 target: &mut Target,
396 ) -> Result<(), Target::AppendError> {
397 self.compose_rdata(target)
398 }
399}
400
401impl<Octs: AsRef<[u8]>> fmt::Display for Hinfo<Octs> {
404 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
405 write!(f, "{} {}", self.cpu, self.os)
406 }
407}
408
409impl<Octs: AsRef<[u8]>> fmt::Debug for Hinfo<Octs> {
412 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
413 f.debug_struct("Hinfo")
414 .field("cpu", &self.cpu)
415 .field("os", &self.os)
416 .finish()
417 }
418}
419
420dname_type_well_known! {
423 (Mb, Mb, madname, into_madname)
429}
430
431dname_type_well_known! {
434 (Md, Md, madname, into_madname)
444}
445
446dname_type_well_known! {
449 (Mf, Mf, madname, into_madname)
459}
460
461dname_type_well_known! {
464 (Mg, Mg, madname, into_madname)
473}
474
475#[derive(Clone, Debug, Hash)]
487#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
488pub struct Minfo<N> {
489 rmailbx: N,
490 emailbx: N,
491}
492
493impl<N> Minfo<N> {
494 pub fn new(rmailbx: N, emailbx: N) -> Self {
496 Minfo { rmailbx, emailbx }
497 }
498
499 pub fn rmailbx(&self) -> &N {
505 &self.rmailbx
506 }
507
508 pub fn emailbx(&self) -> &N {
515 &self.emailbx
516 }
517
518 pub(super) fn convert_octets<Target: OctetsFrom<N>>(
519 self,
520 ) -> Result<Minfo<Target>, Target::Error> {
521 Ok(Minfo::new(
522 self.rmailbx.try_octets_into()?,
523 self.emailbx.try_octets_into()?,
524 ))
525 }
526
527 pub(super) fn flatten<TargetName>(
528 self,
529 ) -> Result<Minfo<TargetName>, N::AppendError>
530 where N: FlattenInto<TargetName> {
531 Ok(Minfo::new(
532 self.rmailbx.try_flatten_into()?,
533 self.emailbx.try_flatten_into()?,
534 ))
535 }
536
537 pub fn scan<S: Scanner<Dname = N>>(
538 scanner: &mut S,
539 ) -> Result<Self, S::Error> {
540 Ok(Self::new(scanner.scan_dname()?, scanner.scan_dname()?))
541 }
542}
543
544impl<Octs> Minfo<ParsedDname<Octs>> {
545 pub fn parse<'a, Src: Octets<Range<'a> = Octs> + ?Sized>(
546 parser: &mut Parser<'a, Src>,
547 ) -> Result<Self, ParseError> {
548 Ok(Self::new(
549 ParsedDname::parse(parser)?,
550 ParsedDname::parse(parser)?,
551 ))
552 }
553}
554
555impl<Name, SrcName> OctetsFrom<Minfo<SrcName>> for Minfo<Name>
558where
559 Name: OctetsFrom<SrcName>,
560{
561 type Error = Name::Error;
562
563 fn try_octets_from(source: Minfo<SrcName>) -> Result<Self, Self::Error> {
564 Ok(Minfo::new(
565 Name::try_octets_from(source.rmailbx)?,
566 Name::try_octets_from(source.emailbx)?,
567 ))
568 }
569}
570
571impl<Name, TName> FlattenInto<Minfo<TName>> for Minfo<Name>
572where
573 Name: FlattenInto<TName>,
574{
575 type AppendError = Name::AppendError;
576
577 fn try_flatten_into(self) -> Result<Minfo<TName>, Name::AppendError> {
578 self.flatten()
579 }
580}
581
582impl<N, NN> PartialEq<Minfo<NN>> for Minfo<N>
585where
586 N: ToDname,
587 NN: ToDname,
588{
589 fn eq(&self, other: &Minfo<NN>) -> bool {
590 self.rmailbx.name_eq(&other.rmailbx)
591 && self.emailbx.name_eq(&other.emailbx)
592 }
593}
594
595impl<N: ToDname> Eq for Minfo<N> {}
596
597impl<N, NN> PartialOrd<Minfo<NN>> for Minfo<N>
600where
601 N: ToDname,
602 NN: ToDname,
603{
604 fn partial_cmp(&self, other: &Minfo<NN>) -> Option<Ordering> {
605 match self.rmailbx.name_cmp(&other.rmailbx) {
606 Ordering::Equal => {}
607 other => return Some(other),
608 }
609 Some(self.emailbx.name_cmp(&other.emailbx))
610 }
611}
612
613impl<N: ToDname> Ord for Minfo<N> {
614 fn cmp(&self, other: &Self) -> Ordering {
615 match self.rmailbx.name_cmp(&other.rmailbx) {
616 Ordering::Equal => {}
617 other => return other,
618 }
619 self.emailbx.name_cmp(&other.emailbx)
620 }
621}
622
623impl<N: ToDname, NN: ToDname> CanonicalOrd<Minfo<NN>> for Minfo<N> {
624 fn canonical_cmp(&self, other: &Minfo<NN>) -> Ordering {
625 match self.rmailbx.lowercase_composed_cmp(&other.rmailbx) {
626 Ordering::Equal => {}
627 other => return other,
628 }
629 self.emailbx.lowercase_composed_cmp(&other.emailbx)
630 }
631}
632
633impl<N> RecordData for Minfo<N> {
636 fn rtype(&self) -> Rtype {
637 Rtype::Minfo
638 }
639}
640
641impl<'a, Octs: Octets + ?Sized> ParseRecordData<'a, Octs>
642 for Minfo<ParsedDname<Octs::Range<'a>>>
643{
644 fn parse_rdata(
645 rtype: Rtype,
646 parser: &mut Parser<'a, Octs>,
647 ) -> Result<Option<Self>, ParseError> {
648 if rtype == Rtype::Minfo {
649 Self::parse(parser).map(Some)
650 } else {
651 Ok(None)
652 }
653 }
654}
655
656impl<Name: ToDname> ComposeRecordData for Minfo<Name> {
657 fn rdlen(&self, compress: bool) -> Option<u16> {
658 if compress {
659 None
660 } else {
661 Some(self.rmailbx.compose_len() + self.emailbx.compose_len())
662 }
663 }
664
665 fn compose_rdata<Target: Composer + ?Sized>(
666 &self,
667 target: &mut Target,
668 ) -> Result<(), Target::AppendError> {
669 if target.can_compress() {
670 target.append_compressed_dname(&self.rmailbx)?;
671 target.append_compressed_dname(&self.emailbx)
672 } else {
673 self.rmailbx.compose(target)?;
674 self.emailbx.compose(target)
675 }
676 }
677
678 fn compose_canonical_rdata<Target: Composer + ?Sized>(
679 &self,
680 target: &mut Target,
681 ) -> Result<(), Target::AppendError> {
682 self.rmailbx.compose_canonical(target)?;
683 self.emailbx.compose_canonical(target)
684 }
685}
686
687impl<N: fmt::Display> fmt::Display for Minfo<N> {
690 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
691 write!(f, "{}. {}.", self.rmailbx, self.emailbx)
692 }
693}
694
695dname_type_well_known! {
698 (Mr, Mr, newname, into_newname)
707}
708
709#[derive(Clone, Debug, Hash)]
718#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
719pub struct Mx<N> {
720 preference: u16,
721 exchange: N,
722}
723
724impl<N> Mx<N> {
725 pub fn new(preference: u16, exchange: N) -> Self {
727 Mx {
728 preference,
729 exchange,
730 }
731 }
732
733 pub fn preference(&self) -> u16 {
738 self.preference
739 }
740
741 pub fn exchange(&self) -> &N {
743 &self.exchange
744 }
745
746 pub(super) fn convert_octets<Target: OctetsFrom<N>>(
747 self,
748 ) -> Result<Mx<Target>, Target::Error> {
749 Ok(Mx::new(self.preference, self.exchange.try_octets_into()?))
750 }
751
752 pub(super) fn flatten<TargetName>(
753 self,
754 ) -> Result<Mx<TargetName>, N::AppendError>
755 where N: FlattenInto<TargetName> {
756 Ok(Mx::new(self.preference, self.exchange.try_flatten_into()?))
757 }
758
759 pub fn scan<S: Scanner<Dname = N>>(
760 scanner: &mut S,
761 ) -> Result<Self, S::Error> {
762 Ok(Self::new(u16::scan(scanner)?, scanner.scan_dname()?))
763 }
764}
765
766impl<Octs> Mx<ParsedDname<Octs>> {
767 pub fn parse<'a, Src: Octets<Range<'a> = Octs> + ?Sized + 'a>(
768 parser: &mut Parser<'a, Src>,
769 ) -> Result<Self, ParseError> {
770 Ok(Self::new(u16::parse(parser)?, ParsedDname::parse(parser)?))
771 }
772}
773
774impl<Name, SrcName> OctetsFrom<Mx<SrcName>> for Mx<Name>
777where
778 Name: OctetsFrom<SrcName>,
779{
780 type Error = Name::Error;
781
782 fn try_octets_from(source: Mx<SrcName>) -> Result<Self, Self::Error> {
783 Ok(Mx::new(
784 source.preference,
785 Name::try_octets_from(source.exchange)?,
786 ))
787 }
788}
789
790impl<Name, TName> FlattenInto<Mx<TName>> for Mx<Name>
791where
792 Name: FlattenInto<TName>,
793{
794 type AppendError = Name::AppendError;
795
796 fn try_flatten_into(self) -> Result<Mx<TName>, Name::AppendError> {
797 self.flatten()
798 }
799}
800
801impl<N, NN> PartialEq<Mx<NN>> for Mx<N>
804where
805 N: ToDname,
806 NN: ToDname,
807{
808 fn eq(&self, other: &Mx<NN>) -> bool {
809 self.preference == other.preference
810 && self.exchange.name_eq(&other.exchange)
811 }
812}
813
814impl<N: ToDname> Eq for Mx<N> {}
815
816impl<N, NN> PartialOrd<Mx<NN>> for Mx<N>
819where
820 N: ToDname,
821 NN: ToDname,
822{
823 fn partial_cmp(&self, other: &Mx<NN>) -> Option<Ordering> {
824 match self.preference.partial_cmp(&other.preference) {
825 Some(Ordering::Equal) => {}
826 other => return other,
827 }
828 Some(self.exchange.name_cmp(&other.exchange))
829 }
830}
831
832impl<N: ToDname> Ord for Mx<N> {
833 fn cmp(&self, other: &Self) -> Ordering {
834 match self.preference.cmp(&other.preference) {
835 Ordering::Equal => {}
836 other => return other,
837 }
838 self.exchange.name_cmp(&other.exchange)
839 }
840}
841
842impl<N: ToDname, NN: ToDname> CanonicalOrd<Mx<NN>> for Mx<N> {
843 fn canonical_cmp(&self, other: &Mx<NN>) -> Ordering {
844 match self.preference.cmp(&other.preference) {
845 Ordering::Equal => {}
846 other => return other,
847 }
848 self.exchange.lowercase_composed_cmp(&other.exchange)
849 }
850}
851
852impl<N> RecordData for Mx<N> {
855 fn rtype(&self) -> Rtype {
856 Rtype::Mx
857 }
858}
859
860impl<'a, Octs: Octets + ?Sized> ParseRecordData<'a, Octs>
861 for Mx<ParsedDname<Octs::Range<'a>>>
862{
863 fn parse_rdata(
864 rtype: Rtype,
865 parser: &mut Parser<'a, Octs>,
866 ) -> Result<Option<Self>, ParseError> {
867 if rtype == Rtype::Mx {
868 Self::parse(parser).map(Some)
869 } else {
870 Ok(None)
871 }
872 }
873}
874
875impl<Name: ToDname> ComposeRecordData for Mx<Name> {
876 fn rdlen(&self, compress: bool) -> Option<u16> {
877 if compress {
878 None
879 } else {
880 Some(u16::COMPOSE_LEN + self.exchange.compose_len())
881 }
882 }
883
884 fn compose_rdata<Target: Composer + ?Sized>(
885 &self,
886 target: &mut Target,
887 ) -> Result<(), Target::AppendError> {
888 if target.can_compress() {
889 self.preference.compose(target)?;
890 target.append_compressed_dname(&self.exchange)
891 } else {
892 self.preference.compose(target)?;
893 self.exchange.compose(target)
894 }
895 }
896
897 fn compose_canonical_rdata<Target: Composer + ?Sized>(
898 &self,
899 target: &mut Target,
900 ) -> Result<(), Target::AppendError> {
901 self.preference.compose(target)?;
902 self.exchange.compose_canonical(target)
903 }
904}
905
906impl<N: fmt::Display> fmt::Display for Mx<N> {
909 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
910 write!(f, "{} {}.", self.preference, self.exchange)
911 }
912}
913
914dname_type_well_known! {
917 (Ns, Ns, nsdname, into_nsdname)
923}
924
925#[derive(Clone)]
934#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
935pub struct Null<Octs: ?Sized> {
936 #[cfg_attr(
937 feature = "serde",
938 serde(
939 serialize_with = "octseq::serde::SerializeOctets::serialize_octets",
940 deserialize_with = "octseq::serde::DeserializeOctets::deserialize_octets",
941 bound(
942 serialize = "Octs: octseq::serde::SerializeOctets",
943 deserialize = "Octs: octseq::serde::DeserializeOctets<'de>",
944 )
945 )
946 )]
947 data: Octs,
948}
949
950impl<Octs> Null<Octs> {
951 pub fn from_octets(data: Octs) -> Result<Self, LongRecordData>
955 where
956 Octs: AsRef<[u8]>,
957 {
958 Null::check_slice(data.as_ref())?;
959 Ok(unsafe { Self::from_octets_unchecked(data) })
960 }
961
962 pub unsafe fn from_octets_unchecked(data: Octs) -> Self {
968 Null { data }
969 }
970}
971
972impl Null<[u8]> {
973 pub fn from_slice(data: &[u8]) -> Result<&Self, LongRecordData> {
977 Self::check_slice(data)?;
978 Ok(unsafe { Self::from_slice_unchecked(data) })
979 }
980
981 #[must_use]
987 pub unsafe fn from_slice_unchecked(data: &[u8]) -> &Self {
988 &*(data as *const [u8] as *const Self)
989 }
990
991 fn check_slice(slice: &[u8]) -> Result<(), LongRecordData> {
993 LongRecordData::check_len(slice.len())
994 }
995}
996
997impl<Octs: ?Sized> Null<Octs> {
998 pub fn data(&self) -> &Octs {
1000 &self.data
1001 }
1002}
1003
1004impl<Octs: AsRef<[u8]>> Null<Octs> {
1005 pub fn len(&self) -> usize {
1006 self.data.as_ref().len()
1007 }
1008
1009 pub fn is_empty(&self) -> bool {
1010 self.data.as_ref().is_empty()
1011 }
1012}
1013
1014impl<Octs> Null<Octs> {
1015 pub(super) fn convert_octets<Target: OctetsFrom<Octs>>(
1016 self,
1017 ) -> Result<Null<Target>, Target::Error> {
1018 Ok(unsafe {
1019 Null::from_octets_unchecked(self.data.try_octets_into()?)
1020 })
1021 }
1022
1023 pub(super) fn flatten<Target: OctetsFrom<Octs>>(
1024 self,
1025 ) -> Result<Null<Target>, Target::Error> {
1026 self.convert_octets()
1027 }
1028}
1029
1030impl<Octs> Null<Octs> {
1031 pub fn parse<'a, Src: Octets<Range<'a> = Octs> + ?Sized>(
1032 parser: &mut Parser<'a, Src>,
1033 ) -> Result<Self, ParseError> {
1034 let len = parser.remaining();
1035 parser
1036 .parse_octets(len)
1037 .map(|res| unsafe { Self::from_octets_unchecked(res) })
1038 .map_err(Into::into)
1039 }
1040}
1041
1042impl<Octs, SrcOcts> OctetsFrom<Null<SrcOcts>> for Null<Octs>
1045where
1046 Octs: OctetsFrom<SrcOcts>,
1047{
1048 type Error = Octs::Error;
1049
1050 fn try_octets_from(source: Null<SrcOcts>) -> Result<Self, Self::Error> {
1051 Octs::try_octets_from(source.data)
1052 .map(|res| unsafe { Self::from_octets_unchecked(res) })
1053 }
1054}
1055
1056impl<Octs, Other> PartialEq<Null<Other>> for Null<Octs>
1059where
1060 Octs: AsRef<[u8]> + ?Sized,
1061 Other: AsRef<[u8]> + ?Sized,
1062{
1063 fn eq(&self, other: &Null<Other>) -> bool {
1064 self.data.as_ref().eq(other.data.as_ref())
1065 }
1066}
1067
1068impl<Octs: AsRef<[u8]> + ?Sized> Eq for Null<Octs> {}
1069
1070impl<Octs, Other> PartialOrd<Null<Other>> for Null<Octs>
1073where
1074 Octs: AsRef<[u8]> + ?Sized,
1075 Other: AsRef<[u8]> + ?Sized,
1076{
1077 fn partial_cmp(&self, other: &Null<Other>) -> Option<Ordering> {
1078 self.data.as_ref().partial_cmp(other.data.as_ref())
1079 }
1080}
1081
1082impl<Octs, Other> CanonicalOrd<Null<Other>> for Null<Octs>
1083where
1084 Octs: AsRef<[u8]> + ?Sized,
1085 Other: AsRef<[u8]> + ?Sized,
1086{
1087 fn canonical_cmp(&self, other: &Null<Other>) -> Ordering {
1088 self.data.as_ref().cmp(other.data.as_ref())
1089 }
1090}
1091
1092impl<Octs: AsRef<[u8]> + ?Sized> Ord for Null<Octs> {
1093 fn cmp(&self, other: &Self) -> Ordering {
1094 self.data.as_ref().cmp(other.data.as_ref())
1095 }
1096}
1097
1098impl<Octs: AsRef<[u8]> + ?Sized> hash::Hash for Null<Octs> {
1101 fn hash<H: hash::Hasher>(&self, state: &mut H) {
1102 self.data.as_ref().hash(state)
1103 }
1104}
1105
1106impl<Octs: ?Sized> RecordData for Null<Octs> {
1109 fn rtype(&self) -> Rtype {
1110 Rtype::Null
1111 }
1112}
1113
1114impl<'a, Octs> ParseRecordData<'a, Octs> for Null<Octs::Range<'a>>
1115where
1116 Octs: Octets + ?Sized,
1117{
1118 fn parse_rdata(
1119 rtype: Rtype,
1120 parser: &mut Parser<'a, Octs>,
1121 ) -> Result<Option<Self>, ParseError> {
1122 if rtype == Rtype::Null {
1123 Self::parse(parser).map(Some)
1124 } else {
1125 Ok(None)
1126 }
1127 }
1128}
1129
1130impl<Octs: AsRef<[u8]> + ?Sized> ComposeRecordData for Null<Octs> {
1131 fn rdlen(&self, _compress: bool) -> Option<u16> {
1132 Some(
1133 u16::try_from(self.data.as_ref().len()).expect("long NULL rdata"),
1134 )
1135 }
1136
1137 fn compose_rdata<Target: Composer + ?Sized>(
1138 &self,
1139 target: &mut Target,
1140 ) -> Result<(), Target::AppendError> {
1141 target.append_slice(self.data.as_ref())
1142 }
1143
1144 fn compose_canonical_rdata<Target: Composer + ?Sized>(
1145 &self,
1146 target: &mut Target,
1147 ) -> Result<(), Target::AppendError> {
1148 self.compose_rdata(target)
1149 }
1150}
1151
1152impl<Octs: AsRef<Other>, Other> AsRef<Other> for Null<Octs> {
1155 fn as_ref(&self) -> &Other {
1156 self.data.as_ref()
1157 }
1158}
1159
1160impl<Octs: AsRef<[u8]>> fmt::Display for Null<Octs> {
1163 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1164 write!(f, "\\# {}", self.data.as_ref().len())?;
1165 for ch in self.data.as_ref().iter() {
1166 write!(f, " {:02x}", ch)?;
1167 }
1168 Ok(())
1169 }
1170}
1171
1172impl<Octs: AsRef<[u8]>> fmt::Debug for Null<Octs> {
1173 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1174 f.write_str("Null(")?;
1175 fmt::Display::fmt(self, f)?;
1176 f.write_str(")")
1177 }
1178}
1179
1180dname_type_well_known! {
1183 (Ptr, Ptr, ptrdname, into_ptrdname)
1190}
1191
1192#[derive(Clone, Debug, Hash)]
1201#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1202pub struct Soa<N> {
1203 mname: N,
1204 rname: N,
1205 serial: Serial,
1206 refresh: Ttl,
1207 retry: Ttl,
1208 expire: Ttl,
1209 minimum: Ttl,
1210}
1211
1212impl<N> Soa<N> {
1213 pub fn new(
1215 mname: N,
1216 rname: N,
1217 serial: Serial,
1218 refresh: Ttl,
1219 retry: Ttl,
1220 expire: Ttl,
1221 minimum: Ttl,
1222 ) -> Self {
1223 Soa {
1224 mname,
1225 rname,
1226 serial,
1227 refresh,
1228 retry,
1229 expire,
1230 minimum,
1231 }
1232 }
1233
1234 pub fn mname(&self) -> &N {
1236 &self.mname
1237 }
1238
1239 pub fn rname(&self) -> &N {
1241 &self.rname
1242 }
1243
1244 pub fn serial(&self) -> Serial {
1246 self.serial
1247 }
1248
1249 pub fn refresh(&self) -> Ttl {
1251 self.refresh
1252 }
1253
1254 pub fn retry(&self) -> Ttl {
1256 self.retry
1257 }
1258
1259 pub fn expire(&self) -> Ttl {
1261 self.expire
1262 }
1263
1264 pub fn minimum(&self) -> Ttl {
1266 self.minimum
1267 }
1268
1269 pub(super) fn convert_octets<Target: OctetsFrom<N>>(
1270 self,
1271 ) -> Result<Soa<Target>, Target::Error> {
1272 Ok(Soa::new(
1273 self.mname.try_octets_into()?,
1274 self.rname.try_octets_into()?,
1275 self.serial,
1276 self.refresh,
1277 self.retry,
1278 self.expire,
1279 self.minimum,
1280 ))
1281 }
1282
1283 pub(super) fn flatten<TargetName>(
1284 self,
1285 ) -> Result<Soa<TargetName>, N::AppendError>
1286 where N: FlattenInto<TargetName> {
1287 Ok(Soa::new(
1288 self.mname.try_flatten_into()?,
1289 self.rname.try_flatten_into()?,
1290 self.serial,
1291 self.refresh,
1292 self.retry,
1293 self.expire,
1294 self.minimum,
1295 ))
1296 }
1297
1298 pub fn scan<S: Scanner<Dname = N>>(
1299 scanner: &mut S,
1300 ) -> Result<Self, S::Error> {
1301 Ok(Self::new(
1302 scanner.scan_dname()?,
1303 scanner.scan_dname()?,
1304 Serial::scan(scanner)?,
1305 Ttl::scan(scanner)?,
1306 Ttl::scan(scanner)?,
1307 Ttl::scan(scanner)?,
1308 Ttl::scan(scanner)?,
1309 ))
1310 }
1311}
1312
1313impl<Octs> Soa<ParsedDname<Octs>> {
1314 pub fn parse<'a, Src: Octets<Range<'a> = Octs> + ?Sized + 'a>(
1315 parser: &mut Parser<'a, Src>,
1316 ) -> Result<Self, ParseError> {
1317 Ok(Self::new(
1318 ParsedDname::parse(parser)?,
1319 ParsedDname::parse(parser)?,
1320 Serial::parse(parser)?,
1321 Ttl::parse(parser)?,
1322 Ttl::parse(parser)?,
1323 Ttl::parse(parser)?,
1324 Ttl::parse(parser)?,
1325 ))
1326 }
1327}
1328
1329impl<Name, SrcName> OctetsFrom<Soa<SrcName>> for Soa<Name>
1332where
1333 Name: OctetsFrom<SrcName>,
1334{
1335 type Error = Name::Error;
1336
1337 fn try_octets_from(source: Soa<SrcName>) -> Result<Self, Self::Error> {
1338 Ok(Soa::new(
1339 Name::try_octets_from(source.mname)?,
1340 Name::try_octets_from(source.rname)?,
1341 source.serial,
1342 source.refresh,
1343 source.retry,
1344 source.expire,
1345 source.minimum,
1346 ))
1347 }
1348}
1349
1350impl<Name, TName> FlattenInto<Soa<TName>> for Soa<Name>
1351where
1352 Name: FlattenInto<TName>,
1353{
1354 type AppendError = Name::AppendError;
1355
1356 fn try_flatten_into(self) -> Result<Soa<TName>, Name::AppendError> {
1357 self.flatten()
1358 }
1359}
1360
1361impl<N, NN> PartialEq<Soa<NN>> for Soa<N>
1364where
1365 N: ToDname,
1366 NN: ToDname,
1367{
1368 fn eq(&self, other: &Soa<NN>) -> bool {
1369 self.mname.name_eq(&other.mname)
1370 && self.rname.name_eq(&other.rname)
1371 && self.serial == other.serial
1372 && self.refresh == other.refresh
1373 && self.retry == other.retry
1374 && self.expire == other.expire
1375 && self.minimum == other.minimum
1376 }
1377}
1378
1379impl<N: ToDname> Eq for Soa<N> {}
1380
1381impl<N, NN> PartialOrd<Soa<NN>> for Soa<N>
1384where
1385 N: ToDname,
1386 NN: ToDname,
1387{
1388 fn partial_cmp(&self, other: &Soa<NN>) -> Option<Ordering> {
1389 match self.mname.name_cmp(&other.mname) {
1390 Ordering::Equal => {}
1391 other => return Some(other),
1392 }
1393 match self.rname.name_cmp(&other.rname) {
1394 Ordering::Equal => {}
1395 other => return Some(other),
1396 }
1397 match u32::from(self.serial).partial_cmp(&u32::from(other.serial)) {
1398 Some(Ordering::Equal) => {}
1399 other => return other,
1400 }
1401 match self.refresh.partial_cmp(&other.refresh) {
1402 Some(Ordering::Equal) => {}
1403 other => return other,
1404 }
1405 match self.retry.partial_cmp(&other.retry) {
1406 Some(Ordering::Equal) => {}
1407 other => return other,
1408 }
1409 match self.expire.partial_cmp(&other.expire) {
1410 Some(Ordering::Equal) => {}
1411 other => return other,
1412 }
1413 self.minimum.partial_cmp(&other.minimum)
1414 }
1415}
1416
1417impl<N: ToDname> Ord for Soa<N> {
1418 fn cmp(&self, other: &Self) -> Ordering {
1419 match self.mname.name_cmp(&other.mname) {
1420 Ordering::Equal => {}
1421 other => return other,
1422 }
1423 match self.rname.name_cmp(&other.rname) {
1424 Ordering::Equal => {}
1425 other => return other,
1426 }
1427 match u32::from(self.serial).cmp(&u32::from(other.serial)) {
1428 Ordering::Equal => {}
1429 other => return other,
1430 }
1431 match self.refresh.cmp(&other.refresh) {
1432 Ordering::Equal => {}
1433 other => return other,
1434 }
1435 match self.retry.cmp(&other.retry) {
1436 Ordering::Equal => {}
1437 other => return other,
1438 }
1439 match self.expire.cmp(&other.expire) {
1440 Ordering::Equal => {}
1441 other => return other,
1442 }
1443 self.minimum.cmp(&other.minimum)
1444 }
1445}
1446
1447impl<N: ToDname, NN: ToDname> CanonicalOrd<Soa<NN>> for Soa<N> {
1448 fn canonical_cmp(&self, other: &Soa<NN>) -> Ordering {
1449 match self.mname.lowercase_composed_cmp(&other.mname) {
1450 Ordering::Equal => {}
1451 other => return other,
1452 }
1453 match self.rname.lowercase_composed_cmp(&other.rname) {
1454 Ordering::Equal => {}
1455 other => return other,
1456 }
1457 match self.serial.canonical_cmp(&other.serial) {
1458 Ordering::Equal => {}
1459 other => return other,
1460 }
1461 match self.refresh.cmp(&other.refresh) {
1462 Ordering::Equal => {}
1463 other => return other,
1464 }
1465 match self.retry.cmp(&other.retry) {
1466 Ordering::Equal => {}
1467 other => return other,
1468 }
1469 match self.expire.cmp(&other.expire) {
1470 Ordering::Equal => {}
1471 other => return other,
1472 }
1473 self.minimum.cmp(&other.minimum)
1474 }
1475}
1476
1477impl<N> RecordData for Soa<N> {
1480 fn rtype(&self) -> Rtype {
1481 Rtype::Soa
1482 }
1483}
1484
1485impl<'a, Octs: Octets + ?Sized> ParseRecordData<'a, Octs>
1486 for Soa<ParsedDname<Octs::Range<'a>>>
1487{
1488 fn parse_rdata(
1489 rtype: Rtype,
1490 parser: &mut Parser<'a, Octs>,
1491 ) -> Result<Option<Self>, ParseError> {
1492 if rtype == Rtype::Soa {
1493 Self::parse(parser).map(Some)
1494 } else {
1495 Ok(None)
1496 }
1497 }
1498}
1499
1500impl<Name: ToDname> ComposeRecordData for Soa<Name> {
1501 fn rdlen(&self, compress: bool) -> Option<u16> {
1502 if compress {
1503 None
1504 } else {
1505 Some(
1506 self.mname.compose_len()
1507 + self.rname.compose_len()
1508 + Serial::COMPOSE_LEN
1509 + 4 * u32::COMPOSE_LEN,
1510 )
1511 }
1512 }
1513
1514 fn compose_rdata<Target: Composer + ?Sized>(
1515 &self,
1516 target: &mut Target,
1517 ) -> Result<(), Target::AppendError> {
1518 if target.can_compress() {
1519 target.append_compressed_dname(&self.mname)?;
1520 target.append_compressed_dname(&self.rname)?;
1521 } else {
1522 self.mname.compose(target)?;
1523 self.rname.compose(target)?;
1524 }
1525 self.compose_fixed(target)
1526 }
1527
1528 fn compose_canonical_rdata<Target: Composer + ?Sized>(
1529 &self,
1530 target: &mut Target,
1531 ) -> Result<(), Target::AppendError> {
1532 self.mname.compose_canonical(target)?;
1533 self.rname.compose_canonical(target)?;
1534 self.compose_fixed(target)
1535 }
1536}
1537
1538impl<Name: ToDname> Soa<Name> {
1539 fn compose_fixed<Target: Composer + ?Sized>(
1540 &self,
1541 target: &mut Target,
1542 ) -> Result<(), Target::AppendError> {
1543 self.serial.compose(target)?;
1544 self.refresh.compose(target)?;
1545 self.retry.compose(target)?;
1546 self.expire.compose(target)?;
1547 self.minimum.compose(target)
1548 }
1549}
1550
1551impl<N: fmt::Display> fmt::Display for Soa<N> {
1554 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1555 write!(
1556 f,
1557 "{}. {}. {} {} {} {} {}",
1558 self.mname,
1559 self.rname,
1560 self.serial,
1561 self.refresh.as_secs(),
1562 self.retry.as_secs(),
1563 self.expire.as_secs(),
1564 self.minimum.as_secs()
1565 )
1566 }
1567}
1568
1569#[derive(Clone)]
1577pub struct Txt<Octs: ?Sized>(Octs);
1578
1579impl<Octs: FromBuilder> Txt<Octs> {
1580 pub fn build_from_slice(text: &[u8]) -> Result<Self, ShortBuf>
1582 where
1583 <Octs as FromBuilder>::Builder:
1584 EmptyBuilder + AsRef<[u8]> + AsMut<[u8]>,
1585 {
1586 let mut builder = TxtBuilder::<Octs::Builder>::new();
1587 builder.append_slice(text)?;
1588 Ok(builder.finish())
1589 }
1590}
1591
1592impl<Octs> Txt<Octs> {
1593 pub fn from_octets(octets: Octs) -> Result<Self, TxtError>
1595 where
1596 Octs: AsRef<[u8]>,
1597 {
1598 Txt::check_slice(octets.as_ref())?;
1599 Ok(unsafe { Txt::from_octets_unchecked(octets) })
1600 }
1601
1602 unsafe fn from_octets_unchecked(octets: Octs) -> Self {
1609 Txt(octets)
1610 }
1611}
1612
1613impl Txt<[u8]> {
1614 pub fn from_slice(slice: &[u8]) -> Result<&Self, TxtError> {
1616 Txt::check_slice(slice)?;
1617 Ok(unsafe { Txt::from_slice_unchecked(slice) })
1618 }
1619
1620 unsafe fn from_slice_unchecked(slice: &[u8]) -> &Self {
1627 unsafe { &*(slice as *const [u8] as *const Self) }
1628 }
1629
1630 fn check_slice(mut slice: &[u8]) -> Result<(), TxtError> {
1632 LongRecordData::check_len(slice.len())?;
1633 while let Some(&len) = slice.first() {
1634 let len = usize::from(len);
1635 if slice.len() <= len {
1636 return Err(TxtError(TxtErrorInner::ShortInput));
1637 }
1638 slice = &slice[len + 1..];
1639 }
1640 Ok(())
1641 }
1642}
1643
1644impl<Octs> Txt<Octs> {
1645 pub fn parse<'a, Src: Octets<Range<'a> = Octs> + ?Sized>(
1646 parser: &mut Parser<'a, Src>,
1647 ) -> Result<Self, ParseError>
1648 where
1649 Octs: AsRef<[u8]>,
1650 {
1651 let len = parser.remaining();
1652 let text = parser.parse_octets(len)?;
1653 let mut tmp = Parser::from_ref(text.as_ref());
1654 while tmp.remaining() != 0 {
1655 CharStr::skip(&mut tmp)?
1656 }
1657 Ok(Txt(text))
1658 }
1659
1660 pub fn scan<S: Scanner<Octets = Octs>>(
1661 scanner: &mut S,
1662 ) -> Result<Self, S::Error> {
1663 scanner.scan_charstr_entry().map(Txt)
1664 }
1665}
1666
1667impl<Octs: AsRef<[u8]> + ?Sized> Txt<Octs> {
1668 pub fn iter(&self) -> TxtIter {
1673 TxtIter(self.iter_char_strs())
1674 }
1675
1676 pub fn iter_char_strs(&self) -> TxtCharStrIter {
1677 TxtCharStrIter(Parser::from_ref(self.0.as_ref()))
1678 }
1679
1680 pub fn as_flat_slice(&self) -> Option<&[u8]> {
1682 if usize::from(self.0.as_ref()[0]) == self.0.as_ref().len() - 1 {
1683 Some(&self.0.as_ref()[1..])
1684 } else {
1685 None
1686 }
1687 }
1688
1689 pub fn len(&self) -> usize {
1690 self.0.as_ref().len()
1691 }
1692
1693 pub fn is_empty(&self) -> bool {
1694 self.0.as_ref().is_empty()
1695 }
1696
1697 pub fn try_text<T: FromBuilder>(
1706 &self,
1707 ) -> Result<T, <<T as FromBuilder>::Builder as OctetsBuilder>::AppendError>
1708 where
1709 <T as FromBuilder>::Builder: EmptyBuilder,
1710 {
1711 let mut res = T::Builder::with_capacity(self.len());
1714 for item in self.iter() {
1715 res.append_slice(item)?;
1716 }
1717 Ok(res.freeze())
1718 }
1719
1720 pub fn text<T: FromBuilder>(&self) -> T
1721 where
1722 <T as FromBuilder>::Builder: EmptyBuilder,
1723 <<T as FromBuilder>::Builder as OctetsBuilder>::AppendError:
1724 Into<Infallible>,
1725 {
1726 infallible(self.try_text())
1727 }
1728}
1729
1730impl<SrcOcts> Txt<SrcOcts> {
1731 pub(super) fn convert_octets<Target: OctetsFrom<SrcOcts>>(
1732 self,
1733 ) -> Result<Txt<Target>, Target::Error> {
1734 Ok(Txt(self.0.try_octets_into()?))
1735 }
1736
1737 pub(super) fn flatten<Octs: OctetsFrom<SrcOcts>>(
1738 self,
1739 ) -> Result<Txt<Octs>, Octs::Error> {
1740 self.convert_octets()
1741 }
1742}
1743
1744impl<Octs, SrcOcts> OctetsFrom<Txt<SrcOcts>> for Txt<Octs>
1747where
1748 Octs: OctetsFrom<SrcOcts>,
1749{
1750 type Error = Octs::Error;
1751
1752 fn try_octets_from(source: Txt<SrcOcts>) -> Result<Self, Self::Error> {
1753 Octs::try_octets_from(source.0).map(Self)
1754 }
1755}
1756
1757impl<'a, Octs: AsRef<[u8]>> IntoIterator for &'a Txt<Octs> {
1760 type Item = &'a [u8];
1761 type IntoIter = TxtIter<'a>;
1762
1763 fn into_iter(self) -> Self::IntoIter {
1764 self.iter()
1765 }
1766}
1767
1768impl<Octs, Other> PartialEq<Txt<Other>> for Txt<Octs>
1771where
1772 Octs: AsRef<[u8]>,
1773 Other: AsRef<[u8]>,
1774{
1775 fn eq(&self, other: &Txt<Other>) -> bool {
1776 self.iter()
1777 .flat_map(|s| s.iter().copied())
1778 .eq(other.iter().flat_map(|s| s.iter().copied()))
1779 }
1780}
1781
1782impl<Octs: AsRef<[u8]>> Eq for Txt<Octs> {}
1783
1784impl<Octs, Other> PartialOrd<Txt<Other>> for Txt<Octs>
1787where
1788 Octs: AsRef<[u8]>,
1789 Other: AsRef<[u8]>,
1790{
1791 fn partial_cmp(&self, other: &Txt<Other>) -> Option<Ordering> {
1792 self.iter()
1793 .flat_map(|s| s.iter().copied())
1794 .partial_cmp(other.iter().flat_map(|s| s.iter().copied()))
1795 }
1796}
1797
1798impl<Octs, Other> CanonicalOrd<Txt<Other>> for Txt<Octs>
1799where
1800 Octs: AsRef<[u8]>,
1801 Other: AsRef<[u8]>,
1802{
1803 fn canonical_cmp(&self, other: &Txt<Other>) -> Ordering {
1804 for (a, b) in self.iter().zip(other.iter()) {
1809 match (a.len(), a).cmp(&(b.len(), b)) {
1810 Ordering::Equal => continue,
1811 r => return r,
1812 }
1813 }
1814
1815 Ordering::Equal
1816 }
1817}
1818
1819impl<Octs: AsRef<[u8]>> Ord for Txt<Octs> {
1820 fn cmp(&self, other: &Self) -> Ordering {
1821 self.iter()
1822 .flat_map(|s| s.iter().copied())
1823 .cmp(other.iter().flat_map(|s| s.iter().copied()))
1824 }
1825}
1826
1827impl<Octs: AsRef<[u8]>> hash::Hash for Txt<Octs> {
1830 fn hash<H: hash::Hasher>(&self, state: &mut H) {
1831 self.iter()
1832 .flat_map(|s| s.iter().copied())
1833 .for_each(|c| c.hash(state))
1834 }
1835}
1836
1837impl<Octs> RecordData for Txt<Octs> {
1840 fn rtype(&self) -> Rtype {
1841 Rtype::Txt
1842 }
1843}
1844
1845impl<'a, Octs> ParseRecordData<'a, Octs> for Txt<Octs::Range<'a>>
1846where
1847 Octs: Octets + ?Sized,
1848{
1849 fn parse_rdata(
1850 rtype: Rtype,
1851 parser: &mut Parser<'a, Octs>,
1852 ) -> Result<Option<Self>, ParseError> {
1853 if rtype == Rtype::Txt {
1854 Self::parse(parser).map(Some)
1855 } else {
1856 Ok(None)
1857 }
1858 }
1859}
1860
1861impl<Octs: AsRef<[u8]>> ComposeRecordData for Txt<Octs> {
1862 fn rdlen(&self, _compress: bool) -> Option<u16> {
1863 Some(u16::try_from(self.0.as_ref().len()).expect("long TXT rdata"))
1864 }
1865
1866 fn compose_rdata<Target: Composer + ?Sized>(
1867 &self,
1868 target: &mut Target,
1869 ) -> Result<(), Target::AppendError> {
1870 target.append_slice(self.0.as_ref())
1871 }
1872
1873 fn compose_canonical_rdata<Target: Composer + ?Sized>(
1874 &self,
1875 target: &mut Target,
1876 ) -> Result<(), Target::AppendError> {
1877 self.compose_rdata(target)
1878 }
1879}
1880
1881impl<Octs: AsRef<[u8]>> fmt::Display for Txt<Octs> {
1884 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1885 for slice in self.iter() {
1886 for ch in slice.iter() {
1887 fmt::Display::fmt(&Symbol::from_octet(*ch), f)?
1888 }
1889 }
1890 Ok(())
1891 }
1892}
1893
1894impl<Octs: AsRef<[u8]>> fmt::Debug for Txt<Octs> {
1897 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1898 f.write_str("Txt(")?;
1899 fmt::Display::fmt(self, f)?;
1900 f.write_str(")")
1901 }
1902}
1903
1904#[cfg(feature = "serde")]
1907impl<Octs> serde::Serialize for Txt<Octs>
1908where
1909 Octs: AsRef<[u8]> + SerializeOctets,
1910{
1911 fn serialize<S: serde::Serializer>(
1912 &self,
1913 serializer: S,
1914 ) -> Result<S::Ok, S::Error> {
1915 use serde::ser::SerializeSeq;
1916
1917 struct TxtSeq<'a, Octs>(&'a Txt<Octs>);
1918
1919 impl<'a, Octs> serde::Serialize for TxtSeq<'a, Octs>
1920 where
1921 Octs: AsRef<[u8]> + SerializeOctets,
1922 {
1923 fn serialize<S: serde::Serializer>(
1924 &self,
1925 serializer: S,
1926 ) -> Result<S::Ok, S::Error> {
1927 let mut serializer = serializer.serialize_seq(None)?;
1928 for item in self.0.iter_char_strs() {
1929 serializer
1930 .serialize_element(&format_args!("{}", item))?;
1931 }
1932 serializer.end()
1933 }
1934 }
1935
1936 if serializer.is_human_readable() {
1937 serializer.serialize_newtype_struct("Txt", &TxtSeq(self))
1938 } else {
1939 serializer.serialize_newtype_struct(
1940 "Txt",
1941 &self.0.as_serialized_octets(),
1942 )
1943 }
1944 }
1945}
1946
1947#[cfg(feature = "serde")]
1948impl<'de, Octs> serde::Deserialize<'de> for Txt<Octs>
1949where
1950 Octs: FromBuilder + DeserializeOctets<'de>,
1951 <Octs as FromBuilder>::Builder: EmptyBuilder + AsRef<[u8]> + AsMut<[u8]>,
1952{
1953 fn deserialize<D: serde::Deserializer<'de>>(
1954 deserializer: D,
1955 ) -> Result<Self, D::Error> {
1956 use core::marker::PhantomData;
1957
1958 struct InnerVisitor<'de, T: DeserializeOctets<'de>>(T::Visitor);
1959
1960 impl<'de, Octs> serde::de::Visitor<'de> for InnerVisitor<'de, Octs>
1961 where
1962 Octs: FromBuilder + DeserializeOctets<'de>,
1963 <Octs as FromBuilder>::Builder:
1964 OctetsBuilder + EmptyBuilder + AsRef<[u8]> + AsMut<[u8]>,
1965 {
1966 type Value = Txt<Octs>;
1967
1968 fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
1969 f.write_str("TXT record data")
1970 }
1971
1972 fn visit_str<E: serde::de::Error>(
1973 self,
1974 v: &str,
1975 ) -> Result<Self::Value, E> {
1976 let mut builder =
1979 TxtBuilder::<<Octs as FromBuilder>::Builder>::new();
1980 let mut chars = v.chars();
1981 while let Some(ch) =
1982 Symbol::from_chars(&mut chars).map_err(E::custom)?
1983 {
1984 builder
1985 .append_u8(ch.into_octet().map_err(E::custom)?)
1986 .map_err(E::custom)?;
1987 }
1988 Ok(builder.finish())
1989 }
1990
1991 fn visit_seq<A: serde::de::SeqAccess<'de>>(
1992 self,
1993 mut seq: A,
1994 ) -> Result<Self::Value, A::Error> {
1995 let mut builder =
1996 TxtBuilder::<<Octs as FromBuilder>::Builder>::new();
1997 while let Some(s) = seq.next_element::<&'de str>()? {
1998 builder.append_zone_char_str(s)?;
1999 }
2000 Ok(builder.finish())
2001 }
2002
2003 fn visit_borrowed_bytes<E: serde::de::Error>(
2004 self,
2005 value: &'de [u8],
2006 ) -> Result<Self::Value, E> {
2007 self.0.visit_borrowed_bytes(value).and_then(|octets| {
2008 Txt::from_octets(octets).map_err(E::custom)
2009 })
2010 }
2011
2012 #[cfg(feature = "std")]
2013 fn visit_byte_buf<E: serde::de::Error>(
2014 self,
2015 value: std::vec::Vec<u8>,
2016 ) -> Result<Self::Value, E> {
2017 self.0.visit_byte_buf(value).and_then(|octets| {
2018 Txt::from_octets(octets).map_err(E::custom)
2019 })
2020 }
2021 }
2022
2023 struct NewtypeVisitor<T>(PhantomData<T>);
2024
2025 impl<'de, Octs> serde::de::Visitor<'de> for NewtypeVisitor<Octs>
2026 where
2027 Octs: FromBuilder + DeserializeOctets<'de>,
2028 <Octs as FromBuilder>::Builder:
2029 OctetsBuilder + EmptyBuilder + AsRef<[u8]> + AsMut<[u8]>,
2030 {
2031 type Value = Txt<Octs>;
2032
2033 fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
2034 f.write_str("TXT record data")
2035 }
2036
2037 fn visit_newtype_struct<D: serde::Deserializer<'de>>(
2038 self,
2039 deserializer: D,
2040 ) -> Result<Self::Value, D::Error> {
2041 deserializer.deserialize_any(InnerVisitor(Octs::visitor()))
2042 }
2054 }
2055
2056 deserializer
2057 .deserialize_newtype_struct("Txt", NewtypeVisitor(PhantomData))
2058 }
2059}
2060
2061#[derive(Clone)]
2065pub struct TxtCharStrIter<'a>(Parser<'a, [u8]>);
2066
2067impl<'a> Iterator for TxtCharStrIter<'a> {
2068 type Item = CharStr<&'a [u8]>;
2069
2070 fn next(&mut self) -> Option<Self::Item> {
2071 if self.0.remaining() == 0 {
2072 None
2073 } else {
2074 Some(CharStr::parse(&mut self.0).unwrap())
2075 }
2076 }
2077}
2078
2079#[derive(Clone)]
2083pub struct TxtIter<'a>(TxtCharStrIter<'a>);
2084
2085impl<'a> Iterator for TxtIter<'a> {
2086 type Item = &'a [u8];
2087
2088 fn next(&mut self) -> Option<Self::Item> {
2089 self.0.next().map(CharStr::into_octets)
2090 }
2091}
2092
2093#[derive(Clone, Debug)]
2096pub struct TxtBuilder<Builder> {
2097 builder: Builder,
2098
2099 start: Option<usize>,
2103}
2104
2105impl<Builder: OctetsBuilder + EmptyBuilder> TxtBuilder<Builder> {
2106 #[must_use]
2107 pub fn new() -> Self {
2108 TxtBuilder {
2109 builder: Builder::empty(),
2110 start: None,
2111 }
2112 }
2113}
2114
2115#[cfg(feature = "bytes")]
2116impl TxtBuilder<BytesMut> {
2117 pub fn new_bytes() -> Self {
2118 Self::new()
2119 }
2120}
2121
2122impl<Builder: OctetsBuilder + AsRef<[u8]> + AsMut<[u8]>> TxtBuilder<Builder> {
2123 fn builder_append_slice(&mut self, slice: &[u8]) -> Result<(), ShortBuf> {
2124 self.builder.append_slice(slice).map_err(Into::into)
2125 }
2126
2127 pub fn append_slice(&mut self, mut slice: &[u8]) -> Result<(), ShortBuf> {
2128 if let Some(start) = self.start {
2129 let left = 255 - (self.builder.as_ref().len() - (start + 1));
2130 if slice.len() < left {
2131 self.builder_append_slice(slice)?;
2132 return Ok(());
2133 }
2134 let (append, left) = slice.split_at(left);
2135 self.builder_append_slice(append)?;
2136 self.builder.as_mut()[start] = 255;
2137 slice = left;
2138 }
2139 for chunk in slice.chunks(255) {
2140 if self.builder.as_ref().len() + chunk.len() + 1 >= 0xFFFF {
2141 return Err(ShortBuf);
2142 }
2143 self.start = if chunk.len() == 255 {
2145 None
2146 } else {
2147 Some(self.builder.as_ref().len())
2148 };
2149 self.builder_append_slice(&[chunk.len() as u8])?;
2150 self.builder_append_slice(chunk)?;
2151 }
2152 Ok(())
2153 }
2154
2155 pub fn append_u8(&mut self, ch: u8) -> Result<(), ShortBuf> {
2156 self.append_slice(&[ch])
2157 }
2158
2159 #[cfg(feature = "serde")]
2160 fn append_zone_char_str<E: serde::de::Error>(
2161 &mut self,
2162 s: &str,
2163 ) -> Result<(), E> {
2164 use crate::base::charstr::CharStrError;
2165
2166 self.close_char_str();
2167 self.start = Some(self.builder.as_ref().len());
2168 self.builder_append_slice(&[0]).map_err(E::custom)?;
2169 let mut len = 0;
2170 let mut chars = s.chars();
2171 while let Some(sym) =
2172 Symbol::from_chars(&mut chars).map_err(E::custom)?
2173 {
2174 if len == 255 {
2175 return Err(E::custom(CharStrError));
2176 }
2177 let sym = sym.into_octet().map_err(E::custom)?;
2178 self.builder_append_slice(&[sym]).map_err(E::custom)?;
2179 len += 1;
2180 }
2181 self.close_char_str();
2182 Ok(())
2183 }
2184
2185 fn close_char_str(&mut self) {
2186 if let Some(start) = self.start {
2187 let last_slice_len = self.builder.as_ref().len() - (start + 1);
2188 self.builder.as_mut()[start] = last_slice_len as u8;
2189 }
2190 }
2191
2192 pub fn finish(mut self) -> Txt<Builder::Octets>
2193 where
2194 Builder: FreezeBuilder,
2195 {
2196 self.close_char_str();
2197 Txt(self.builder.freeze())
2198 }
2199}
2200
2201impl<Builder: OctetsBuilder + EmptyBuilder> Default for TxtBuilder<Builder> {
2202 fn default() -> Self {
2203 Self::new()
2204 }
2205}
2206
2207#[derive(Clone, Copy, Debug)]
2213pub struct TxtError(TxtErrorInner);
2214
2215#[derive(Clone, Copy, Debug)]
2216enum TxtErrorInner {
2217 Long(LongRecordData),
2218 ShortInput,
2219}
2220
2221impl TxtError {
2222 #[must_use]
2223 pub fn as_str(self) -> &'static str {
2224 match self.0 {
2225 TxtErrorInner::Long(err) => err.as_str(),
2226 TxtErrorInner::ShortInput => "short input",
2227 }
2228 }
2229}
2230
2231impl From<LongRecordData> for TxtError {
2232 fn from(err: LongRecordData) -> TxtError {
2233 TxtError(TxtErrorInner::Long(err))
2234 }
2235}
2236
2237impl From<TxtError> for FormError {
2238 fn from(err: TxtError) -> FormError {
2239 FormError::new(err.as_str())
2240 }
2241}
2242
2243impl fmt::Display for TxtError {
2244 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2245 f.write_str(self.as_str())
2246 }
2247}
2248
2249#[cfg(test)]
2252#[cfg(all(feature = "std", feature = "bytes"))]
2253mod test {
2254 use super::*;
2255 use crate::base::name::Dname;
2256 use crate::base::rdata::test::{
2257 test_compose_parse, test_rdlen, test_scan,
2258 };
2259 use octseq::octets::OctetsInto;
2260 use std::vec::Vec;
2261
2262 #[test]
2265 fn a_compose_parse_scan() {
2266 let rdata = A::from_octets(1, 2, 3, 4);
2267 test_rdlen(&rdata);
2268 test_compose_parse(&rdata, A::parse);
2269 test_scan(&["1.2.3.4"], A::scan, &rdata);
2270 }
2271
2272 #[test]
2277 #[allow(clippy::redundant_closure)] fn cname_compose_parse_scan() {
2279 let rdata =
2280 Cname::<Dname<Vec<u8>>>::from_str("www.example.com").unwrap();
2281 test_rdlen(&rdata);
2282 test_compose_parse(&rdata, |parser| Cname::parse(parser));
2283 test_scan(&["www.example.com"], Cname::scan, &rdata);
2284 }
2285
2286 #[test]
2289 #[allow(clippy::redundant_closure)] fn hinfo_compose_parse_scan() {
2291 let rdata = Hinfo::new(
2292 CharStr::from_octets("cpu").unwrap(),
2293 CharStr::from_octets("os").unwrap(),
2294 );
2295 test_rdlen(&rdata);
2296 test_compose_parse(&rdata, |parser| Hinfo::parse(parser));
2297 test_scan(&["cpu", "os"], Hinfo::scan, &rdata);
2298 }
2299
2300 #[test]
2301 fn hinfo_octets_into() {
2302 let hinfo: Hinfo<Vec<u8>> =
2303 Hinfo::new("1234".parse().unwrap(), "abcd".parse().unwrap());
2304 let hinfo_bytes: Hinfo<bytes::Bytes> = hinfo.clone().octets_into();
2305 assert_eq!(hinfo.cpu(), hinfo_bytes.cpu());
2306 assert_eq!(hinfo.os(), hinfo_bytes.os());
2307 }
2308
2309 #[test]
2312 #[allow(clippy::redundant_closure)] fn minfo_compose_parse_scan() {
2314 let rdata = Minfo::<Dname<Vec<u8>>>::new(
2315 Dname::from_str("r.example.com").unwrap(),
2316 Dname::from_str("e.example.com").unwrap(),
2317 );
2318 test_rdlen(&rdata);
2319 test_compose_parse(&rdata, |parser| Minfo::parse(parser));
2320 test_scan(&["r.example.com", "e.example.com"], Minfo::scan, &rdata);
2321 }
2322
2323 #[test]
2324 fn minfo_octets_into() {
2325 let minfo: Minfo<Dname<Vec<u8>>> = Minfo::new(
2326 "a.example".parse().unwrap(),
2327 "b.example".parse().unwrap(),
2328 );
2329 let minfo_bytes: Minfo<Dname<bytes::Bytes>> =
2330 minfo.clone().octets_into();
2331 assert_eq!(minfo.rmailbx(), minfo_bytes.rmailbx());
2332 assert_eq!(minfo.emailbx(), minfo_bytes.emailbx());
2333 }
2334
2335 #[test]
2338 #[allow(clippy::redundant_closure)] fn mx_compose_parse_scan() {
2340 let rdata = Mx::<Dname<Vec<u8>>>::new(
2341 12,
2342 Dname::from_str("mail.example.com").unwrap(),
2343 );
2344 test_rdlen(&rdata);
2345 test_compose_parse(&rdata, |parser| Mx::parse(parser));
2346 test_scan(&["12", "mail.example.com"], Mx::scan, &rdata);
2347 }
2348
2349 #[test]
2352 #[allow(clippy::redundant_closure)] fn null_compose_parse_scan() {
2354 let rdata = Null::from_octets("foo").unwrap();
2355 test_rdlen(&rdata);
2356 test_compose_parse(&rdata, |parser| Null::parse(parser));
2357 }
2358
2359 #[test]
2362 #[allow(clippy::redundant_closure)] fn soa_compose_parse_scan() {
2364 let rdata = Soa::<Dname<Vec<u8>>>::new(
2365 Dname::from_str("m.example.com").unwrap(),
2366 Dname::from_str("r.example.com").unwrap(),
2367 Serial(11),
2368 Ttl::from_secs(12),
2369 Ttl::from_secs(13),
2370 Ttl::from_secs(14),
2371 Ttl::from_secs(15),
2372 );
2373 test_rdlen(&rdata);
2374 test_compose_parse(&rdata, |parser| Soa::parse(parser));
2375 test_scan(
2376 &[
2377 "m.example.com",
2378 "r.example.com",
2379 "11",
2380 "12",
2381 "13",
2382 "14",
2383 "15",
2384 ],
2385 Soa::scan,
2386 &rdata,
2387 );
2388 }
2389
2390 #[test]
2393 #[allow(clippy::redundant_closure)] fn txt_compose_parse_scan() {
2395 let rdata = Txt::from_octets(b"\x03foo\x03bar".as_ref()).unwrap();
2396 test_rdlen(&rdata);
2397 test_compose_parse(&rdata, |parser| Txt::parse(parser));
2398 test_scan(&["foo", "bar"], Txt::scan, &rdata);
2399 }
2400
2401 #[test]
2402 fn txt_from_slice() {
2403 let short = b"01234";
2404 let txt: Txt<Vec<u8>> = Txt::build_from_slice(short).unwrap();
2405 assert_eq!(Some(&short[..]), txt.as_flat_slice());
2406 assert_eq!(short.to_vec(), txt.text::<Vec<u8>>());
2407
2408 let full = short.repeat(51);
2410 let txt: Txt<Vec<u8>> = Txt::build_from_slice(&full).unwrap();
2411 assert_eq!(Some(&full[..]), txt.as_flat_slice());
2412 assert_eq!(full.to_vec(), txt.text::<Vec<u8>>());
2413
2414 let long = short.repeat(52);
2416 let txt: Txt<Vec<u8>> = Txt::build_from_slice(&long).unwrap();
2417 assert_eq!(None, txt.as_flat_slice());
2418 assert_eq!(long.to_vec(), txt.text::<Vec<u8>>());
2419
2420 let mut builder: TxtBuilder<Vec<u8>> = TxtBuilder::new();
2422 for chunk in long.chunks(9) {
2423 builder.append_slice(chunk).unwrap();
2424 }
2425 let txt = builder.finish();
2426 assert_eq!(None, txt.as_flat_slice());
2427 assert_eq!(long.to_vec(), txt.text::<Vec<u8>>());
2428
2429 let mut builder: TxtBuilder<Vec<u8>> = TxtBuilder::new();
2431 assert!(builder.append_slice(&[]).is_ok());
2432 let empty = builder.finish();
2433 assert!(empty.is_empty());
2434 assert_eq!(0, empty.iter().count());
2435
2436 let mut parser = Parser::from_static(b"\x01");
2438 assert!(Txt::parse(&mut parser).is_err());
2439
2440 let mut builder: TxtBuilder<Vec<u8>> = TxtBuilder::new();
2442 assert!(builder
2443 .append_slice(&b"\x00".repeat(std::u16::MAX as usize))
2444 .is_err());
2445
2446 let mut builder: TxtBuilder<Vec<u8>> = TxtBuilder::new();
2448 assert!(builder
2449 .append_slice(&b"\x00".repeat(std::u16::MAX as usize - 512))
2450 .is_ok());
2451 assert!(builder.append_slice(&b"\x00".repeat(512)).is_err());
2452 }
2453
2454 #[test]
2455 fn txt_canonical_compare() {
2456 let data = [
2457 "mailru-verification: 14505c6eb222c847",
2458 "yandex-verification: 6059b187e78de544",
2459 "v=spf1 include:_spf.protonmail.ch ~all",
2460 "swisssign-check=CF0JHMTlTDNoES3rrknIRggocffSwqmzMb9X8YbjzK",
2461 "google-site-verification=aq9zJnp3H3bNE0Y4D4rH5I5Dhj8VMaLYx0uQ7Rozfgg",
2462 "ahrefs-site-verification_4bdac6bbaa81e0d591d7c0f3ef238905c0521b69bf3d74e64d3775bcb2743afd",
2463 "brave-ledger-verification=66a7f27fb99949cc0c564ab98efcc58ea1bac3e97eb557c782ab2d44b49aefd7",
2464 ];
2465
2466 let records = data
2467 .iter()
2468 .map(|e| {
2469 let mut builder = TxtBuilder::<Vec<u8>>::new();
2470 builder.append_slice(e.as_bytes()).unwrap();
2471 builder.finish()
2472 })
2473 .collect::<Vec<_>>();
2474
2475 let mut sorted = records.clone();
2477 sorted.sort_by(|a, b| a.canonical_cmp(b));
2478
2479 for (a, b) in records.iter().zip(sorted.iter()) {
2480 assert_eq!(a, b);
2481 }
2482 }
2483
2484 #[cfg(all(feature = "serde", feature = "std"))]
2485 #[test]
2486 fn txt_ser_de() {
2487 use serde_test::{assert_tokens, Configure, Token};
2488
2489 let txt = Txt::from_octets(Vec::from(b"\x03foo".as_ref())).unwrap();
2490 assert_tokens(
2491 &txt.clone().compact(),
2492 &[
2493 Token::NewtypeStruct { name: "Txt" },
2494 Token::ByteBuf(b"\x03foo"),
2495 ],
2496 );
2497 assert_tokens(
2498 &txt.readable(),
2499 &[
2500 Token::NewtypeStruct { name: "Txt" },
2501 Token::Seq { len: None },
2502 Token::BorrowedStr("foo"),
2503 Token::SeqEnd,
2504 ],
2505 );
2506 }
2507
2508 #[cfg(all(feature = "serde", feature = "std"))]
2509 #[test]
2510 fn txt_de_str() {
2511 use serde_test::{assert_de_tokens, Configure, Token};
2512
2513 assert_de_tokens(
2514 &Txt::from_octets(Vec::from(b"\x03foo".as_ref()))
2515 .unwrap()
2516 .readable(),
2517 &[
2518 Token::NewtypeStruct { name: "Txt" },
2519 Token::BorrowedStr("foo"),
2520 ],
2521 );
2522 }
2523}