domain/base/name/
builder.rs

1//! Building a domain name.
2//!
3//! This is a private module for tidiness. `NameBuilder` and `PushError`
4//! are re-exported by the parent module.
5
6use super::super::scan::{BadSymbol, Symbol, SymbolCharsError, Symbols};
7use super::absolute::Name;
8use super::relative::{RelativeName, RelativeNameError};
9use super::traits::{ToName, ToRelativeName};
10use super::Label;
11#[cfg(feature = "bytes")]
12use bytes::BytesMut;
13use core::fmt;
14use octseq::builder::{EmptyBuilder, FreezeBuilder, OctetsBuilder, ShortBuf};
15#[cfg(feature = "std")]
16use std::vec::Vec;
17
18//------------ NameBuilder --------------------------------------------------
19
20/// Builds a domain name step by step by appending data.
21///
22/// The domain name builder is the most fundamental way to construct a new
23/// domain name. It wraps an octets builder that assembles the name step by
24/// step.
25///
26/// The methods [`push`][Self::push] and [`append_slice`][Self::append_slice]
27/// to add the octets of a label to end of the builder. Once a label is
28/// complete, [`end_label`][Self::end_label] finishes the current label and
29/// starts a new one.
30///
31/// The method [`append_label`][Self::append_label] combines this process
32/// and appends the given octets as a label.
33///
34/// The name builder currently is not aware of internationalized domain
35/// names. The octets passed to it are used as is and are not converted.
36#[derive(Clone)]
37pub struct NameBuilder<Builder> {
38    /// The buffer to build the name in.
39    builder: Builder,
40
41    /// The position in `octets` where the current label started.
42    ///
43    /// If this is `None` we currently do not have a label.
44    head: Option<usize>,
45}
46
47impl<Builder> NameBuilder<Builder> {
48    /// Creates a new domain name builder from an octets builder.
49    ///
50    /// Whatever is in the buffer already is considered to be a relative
51    /// domain name. Since that may not be the case, this function is
52    /// unsafe.
53    pub(super) unsafe fn from_builder_unchecked(builder: Builder) -> Self {
54        NameBuilder {
55            builder,
56            head: None,
57        }
58    }
59
60    /// Creates a new, empty name builder.
61    #[must_use]
62    pub fn new() -> Self
63    where
64        Builder: EmptyBuilder,
65    {
66        unsafe { NameBuilder::from_builder_unchecked(Builder::empty()) }
67    }
68
69    /// Creates a new, empty builder with a given capacity.
70    #[must_use]
71    pub fn with_capacity(capacity: usize) -> Self
72    where
73        Builder: EmptyBuilder,
74    {
75        unsafe {
76            NameBuilder::from_builder_unchecked(Builder::with_capacity(
77                capacity,
78            ))
79        }
80    }
81
82    /// Creates a new domain name builder atop an existing octets builder.
83    ///
84    /// The function checks that whatever is in the builder already
85    /// consititutes a correctly encoded relative domain name.
86    pub fn from_builder(builder: Builder) -> Result<Self, RelativeNameError>
87    where
88        Builder: OctetsBuilder + AsRef<[u8]>,
89    {
90        RelativeName::check_slice(builder.as_ref())?;
91        Ok(unsafe { NameBuilder::from_builder_unchecked(builder) })
92    }
93}
94
95#[cfg(feature = "std")]
96impl NameBuilder<Vec<u8>> {
97    /// Creates an empty domain name builder atop a `Vec<u8>`.
98    #[must_use]
99    pub fn new_vec() -> Self {
100        Self::new()
101    }
102
103    /// Creates an empty builder atop a `Vec<u8>` with given capacity.
104    ///
105    /// Names are limited to a length of 255 octets, but you can provide any
106    /// capacity you like here.
107    #[must_use]
108    pub fn vec_with_capacity(capacity: usize) -> Self {
109        Self::with_capacity(capacity)
110    }
111}
112
113#[cfg(feature = "bytes")]
114impl NameBuilder<BytesMut> {
115    /// Creates an empty domain name bulider atop a bytes value.
116    pub fn new_bytes() -> Self {
117        Self::new()
118    }
119
120    /// Creates an empty bulider atop a bytes value with given capacity.
121    ///
122    /// Names are limited to a length of 255 octets, but you can provide any
123    /// capacity you like here.
124    pub fn bytes_with_capacity(capacity: usize) -> Self {
125        Self::with_capacity(capacity)
126    }
127}
128
129impl<Builder: AsRef<[u8]>> NameBuilder<Builder> {
130    /// Returns the already assembled domain name as an octets slice.
131    pub fn as_slice(&self) -> &[u8] {
132        self.builder.as_ref()
133    }
134
135    /// Returns the length of the already assembled domain name.
136    pub fn len(&self) -> usize {
137        self.builder.as_ref().len()
138    }
139
140    /// Returns whether the name is still empty.
141    pub fn is_empty(&self) -> bool {
142        self.builder.as_ref().is_empty()
143    }
144}
145
146impl<Builder> NameBuilder<Builder>
147where
148    Builder: OctetsBuilder + AsRef<[u8]> + AsMut<[u8]>,
149{
150    /// Returns whether there currently is a label under construction.
151    ///
152    /// This returns `false` if the name is still empty or if the last thing
153    /// that happend was a call to [`end_label`].
154    ///
155    /// [`end_label`]: NameBuilder::end_label
156    pub fn in_label(&self) -> bool {
157        self.head.is_some()
158    }
159
160    /// Attempts to append a slice to the underlying builder.
161    ///
162    /// This method doesn’t perform any checks but only does the necessary
163    /// error conversion.
164    fn _append_slice(&mut self, slice: &[u8]) -> Result<(), PushError> {
165        self.builder
166            .append_slice(slice)
167            .map_err(|_| PushError::ShortBuf)
168    }
169
170    /// Pushes an octet to the end of the domain name.
171    ///
172    /// Starts a new label if necessary. Returns an error if pushing the
173    /// octet would exceed the size limits for labels or domain names.
174    pub fn push(&mut self, ch: u8) -> Result<(), PushError> {
175        let len = self.len();
176        if len >= 254 {
177            return Err(PushError::LongName);
178        }
179        if let Some(head) = self.head {
180            if len - head > Label::MAX_LEN {
181                return Err(PushError::LongLabel);
182            }
183            self._append_slice(&[ch])?;
184        } else {
185            self.head = Some(len);
186            self._append_slice(&[0, ch])?;
187        }
188        Ok(())
189    }
190
191    /// Pushes a symbol to the end of the domain name.
192    ///
193    /// The symbol is iterpreted as part of the presentation format of a
194    /// domain name, i.e., an unescaped dot is considered a label separator.
195    pub fn push_symbol(&mut self, sym: Symbol) -> Result<(), FromStrError> {
196        if matches!(sym, Symbol::Char('.')) {
197            if !self.in_label() {
198                return Err(PresentationErrorEnum::EmptyLabel.into());
199            }
200            self.end_label();
201            Ok(())
202        } else if matches!(sym, Symbol::SimpleEscape(b'['))
203            && !self.in_label()
204        {
205            Err(LabelFromStrErrorEnum::BinaryLabel.into())
206        } else {
207            self.push(sym.into_octet()?).map_err(Into::into)
208        }
209    }
210
211    /// Appends the content of an octets slice to the end of the domain name.
212    ///
213    /// Starts a new label if necessary. Returns an error if pushing
214    /// would exceed the size limits for labels or domain names.
215    ///
216    /// If `slice` is empty, does absolutely nothing.
217    pub fn append_slice(&mut self, slice: &[u8]) -> Result<(), PushError> {
218        if slice.is_empty() {
219            return Ok(());
220        }
221        if let Some(head) = self.head {
222            if slice.len() > Label::MAX_LEN - (self.len() - head) {
223                return Err(PushError::LongLabel);
224            }
225        } else {
226            if slice.len() > Label::MAX_LEN {
227                return Err(PushError::LongLabel);
228            }
229            if self.len() + slice.len() > 254 {
230                return Err(PushError::LongName);
231            }
232            self.head = Some(self.len());
233            self._append_slice(&[0])?;
234        }
235        self._append_slice(slice)?;
236        Ok(())
237    }
238
239    /// Ends the current label.
240    ///
241    /// If there isn’t a current label, does nothing.
242    pub fn end_label(&mut self) {
243        if let Some(head) = self.head {
244            let len = self.len() - head - 1;
245            self.builder.as_mut()[head] = len as u8;
246            self.head = None;
247        }
248    }
249
250    /// Appends an octets slice as a complete label.
251    ///
252    /// If there currently is a label under construction, it will be ended
253    /// before appending `label`.
254    ///
255    /// Returns an error if `label` exceeds the label size limit of 63 bytes
256    /// or appending the label would exceed the domain name size limit of
257    /// 255 bytes.
258    pub fn append_label(&mut self, label: &[u8]) -> Result<(), PushError> {
259        let head = self.head;
260        self.end_label();
261        if let Err(err) = self.append_slice(label) {
262            self.head = head;
263            return Err(err);
264        }
265        self.end_label();
266        Ok(())
267    }
268
269    /// Appends a label with the decimal representation of `u8`.
270    ///
271    /// If there currently is a label under construction, it will be ended
272    /// before appending `label`.
273    ///
274    /// Returns an error if appending would result in a name longer than 254
275    /// bytes.
276    pub fn append_dec_u8_label(
277        &mut self,
278        value: u8,
279    ) -> Result<(), PushError> {
280        self.end_label();
281        let hecto = value / 100;
282        if hecto > 0 {
283            self.push(hecto + b'0')?;
284        }
285        let deka = (value / 10) % 10;
286        if hecto > 0 || deka > 0 {
287            self.push(deka + b'0')?;
288        }
289        self.push(value % 10 + b'0')?;
290        self.end_label();
291        Ok(())
292    }
293
294    /// Appends a label with the hex digit.
295    ///
296    /// If there currently is a label under construction, it will be ended
297    /// before appending `label`.
298    ///
299    /// Returns an error if appending would result in a name longer than 254
300    /// bytes.
301    pub fn append_hex_digit_label(
302        &mut self,
303        nibble: u8,
304    ) -> Result<(), PushError> {
305        fn hex_digit(nibble: u8) -> u8 {
306            match nibble & 0x0F {
307                0 => b'0',
308                1 => b'1',
309                2 => b'2',
310                3 => b'3',
311                4 => b'4',
312                5 => b'5',
313                6 => b'6',
314                7 => b'7',
315                8 => b'8',
316                9 => b'9',
317                10 => b'A',
318                11 => b'B',
319                12 => b'C',
320                13 => b'D',
321                14 => b'E',
322                15 => b'F',
323                _ => unreachable!(),
324            }
325        }
326
327        self.end_label();
328        self.push(hex_digit(nibble))?;
329        self.end_label();
330        Ok(())
331    }
332
333    /// Appends a relative domain name.
334    ///
335    /// If there currently is a label under construction, it will be ended
336    /// before appending `name`.
337    ///
338    /// Returns an error if appending would result in a name longer than 254
339    /// bytes.
340    //
341    //  XXX NEEDS TESTS
342    pub fn append_name<N: ToRelativeName>(
343        &mut self,
344        name: &N,
345    ) -> Result<(), PushNameError> {
346        let head = self.head.take();
347        self.end_label();
348        if self.len() + usize::from(name.compose_len()) > 254 {
349            self.head = head;
350            return Err(PushNameError::LongName);
351        }
352        for label in name.iter_labels() {
353            label
354                .compose(&mut self.builder)
355                .map_err(|_| PushNameError::ShortBuf)?;
356        }
357        Ok(())
358    }
359
360    /// Appends a name from a sequence of symbols.
361    ///
362    /// If there currently is a label under construction, it will be ended
363    /// before appending `chars`.
364    ///
365    /// The character sequence must result in a domain name in representation
366    /// format. That is, its labels should be separated by dots,
367    /// actual dots, white space, backslashes  and byte values that are not
368    /// printable ASCII characters should be escaped.
369    ///
370    /// The last label will only be ended if the last character was a dot.
371    /// Thus, you can determine if that was the case via
372    /// [`in_label`][Self::in_label].
373    pub fn append_symbols<Sym: IntoIterator<Item = Symbol>>(
374        &mut self,
375        symbols: Sym,
376    ) -> Result<(), FromStrError> {
377        symbols
378            .into_iter()
379            .try_for_each(|symbol| self.push_symbol(symbol))
380    }
381
382    /// Appends a name from a sequence of characters.
383    ///
384    /// If there currently is a label under construction, it will be ended
385    /// before appending `chars`.
386    ///
387    /// The character sequence must result in a domain name in representation
388    /// format. That is, its labels should be separated by dots,
389    /// actual dots, white space and backslashes should be escaped by a
390    /// preceeding backslash, and any byte value that is not a printable
391    /// ASCII character should be encoded by a backslash followed by its
392    /// three digit decimal value.
393    ///
394    /// The last label will only be ended if the last character was a dot.
395    /// Thus, you can determine if that was the case via
396    /// [`in_label`][Self::in_label].
397    pub fn append_chars<C: IntoIterator<Item = char>>(
398        &mut self,
399        chars: C,
400    ) -> Result<(), FromStrError> {
401        Symbols::with(chars.into_iter(), |symbols| {
402            self.append_symbols(symbols)
403        })
404    }
405
406    /// Finishes building the name and returns the resulting relative name.
407    ///
408    /// If there currently is a label being built, ends the label first
409    /// before returning the name. I.e., you don’t have to call [`end_label`]
410    /// explicitely.
411    ///
412    /// This method converts the builder into a relative name. If you would
413    /// like to turn it into an absolute name, use [`into_name`] which
414    /// appends the root label before finishing.
415    ///
416    /// [`end_label`]: NameBuilder::end_label
417    /// [`into_name`]: NameBuilder::into_name
418    pub fn finish(mut self) -> RelativeName<Builder::Octets>
419    where
420        Builder: FreezeBuilder,
421    {
422        self.end_label();
423        unsafe { RelativeName::from_octets_unchecked(self.builder.freeze()) }
424    }
425
426    /// Appends the root label to the name and returns it as a [`Name`].
427    ///
428    /// If there currently is a label under construction, ends the label.
429    /// Then adds the empty root label and transforms the name into a
430    /// [`Name`].
431    pub fn into_name(mut self) -> Result<Name<Builder::Octets>, PushError>
432    where
433        Builder: FreezeBuilder,
434    {
435        self.end_label();
436        self._append_slice(&[0])?;
437        Ok(unsafe { Name::from_octets_unchecked(self.builder.freeze()) })
438    }
439
440    /// Appends an origin and returns the resulting [`Name`].
441    ///
442    /// If there currently is a label under construction, ends the label.
443    /// Then adds the `origin` and transforms the name into a
444    /// [`Name`].
445    //
446    //  XXX NEEDS TESTS
447    pub fn append_origin<N: ToName>(
448        mut self,
449        origin: &N,
450    ) -> Result<Name<Builder::Octets>, PushNameError>
451    where
452        Builder: FreezeBuilder,
453    {
454        self.end_label();
455        if self.len() + usize::from(origin.compose_len()) > Name::MAX_LEN {
456            return Err(PushNameError::LongName);
457        }
458        for label in origin.iter_labels() {
459            label
460                .compose(&mut self.builder)
461                .map_err(|_| PushNameError::ShortBuf)?;
462        }
463        Ok(unsafe { Name::from_octets_unchecked(self.builder.freeze()) })
464    }
465}
466
467//--- Default
468
469impl<Builder: EmptyBuilder> Default for NameBuilder<Builder> {
470    fn default() -> Self {
471        Self::new()
472    }
473}
474
475//--- AsRef
476
477impl<Builder: AsRef<[u8]>> AsRef<[u8]> for NameBuilder<Builder> {
478    fn as_ref(&self) -> &[u8] {
479        self.builder.as_ref()
480    }
481}
482
483//------------ Santa’s Little Helpers ----------------------------------------
484
485/// Parses the contents of an escape sequence from `chars`.
486///
487/// The backslash should already have been taken out of `chars`.
488pub(super) fn parse_escape<C>(
489    chars: &mut C,
490    in_label: bool,
491) -> Result<u8, LabelFromStrError>
492where
493    C: Iterator<Item = char>,
494{
495    let ch = chars.next().ok_or(SymbolCharsError::short_input())?;
496    if ch.is_ascii_digit() {
497        let v = ch.to_digit(10).unwrap() * 100
498            + chars
499                .next()
500                .ok_or(SymbolCharsError::short_input())
501                .and_then(|c| {
502                    c.to_digit(10).ok_or(SymbolCharsError::bad_escape())
503                })?
504                * 10
505            + chars
506                .next()
507                .ok_or(SymbolCharsError::short_input())
508                .and_then(|c| {
509                    c.to_digit(10).ok_or(SymbolCharsError::bad_escape())
510                })?;
511        if v > 255 {
512            return Err(SymbolCharsError::bad_escape().into());
513        }
514        Ok(v as u8)
515    } else if ch == '[' {
516        // `\[` at the start of a label marks a binary label which we don’t
517        // support. Within a label, the sequence is fine.
518        if in_label {
519            Ok(b'[')
520        } else {
521            Err(LabelFromStrErrorEnum::BinaryLabel.into())
522        }
523    } else {
524        Ok(ch as u8)
525    }
526}
527
528//============ Error Types ===================================================
529
530//------------ PushError -----------------------------------------------------
531
532/// An error happened while trying to push data to a domain name builder.
533#[derive(Clone, Copy, Debug, Eq, PartialEq)]
534pub enum PushError {
535    /// The current label would exceed the limit of 63 bytes.
536    LongLabel,
537
538    /// The name would exceed the limit of 255 bytes.
539    LongName,
540
541    /// The buffer is too short to contain the name.
542    ShortBuf,
543}
544
545//--- From
546
547impl From<ShortBuf> for PushError {
548    fn from(_: ShortBuf) -> PushError {
549        PushError::ShortBuf
550    }
551}
552
553//--- Display and Error
554
555impl fmt::Display for PushError {
556    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
557        match *self {
558            PushError::LongLabel => f.write_str("long label"),
559            PushError::LongName => f.write_str("long domain name"),
560            PushError::ShortBuf => ShortBuf.fmt(f),
561        }
562    }
563}
564
565#[cfg(feature = "std")]
566impl std::error::Error for PushError {}
567
568//------------ PushNameError -------------------------------------------------
569
570/// An error happened while trying to push a name to a domain name builder.
571#[derive(Clone, Copy, Debug, Eq, PartialEq)]
572pub enum PushNameError {
573    /// The name would exceed the limit of 255 bytes.
574    LongName,
575
576    /// The buffer is too short to contain the name.
577    ShortBuf,
578}
579
580//--- From
581
582impl From<ShortBuf> for PushNameError {
583    fn from(_: ShortBuf) -> Self {
584        PushNameError::ShortBuf
585    }
586}
587
588//--- Display and Error
589
590impl fmt::Display for PushNameError {
591    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
592        match *self {
593            PushNameError::LongName => f.write_str("long domain name"),
594            PushNameError::ShortBuf => ShortBuf.fmt(f),
595        }
596    }
597}
598
599#[cfg(feature = "std")]
600impl std::error::Error for PushNameError {}
601
602//------------ LabelFromStrError ---------------------------------------------
603
604/// An error occured while reading a label from a string.
605#[derive(Clone, Copy, Debug, Eq, PartialEq)]
606pub struct LabelFromStrError(LabelFromStrErrorEnum);
607
608#[derive(Clone, Copy, Debug, Eq, PartialEq)]
609pub(super) enum LabelFromStrErrorEnum {
610    SymbolChars(SymbolCharsError),
611
612    BadSymbol(BadSymbol),
613
614    /// A binary label was encountered.
615    BinaryLabel,
616
617    /// The label would exceed the limit of 63 bytes.
618    LongLabel,
619}
620
621//--- From
622
623impl From<LabelFromStrErrorEnum> for LabelFromStrError {
624    fn from(inner: LabelFromStrErrorEnum) -> Self {
625        Self(inner)
626    }
627}
628
629impl From<SymbolCharsError> for LabelFromStrError {
630    fn from(err: SymbolCharsError) -> Self {
631        Self(LabelFromStrErrorEnum::SymbolChars(err))
632    }
633}
634
635impl From<BadSymbol> for LabelFromStrError {
636    fn from(err: BadSymbol) -> Self {
637        Self(LabelFromStrErrorEnum::BadSymbol(err))
638    }
639}
640
641//--- Display and Error
642
643impl fmt::Display for LabelFromStrError {
644    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
645        match self.0 {
646            LabelFromStrErrorEnum::SymbolChars(err) => err.fmt(f),
647            LabelFromStrErrorEnum::BadSymbol(err) => err.fmt(f),
648            LabelFromStrErrorEnum::BinaryLabel => {
649                f.write_str("a binary label was encountered")
650            }
651            LabelFromStrErrorEnum::LongLabel => {
652                f.write_str("label length limit exceeded")
653            }
654        }
655    }
656}
657
658#[cfg(feature = "std")]
659impl std::error::Error for LabelFromStrError {}
660
661//------------ FromStrError --------------------------------------------------
662
663#[derive(Clone, Copy, Debug, Eq, PartialEq)]
664pub enum FromStrError {
665    /// The string content was wrongly formatted.
666    Presentation(PresentationError),
667
668    /// The buffer is too short to contain the name.
669    ShortBuf,
670}
671
672impl FromStrError {
673    pub(super) fn empty_label() -> Self {
674        Self::Presentation(PresentationErrorEnum::EmptyLabel.into())
675    }
676}
677
678//--- From
679
680impl From<PushError> for FromStrError {
681    fn from(err: PushError) -> FromStrError {
682        match err {
683            PushError::LongLabel => LabelFromStrErrorEnum::LongLabel.into(),
684            PushError::LongName => PresentationErrorEnum::LongName.into(),
685            PushError::ShortBuf => FromStrError::ShortBuf,
686        }
687    }
688}
689
690impl From<PushNameError> for FromStrError {
691    fn from(err: PushNameError) -> FromStrError {
692        match err {
693            PushNameError::LongName => PresentationErrorEnum::LongName.into(),
694            PushNameError::ShortBuf => FromStrError::ShortBuf,
695        }
696    }
697}
698
699impl<T: Into<PresentationError>> From<T> for FromStrError {
700    fn from(err: T) -> Self {
701        Self::Presentation(err.into())
702    }
703}
704
705//--- Display and Error
706
707impl fmt::Display for FromStrError {
708    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
709        match *self {
710            FromStrError::Presentation(err) => err.fmt(f),
711            FromStrError::ShortBuf => ShortBuf.fmt(f),
712        }
713    }
714}
715
716#[cfg(feature = "std")]
717impl std::error::Error for FromStrError {}
718
719//------------ PresentationError ---------------------------------------------
720
721/// An illegal presentation format was encountered.
722#[derive(Clone, Copy, Debug, Eq, PartialEq)]
723pub struct PresentationError(PresentationErrorEnum);
724
725#[derive(Clone, Copy, Debug, Eq, PartialEq)]
726enum PresentationErrorEnum {
727    BadLabel(LabelFromStrError),
728
729    /// An empty label was encountered.
730    EmptyLabel,
731
732    /// The name has more than 255 characters.
733    LongName,
734}
735
736//--- From
737
738impl From<PresentationErrorEnum> for PresentationError {
739    fn from(err: PresentationErrorEnum) -> Self {
740        Self(err)
741    }
742}
743
744impl<T: Into<LabelFromStrError>> From<T> for PresentationError {
745    fn from(err: T) -> Self {
746        Self(PresentationErrorEnum::BadLabel(err.into()))
747    }
748}
749
750//--- Display and Error
751
752impl fmt::Display for PresentationError {
753    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
754        match self.0 {
755            PresentationErrorEnum::BadLabel(ref err) => err.fmt(f),
756            PresentationErrorEnum::EmptyLabel => f.write_str("empty label"),
757            PresentationErrorEnum::LongName => {
758                f.write_str("long domain name")
759            }
760        }
761    }
762}
763
764#[cfg(feature = "std")]
765impl std::error::Error for PresentationError {}
766
767//============ Testing =======================================================
768
769#[cfg(test)]
770#[cfg(feature = "std")]
771mod test {
772    use super::*;
773
774    #[test]
775    fn compose() {
776        let mut builder = NameBuilder::new_vec();
777        builder.push(b'w').unwrap();
778        builder.append_slice(b"ww").unwrap();
779        builder.end_label();
780        builder.append_slice(b"exa").unwrap();
781        builder.push(b'm').unwrap();
782        builder.push(b'p').unwrap();
783        builder.append_slice(b"le").unwrap();
784        builder.end_label();
785        builder.append_slice(b"com").unwrap();
786        assert_eq!(builder.finish().as_slice(), b"\x03www\x07example\x03com");
787    }
788
789    #[test]
790    fn build_by_label() {
791        let mut builder = NameBuilder::new_vec();
792        builder.append_label(b"www").unwrap();
793        builder.append_label(b"example").unwrap();
794        builder.append_label(b"com").unwrap();
795        assert_eq!(builder.finish().as_slice(), b"\x03www\x07example\x03com");
796    }
797
798    #[test]
799    fn build_mixed() {
800        let mut builder = NameBuilder::new_vec();
801        builder.push(b'w').unwrap();
802        builder.append_slice(b"ww").unwrap();
803        builder.append_label(b"example").unwrap();
804        builder.append_slice(b"com").unwrap();
805        assert_eq!(builder.finish().as_slice(), b"\x03www\x07example\x03com");
806    }
807
808    #[test]
809    fn name_limit() {
810        let mut builder = NameBuilder::new_vec();
811        for _ in 0..25 {
812            // 9 bytes label is 10 bytes in total
813            builder.append_label(b"123456789").unwrap();
814        }
815
816        assert_eq!(builder.append_label(b"12345"), Err(PushError::LongName));
817        assert_eq!(builder.clone().append_label(b"1234"), Ok(()));
818
819        assert_eq!(builder.append_slice(b"12345"), Err(PushError::LongName));
820        assert_eq!(builder.clone().append_slice(b"1234"), Ok(()));
821
822        assert_eq!(builder.append_slice(b"12"), Ok(()));
823        assert_eq!(builder.push(b'3'), Ok(()));
824        assert_eq!(builder.push(b'4'), Err(PushError::LongName))
825    }
826
827    #[test]
828    fn label_limit() {
829        let mut builder = NameBuilder::new_vec();
830        builder.append_label(&[0u8; 63][..]).unwrap();
831        assert_eq!(
832            builder.append_label(&[0u8; 64][..]),
833            Err(PushError::LongLabel)
834        );
835        assert_eq!(
836            builder.append_label(&[0u8; 164][..]),
837            Err(PushError::LongLabel)
838        );
839
840        builder.append_slice(&[0u8; 60][..]).unwrap();
841        builder.clone().append_label(b"123").unwrap();
842        assert_eq!(builder.append_slice(b"1234"), Err(PushError::LongLabel));
843        builder.append_slice(b"12").unwrap();
844        builder.push(b'3').unwrap();
845        assert_eq!(builder.push(b'4'), Err(PushError::LongLabel));
846    }
847
848    #[test]
849    fn finish() {
850        let mut builder = NameBuilder::new_vec();
851        builder.append_label(b"www").unwrap();
852        builder.append_label(b"example").unwrap();
853        builder.append_slice(b"com").unwrap();
854        assert_eq!(builder.finish().as_slice(), b"\x03www\x07example\x03com");
855    }
856
857    #[test]
858    fn into_name() {
859        let mut builder = NameBuilder::new_vec();
860        builder.append_label(b"www").unwrap();
861        builder.append_label(b"example").unwrap();
862        builder.append_slice(b"com").unwrap();
863        assert_eq!(
864            builder.into_name().unwrap().as_slice(),
865            b"\x03www\x07example\x03com\x00"
866        );
867    }
868}