ndn_packet/
name.rs

1use core::str::FromStr;
2
3#[cfg(not(feature = "std"))]
4use alloc::vec::Vec;
5
6use bytes::Bytes;
7use smallvec::SmallVec;
8
9use crate::PacketError;
10use crate::tlv_type;
11use ndn_tlv::TlvReader;
12
13/// A single NDN name component: a (type, value) pair.
14///
15/// The value is a zero-copy slice of the original packet buffer.
16///
17/// Ordering follows the NDN Packet Format v0.3 §2.1 canonical order:
18/// TLV-TYPE first, then TLV-LENGTH (shorter is smaller), then TLV-VALUE
19/// byte-by-byte. This matches the order used by NFD and ndn-cxx.
20#[derive(Clone, Debug, PartialEq, Eq, Hash)]
21pub struct NameComponent {
22    pub typ: u64,
23    pub value: Bytes,
24}
25
26impl PartialOrd for NameComponent {
27    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
28        Some(self.cmp(other))
29    }
30}
31
32impl Ord for NameComponent {
33    /// NDN canonical component ordering (NDN Packet Format v0.3 §2.1).
34    ///
35    /// Order: TLV-TYPE ascending, then TLV-LENGTH ascending (shorter is
36    /// smaller), then TLV-VALUE byte-by-byte ascending.
37    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
38        self.typ
39            .cmp(&other.typ)
40            .then_with(|| self.value.len().cmp(&other.value.len()))
41            .then_with(|| self.value.as_ref().cmp(other.value.as_ref()))
42    }
43}
44
45impl NameComponent {
46    pub fn new(typ: u64, value: Bytes) -> Self {
47        Self { typ, value }
48    }
49
50    pub fn generic(value: Bytes) -> Self {
51        Self {
52            typ: tlv_type::NAME_COMPONENT,
53            value,
54        }
55    }
56
57    /// Create a BLAKE3 content-digest component (type 0x03, 32 bytes).
58    ///
59    /// **Experimental / NDA extension.** The 32-byte BLAKE3 hash of a block's
60    /// content forms a self-certifying name component in NDA zones.  The actual
61    /// BLAKE3 computation must be performed by the caller.
62    pub fn blake3_digest(hash: [u8; 32]) -> Self {
63        Self::new(tlv_type::BLAKE3_DIGEST, Bytes::copy_from_slice(&hash))
64    }
65
66    /// Extract the 32-byte BLAKE3 digest value, or `None` if this is not a
67    /// `BLAKE3_DIGEST` (type 0x03) component or the value is not 32 bytes.
68    pub fn as_blake3_digest(&self) -> Option<[u8; 32]> {
69        if self.typ == tlv_type::BLAKE3_DIGEST && self.value.len() == 32 {
70            let mut arr = [0u8; 32];
71            arr.copy_from_slice(&self.value);
72            Some(arr)
73        } else {
74            None
75        }
76    }
77
78    /// Create a Keyword component (type 0x20) with opaque bytes.
79    pub fn keyword(value: Bytes) -> Self {
80        Self::new(tlv_type::KEYWORD, value)
81    }
82
83    /// Create a ByteOffset component (type 0x34), big-endian with leading zeros stripped.
84    pub fn byte_offset(offset: u64) -> Self {
85        Self::new(tlv_type::BYTE_OFFSET, encode_nonnegtive_integer(offset))
86    }
87
88    /// Create a Version component (type 0x36), big-endian with leading zeros stripped.
89    pub fn version(v: u64) -> Self {
90        Self::new(tlv_type::VERSION, encode_nonnegtive_integer(v))
91    }
92
93    /// Create a Timestamp component (type 0x38), big-endian with leading zeros stripped.
94    pub fn timestamp(ts: u64) -> Self {
95        Self::new(tlv_type::TIMESTAMP, encode_nonnegtive_integer(ts))
96    }
97
98    /// Create a SequenceNum component (type 0x3A), big-endian with leading zeros stripped.
99    pub fn sequence_num(seq: u64) -> Self {
100        Self::new(tlv_type::SEQUENCE_NUM, encode_nonnegtive_integer(seq))
101    }
102
103    /// Decode a Segment component value as u64. Returns `None` if not type 0x32.
104    pub fn as_segment(&self) -> Option<u64> {
105        if self.typ == tlv_type::SEGMENT {
106            Some(decode_nonnegative_integer(&self.value))
107        } else {
108            None
109        }
110    }
111
112    /// Decode a ByteOffset component value as u64. Returns `None` if not type 0x34.
113    pub fn as_byte_offset(&self) -> Option<u64> {
114        if self.typ == tlv_type::BYTE_OFFSET {
115            Some(decode_nonnegative_integer(&self.value))
116        } else {
117            None
118        }
119    }
120
121    /// Decode a Version component value as u64. Returns `None` if not type 0x36.
122    pub fn as_version(&self) -> Option<u64> {
123        if self.typ == tlv_type::VERSION {
124            Some(decode_nonnegative_integer(&self.value))
125        } else {
126            None
127        }
128    }
129
130    /// Decode a Timestamp component value as u64. Returns `None` if not type 0x38.
131    pub fn as_timestamp(&self) -> Option<u64> {
132        if self.typ == tlv_type::TIMESTAMP {
133            Some(decode_nonnegative_integer(&self.value))
134        } else {
135            None
136        }
137    }
138
139    /// Decode a SequenceNum component value as u64. Returns `None` if not type 0x3A.
140    pub fn as_sequence_num(&self) -> Option<u64> {
141        if self.typ == tlv_type::SEQUENCE_NUM {
142            Some(decode_nonnegative_integer(&self.value))
143        } else {
144            None
145        }
146    }
147}
148
149/// Encode a u64 as an NDN TLV NonNegativeInteger.
150///
151/// NDN TLV NonNegativeInteger uses the minimum number of bytes from the set
152/// {1, 2, 4, 8} that can represent the value (big-endian). Arbitrary-length
153/// encodings are NOT valid per the NDN Packet Format spec, and interoperability
154/// with NDNts and ndn-cxx requires strict compliance.
155fn encode_nonnegtive_integer(v: u64) -> Bytes {
156    let b = v.to_be_bytes();
157    Bytes::copy_from_slice(match v {
158        0..=0xFF => &b[7..],
159        0x100..=0xFFFF => &b[6..],
160        0x10000..=0xFFFF_FFFF => &b[4..],
161        _ => &b,
162    })
163}
164
165/// Decode big-endian stripped bytes back to u64.
166fn decode_nonnegative_integer(bytes: &[u8]) -> u64 {
167    let mut val: u64 = 0;
168    for &b in bytes {
169        val = (val << 8) | u64::from(b);
170    }
171    val
172}
173
174/// An NDN name: an ordered sequence of name components.
175///
176/// Components are stored in a `SmallVec` with inline capacity for 8 elements,
177/// covering typical 4–8 component names without heap allocation.
178///
179/// Ordering follows the NDN Packet Format v0.3 §2.1 canonical order,
180/// component by component using [`NameComponent`]'s `Ord` impl.
181#[derive(Clone, Debug, PartialEq, Eq, Hash)]
182pub struct Name {
183    components: SmallVec<[NameComponent; 8]>,
184}
185
186impl PartialOrd for Name {
187    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
188        Some(self.cmp(other))
189    }
190}
191
192impl Ord for Name {
193    /// NDN canonical name ordering (NDN Packet Format v0.3 §2.1).
194    ///
195    /// Names are compared component by component from left to right.
196    /// If all shared components are equal, the shorter name is smaller
197    /// (prefix ordering).
198    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
199        self.components.iter().cmp(other.components.iter())
200    }
201}
202
203impl Name {
204    /// The root (empty) name `/`.
205    pub fn root() -> Self {
206        Self {
207            components: SmallVec::new(),
208        }
209    }
210
211    pub fn from_components(components: impl IntoIterator<Item = NameComponent>) -> Self {
212        Self {
213            components: components.into_iter().collect(),
214        }
215    }
216
217    pub fn components(&self) -> &[NameComponent] {
218        &self.components
219    }
220
221    pub fn len(&self) -> usize {
222        self.components.len()
223    }
224
225    pub fn is_empty(&self) -> bool {
226        self.components.is_empty()
227    }
228
229    /// Returns `true` if `prefix` is a prefix of (or equal to) this name.
230    pub fn has_prefix(&self, prefix: &Name) -> bool {
231        if prefix.len() > self.len() {
232            return false;
233        }
234        self.components
235            .iter()
236            .zip(prefix.components.iter())
237            .all(|(a, b)| a == b)
238    }
239
240    /// Decode a `Name` TLV from `reader`. The reader must be positioned at the
241    /// start of the Name value (after the outer type+length have been consumed).
242    pub fn decode(value: Bytes) -> Result<Self, PacketError> {
243        let mut reader = TlvReader::new(value);
244        let mut components = SmallVec::new();
245        while !reader.is_empty() {
246            let (typ, val) = reader.read_tlv()?;
247            components.push(NameComponent::new(typ, val));
248        }
249        Ok(Self { components })
250    }
251
252    // ── Builder methods ──────────────────────────────────────────────────────
253
254    /// Append a generic component from raw bytes.
255    pub fn append(mut self, value: impl AsRef<[u8]>) -> Self {
256        self.components
257            .push(NameComponent::generic(Bytes::copy_from_slice(
258                value.as_ref(),
259            )));
260        self
261    }
262
263    /// Append an already-constructed component.
264    pub fn append_component(mut self, comp: NameComponent) -> Self {
265        self.components.push(comp);
266        self
267    }
268
269    /// Append a segment number component (type `0x32`, big-endian encoding with
270    /// leading zeros stripped per NDN naming conventions).
271    pub fn append_segment(self, seg: u64) -> Self {
272        self.append_component(NameComponent::new(
273            tlv_type::SEGMENT,
274            encode_nonnegtive_integer(seg),
275        ))
276    }
277
278    /// Append a Version component (type 0x36).
279    pub fn append_version(self, v: u64) -> Self {
280        self.append_component(NameComponent::version(v))
281    }
282
283    /// Append a Timestamp component (type 0x38).
284    pub fn append_timestamp(self, ts: u64) -> Self {
285        self.append_component(NameComponent::timestamp(ts))
286    }
287
288    /// Append a SequenceNum component (type 0x3A).
289    pub fn append_sequence_num(self, seq: u64) -> Self {
290        self.append_component(NameComponent::sequence_num(seq))
291    }
292
293    /// Append a ByteOffset component (type 0x34).
294    pub fn append_byte_offset(self, off: u64) -> Self {
295        self.append_component(NameComponent::byte_offset(off))
296    }
297
298    /// Append a BLAKE3 digest component (type 0x03, 32 bytes).
299    ///
300    /// **Experimental / NDA extension.** See [`NameComponent::blake3_digest`].
301    pub fn append_blake3_digest(self, hash: [u8; 32]) -> Self {
302        self.append_component(NameComponent::blake3_digest(hash))
303    }
304
305    /// Build a single-component zone-root name from a pre-computed 32-byte hash.
306    ///
307    /// The hash should be the BLAKE3 digest of a public key (or any 32-byte
308    /// identifier). This is the low-level constructor; use
309    /// `ndn_security::ZoneKey::zone_root_name()` for the full API that computes
310    /// the hash from a public key.
311    ///
312    /// **Experimental / NDA extension** — BLAKE3 component type (0x03) is not
313    /// yet in the NDN Packet Format specification.
314    pub fn zone_root_from_hash(hash: [u8; 32]) -> Self {
315        Name::from_components([NameComponent::blake3_digest(hash)])
316    }
317
318    /// Returns `true` if this name is a single BLAKE3-digest component (a zone root).
319    pub fn is_zone_root(&self) -> bool {
320        self.components().len() == 1
321            && self.components()[0].typ == crate::tlv_type::BLAKE3_DIGEST
322            && self.components()[0].value.len() == 32
323    }
324}
325
326impl FromStr for Name {
327    type Err = PacketError;
328
329    /// Parse an NDN URI string into a `Name`.
330    ///
331    /// Handles percent-decoding to roundtrip with `Display`.
332    ///
333    /// ```
334    /// # use ndn_packet::Name;
335    /// let name: Name = "/edu/ucla/data".parse().unwrap();
336    /// assert_eq!(name.to_string(), "/edu/ucla/data");
337    /// ```
338    fn from_str(s: &str) -> Result<Self, Self::Err> {
339        let s = s.trim();
340        if s.is_empty() || s == "/" {
341            return Ok(Self::root());
342        }
343
344        // Must start with '/'.
345        if !s.starts_with('/') {
346            return Err(PacketError::MalformedPacket(
347                "name must start with '/'".into(),
348            ));
349        }
350
351        let mut components = SmallVec::new();
352        for part in s[1..].split('/') {
353            if part.is_empty() {
354                continue; // tolerate trailing slash
355            }
356            // Typed component prefixes — mirrors the Display impl for roundtrip.
357            let comp = if let Some(rest) = part.strip_prefix("seg=") {
358                if let Ok(n) = rest.parse::<u64>() {
359                    NameComponent::new(tlv_type::SEGMENT, encode_nonnegtive_integer(n))
360                } else {
361                    NameComponent::generic(Bytes::from(percent_decode(part).map_err(|_| {
362                        PacketError::MalformedPacket("invalid percent-encoding in name".into())
363                    })?))
364                }
365            } else if let Some(rest) = part.strip_prefix("v=") {
366                if let Ok(n) = rest.parse::<u64>() {
367                    NameComponent::new(tlv_type::VERSION, encode_nonnegtive_integer(n))
368                } else {
369                    NameComponent::generic(Bytes::from(percent_decode(part).map_err(|_| {
370                        PacketError::MalformedPacket("invalid percent-encoding in name".into())
371                    })?))
372                }
373            } else if let Some(rest) = part.strip_prefix("off=") {
374                if let Ok(n) = rest.parse::<u64>() {
375                    NameComponent::new(tlv_type::BYTE_OFFSET, encode_nonnegtive_integer(n))
376                } else {
377                    NameComponent::generic(Bytes::from(percent_decode(part).map_err(|_| {
378                        PacketError::MalformedPacket("invalid percent-encoding in name".into())
379                    })?))
380                }
381            } else if let Some(rest) = part.strip_prefix("t=") {
382                if let Ok(n) = rest.parse::<u64>() {
383                    NameComponent::new(tlv_type::TIMESTAMP, encode_nonnegtive_integer(n))
384                } else {
385                    NameComponent::generic(Bytes::from(percent_decode(part).map_err(|_| {
386                        PacketError::MalformedPacket("invalid percent-encoding in name".into())
387                    })?))
388                }
389            } else if let Some(rest) = part.strip_prefix("seq=") {
390                if let Ok(n) = rest.parse::<u64>() {
391                    NameComponent::new(tlv_type::SEQUENCE_NUM, encode_nonnegtive_integer(n))
392                } else {
393                    NameComponent::generic(Bytes::from(percent_decode(part).map_err(|_| {
394                        PacketError::MalformedPacket("invalid percent-encoding in name".into())
395                    })?))
396                }
397            } else {
398                let decoded = percent_decode(part).map_err(|_| {
399                    PacketError::MalformedPacket("invalid percent-encoding in name".into())
400                })?;
401                NameComponent::generic(Bytes::from(decoded))
402            };
403            components.push(comp);
404        }
405
406        if components.is_empty() {
407            Ok(Self::root())
408        } else {
409            Ok(Self { components })
410        }
411    }
412}
413
414/// Decode percent-encoded bytes in a name component.
415fn percent_decode(s: &str) -> Result<Vec<u8>, ()> {
416    let mut out = Vec::with_capacity(s.len());
417    let bytes = s.as_bytes();
418    let mut i = 0;
419    while i < bytes.len() {
420        if bytes[i] == b'%' {
421            if i + 2 >= bytes.len() {
422                return Err(());
423            }
424            let hi = hex_digit(bytes[i + 1])?;
425            let lo = hex_digit(bytes[i + 2])?;
426            out.push((hi << 4) | lo);
427            i += 3;
428        } else {
429            out.push(bytes[i]);
430            i += 1;
431        }
432    }
433    Ok(out)
434}
435
436fn hex_digit(b: u8) -> Result<u8, ()> {
437    match b {
438        b'0'..=b'9' => Ok(b - b'0'),
439        b'A'..=b'F' => Ok(b - b'A' + 10),
440        b'a'..=b'f' => Ok(b - b'a' + 10),
441        _ => Err(()),
442    }
443}
444
445/// Construct a [`Name`] from an NDN URI string literal.
446///
447/// ```
448/// # use ndn_packet::name;
449/// let prefix = name!("/iperf");
450/// assert_eq!(prefix.to_string(), "/iperf");
451/// ```
452///
453/// Panics at runtime if the string is not a valid NDN name.
454#[macro_export]
455macro_rules! name {
456    ($s:expr) => {
457        <$crate::Name as ::core::str::FromStr>::from_str($s)
458            .expect(concat!("invalid NDN name: ", $s))
459    };
460}
461
462/// Build Name TLV value bytes (the content inside a `0x07` TLV) for testing.
463#[cfg(test)]
464pub(crate) fn build_name_value(components: &[&[u8]]) -> bytes::Bytes {
465    let mut w = ndn_tlv::TlvWriter::new();
466    for comp in components {
467        w.write_tlv(tlv_type::NAME_COMPONENT, comp);
468    }
469    w.finish()
470}
471
472/// Percent-encode a byte slice for NDN URI display.
473fn percent_encode_component(f: &mut core::fmt::Formatter<'_>, value: &[u8]) -> core::fmt::Result {
474    for &b in value {
475        if b.is_ascii_graphic() && b != b'/' && b != b'%' {
476            write!(f, "{}", b as char)?;
477        } else {
478            write!(f, "%{b:02X}")?;
479        }
480    }
481    Ok(())
482}
483
484impl core::fmt::Display for Name {
485    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
486        write!(f, "/")?;
487        for (i, c) in self.components.iter().enumerate() {
488            if i > 0 {
489                write!(f, "/")?;
490            }
491            match c.typ {
492                tlv_type::BLAKE3_DIGEST => {
493                    write!(f, "blake3digest=")?;
494                    for &b in c.value.iter() {
495                        write!(f, "{b:02x}")?;
496                    }
497                }
498                tlv_type::IMPLICIT_SHA256 => {
499                    write!(f, "sha256digest=")?;
500                    for &b in c.value.iter() {
501                        write!(f, "{b:02x}")?;
502                    }
503                }
504                tlv_type::PARAMETERS_SHA256 => {
505                    write!(f, "params-sha256=")?;
506                    for &b in c.value.iter() {
507                        write!(f, "{b:02x}")?;
508                    }
509                }
510                tlv_type::KEYWORD => {
511                    write!(f, "keyword=")?;
512                    percent_encode_component(f, &c.value)?;
513                }
514                tlv_type::SEGMENT => {
515                    write!(f, "seg={}", decode_nonnegative_integer(&c.value))?;
516                }
517                tlv_type::BYTE_OFFSET => {
518                    write!(f, "off={}", decode_nonnegative_integer(&c.value))?;
519                }
520                tlv_type::VERSION => {
521                    write!(f, "v={}", decode_nonnegative_integer(&c.value))?;
522                }
523                tlv_type::TIMESTAMP => {
524                    write!(f, "t={}", decode_nonnegative_integer(&c.value))?;
525                }
526                tlv_type::SEQUENCE_NUM => {
527                    write!(f, "seq={}", decode_nonnegative_integer(&c.value))?;
528                }
529                _ => {
530                    // Generic component or unknown type: percent-encode.
531                    percent_encode_component(f, &c.value)?;
532                }
533            }
534        }
535        Ok(())
536    }
537}
538
539#[cfg(test)]
540mod tests {
541    use super::*;
542    use std::collections::hash_map::DefaultHasher;
543    use std::hash::{Hash, Hasher};
544
545    fn hash_of(name: &Name) -> u64 {
546        let mut h = DefaultHasher::new();
547        name.hash(&mut h);
548        h.finish()
549    }
550
551    fn comp(s: &[u8]) -> NameComponent {
552        NameComponent::generic(bytes::Bytes::copy_from_slice(s))
553    }
554
555    // ── constructors ──────────────────────────────────────────────────────────
556
557    #[test]
558    fn root_is_empty() {
559        let n = Name::root();
560        assert!(n.is_empty());
561        assert_eq!(n.len(), 0);
562        assert_eq!(n.components().len(), 0);
563    }
564
565    #[test]
566    fn from_components_stores_all() {
567        let n = Name::from_components([comp(b"edu"), comp(b"ucla"), comp(b"news")]);
568        assert_eq!(n.len(), 3);
569        assert_eq!(n.components()[0].value.as_ref(), b"edu");
570        assert_eq!(n.components()[1].value.as_ref(), b"ucla");
571        assert_eq!(n.components()[2].value.as_ref(), b"news");
572    }
573
574    // ── has_prefix ────────────────────────────────────────────────────────────
575
576    #[test]
577    fn has_prefix_true() {
578        let name = Name::from_components([comp(b"edu"), comp(b"ucla"), comp(b"news")]);
579        let prefix = Name::from_components([comp(b"edu"), comp(b"ucla")]);
580        assert!(name.has_prefix(&prefix));
581    }
582
583    #[test]
584    fn has_prefix_equal_names() {
585        let name = Name::from_components([comp(b"edu"), comp(b"ucla")]);
586        assert!(name.has_prefix(&name.clone()));
587    }
588
589    #[test]
590    fn has_prefix_root_is_prefix_of_everything() {
591        let name = Name::from_components([comp(b"any"), comp(b"name")]);
592        assert!(name.has_prefix(&Name::root()));
593    }
594
595    #[test]
596    fn has_prefix_false_different_component() {
597        let name = Name::from_components([comp(b"edu"), comp(b"ucla")]);
598        let prefix = Name::from_components([comp(b"edu"), comp(b"mit")]);
599        assert!(!name.has_prefix(&prefix));
600    }
601
602    #[test]
603    fn has_prefix_false_prefix_longer_than_name() {
604        let name = Name::from_components([comp(b"edu")]);
605        let prefix = Name::from_components([comp(b"edu"), comp(b"ucla")]);
606        assert!(!name.has_prefix(&prefix));
607    }
608
609    // ── decode ────────────────────────────────────────────────────────────────
610
611    #[test]
612    fn decode_empty_name() {
613        let name = Name::decode(bytes::Bytes::new()).unwrap();
614        assert!(name.is_empty());
615    }
616
617    #[test]
618    fn decode_one_component() {
619        let value = build_name_value(&[b"hello"]);
620        let name = Name::decode(value).unwrap();
621        assert_eq!(name.len(), 1);
622        assert_eq!(name.components()[0].value.as_ref(), b"hello");
623        assert_eq!(name.components()[0].typ, tlv_type::NAME_COMPONENT);
624    }
625
626    #[test]
627    fn decode_multiple_components() {
628        let value = build_name_value(&[b"edu", b"ucla", b"data"]);
629        let name = Name::decode(value).unwrap();
630        assert_eq!(name.len(), 3);
631        assert_eq!(name.components()[2].value.as_ref(), b"data");
632    }
633
634    #[test]
635    fn decode_preserves_component_type() {
636        // Component with a non-generic type (e.g. ImplicitSha256 = 0x01).
637        let mut w = ndn_tlv::TlvWriter::new();
638        w.write_tlv(0x01, &[0xAA; 32]);
639        let value = w.finish();
640        let name = Name::decode(value).unwrap();
641        assert_eq!(name.components()[0].typ, 0x01);
642    }
643
644    // ── Display ───────────────────────────────────────────────────────────────
645
646    #[test]
647    fn display_root() {
648        assert_eq!(Name::root().to_string(), "/");
649    }
650
651    #[test]
652    fn display_single_component() {
653        let n = Name::from_components([comp(b"ndn")]);
654        assert_eq!(n.to_string(), "/ndn");
655    }
656
657    #[test]
658    fn display_multi_component() {
659        let n = Name::from_components([comp(b"edu"), comp(b"ucla"), comp(b"data")]);
660        assert_eq!(n.to_string(), "/edu/ucla/data");
661    }
662
663    #[test]
664    fn display_non_ascii_percent_encoded() {
665        let n =
666            Name::from_components([NameComponent::generic(bytes::Bytes::from(vec![0x00, 0xFF]))]);
667        // 0x00 is not ascii_graphic, 0xFF is not ascii_graphic
668        assert_eq!(n.to_string(), "/%00%FF");
669    }
670
671    // ── Hash / Eq ─────────────────────────────────────────────────────────────
672
673    #[test]
674    fn equal_names_have_equal_hash() {
675        let a = Name::from_components([comp(b"foo"), comp(b"bar")]);
676        let b = Name::from_components([comp(b"foo"), comp(b"bar")]);
677        assert_eq!(a, b);
678        assert_eq!(hash_of(&a), hash_of(&b));
679    }
680
681    #[test]
682    fn different_names_are_not_equal() {
683        let a = Name::from_components([comp(b"foo")]);
684        let b = Name::from_components([comp(b"bar")]);
685        assert_ne!(a, b);
686    }
687
688    #[test]
689    fn component_type_affects_equality() {
690        let generic = NameComponent::generic(bytes::Bytes::copy_from_slice(b"abc"));
691        let implicit = NameComponent {
692            typ: 0x01,
693            value: bytes::Bytes::copy_from_slice(b"abc"),
694        };
695        assert_ne!(generic, implicit);
696    }
697
698    // ── FromStr ───────────────────────────────────────────────────────────────
699
700    #[test]
701    fn from_str_simple() {
702        let n: Name = "/edu/ucla/data".parse().unwrap();
703        assert_eq!(n.len(), 3);
704        assert_eq!(n.components()[0].value.as_ref(), b"edu");
705        assert_eq!(n.components()[2].value.as_ref(), b"data");
706    }
707
708    #[test]
709    fn from_str_root() {
710        let n: Name = "/".parse().unwrap();
711        assert!(n.is_empty());
712    }
713
714    #[test]
715    fn from_str_empty_string() {
716        let n: Name = "".parse().unwrap();
717        assert!(n.is_empty());
718    }
719
720    #[test]
721    fn from_str_trailing_slash() {
722        let n: Name = "/test/".parse().unwrap();
723        assert_eq!(n.len(), 1);
724        assert_eq!(n.components()[0].value.as_ref(), b"test");
725    }
726
727    #[test]
728    fn from_str_percent_decode() {
729        let n: Name = "/%00%FF".parse().unwrap();
730        assert_eq!(n.len(), 1);
731        assert_eq!(n.components()[0].value.as_ref(), &[0x00, 0xFF]);
732    }
733
734    #[test]
735    fn from_str_lowercase_hex() {
736        let n: Name = "/%0a%ff".parse().unwrap();
737        assert_eq!(n.components()[0].value.as_ref(), &[0x0A, 0xFF]);
738    }
739
740    #[test]
741    fn from_str_no_leading_slash_is_err() {
742        assert!("edu/ucla".parse::<Name>().is_err());
743    }
744
745    #[test]
746    fn from_str_bad_percent_is_err() {
747        assert!("/%ZZ".parse::<Name>().is_err());
748    }
749
750    #[test]
751    fn display_from_str_roundtrip() {
752        let original = Name::from_components([
753            comp(b"edu"),
754            comp(b"ucla"),
755            NameComponent::generic(bytes::Bytes::from(vec![0x00, 0xFF])),
756        ]);
757        let s = original.to_string();
758        let parsed: Name = s.parse().unwrap();
759        assert_eq!(original, parsed);
760    }
761
762    // ── append ────────────────────────────────────────────────────────────────
763
764    #[test]
765    fn append_builds_name() {
766        let n = Name::root().append("edu").append("ucla");
767        assert_eq!(n.len(), 2);
768        assert_eq!(n.to_string(), "/edu/ucla");
769    }
770
771    #[test]
772    fn append_segment() {
773        let n: Name = "/iperf".parse().unwrap();
774        let n = n.append_segment(42);
775        assert_eq!(n.len(), 2);
776        assert_eq!(n.components()[1].typ, tlv_type::SEGMENT);
777    }
778
779    #[test]
780    fn append_segment_zero() {
781        let n = Name::root().append_segment(0);
782        assert_eq!(n.components()[0].value.as_ref(), &[0u8]);
783    }
784
785    // ── name! macro ───────────────────────────────────────────────────────────
786
787    #[test]
788    fn name_macro() {
789        let n = name!("/iperf/data");
790        assert_eq!(n.len(), 2);
791        assert_eq!(n.to_string(), "/iperf/data");
792    }
793
794    // ── Typed component constructors ─────────────────────────────────────────
795
796    #[test]
797    fn keyword_component_roundtrip() {
798        let c = NameComponent::keyword(Bytes::from_static(b"hello"));
799        assert_eq!(c.typ, tlv_type::KEYWORD);
800        assert_eq!(c.value.as_ref(), b"hello");
801    }
802
803    #[test]
804    fn byte_offset_roundtrip() {
805        let c = NameComponent::byte_offset(1024);
806        assert_eq!(c.typ, tlv_type::BYTE_OFFSET);
807        assert_eq!(c.as_byte_offset(), Some(1024));
808    }
809
810    #[test]
811    fn version_roundtrip() {
812        let c = NameComponent::version(7);
813        assert_eq!(c.typ, tlv_type::VERSION);
814        assert_eq!(c.as_version(), Some(7));
815    }
816
817    #[test]
818    fn timestamp_roundtrip() {
819        let c = NameComponent::timestamp(1_700_000_000);
820        assert_eq!(c.typ, tlv_type::TIMESTAMP);
821        assert_eq!(c.as_timestamp(), Some(1_700_000_000));
822    }
823
824    #[test]
825    fn sequence_num_roundtrip() {
826        let c = NameComponent::sequence_num(42);
827        assert_eq!(c.typ, tlv_type::SEQUENCE_NUM);
828        assert_eq!(c.as_sequence_num(), Some(42));
829    }
830
831    #[test]
832    fn zero_value_roundtrip() {
833        assert_eq!(NameComponent::version(0).as_version(), Some(0));
834        assert_eq!(NameComponent::sequence_num(0).as_sequence_num(), Some(0));
835        assert_eq!(NameComponent::byte_offset(0).as_byte_offset(), Some(0));
836        assert_eq!(NameComponent::timestamp(0).as_timestamp(), Some(0));
837    }
838
839    #[test]
840    fn accessor_wrong_type_returns_none() {
841        let c = NameComponent::version(5);
842        assert_eq!(c.as_segment(), None);
843        assert_eq!(c.as_byte_offset(), None);
844        assert_eq!(c.as_timestamp(), None);
845        assert_eq!(c.as_sequence_num(), None);
846    }
847
848    #[test]
849    fn as_segment_accessor() {
850        let n = Name::root().append_segment(99);
851        assert_eq!(n.components()[0].as_segment(), Some(99));
852    }
853
854    // ── Builder method chaining ──────────────────────────────────────────────
855
856    #[test]
857    fn builder_chaining_all_types() {
858        let n = Name::root()
859            .append("data")
860            .append_version(3)
861            .append_segment(0);
862        assert_eq!(n.len(), 3);
863        assert_eq!(n.components()[0].typ, tlv_type::NAME_COMPONENT);
864        assert_eq!(n.components()[1].typ, tlv_type::VERSION);
865        assert_eq!(n.components()[1].as_version(), Some(3));
866        assert_eq!(n.components()[2].typ, tlv_type::SEGMENT);
867        assert_eq!(n.components()[2].as_segment(), Some(0));
868    }
869
870    #[test]
871    fn builder_timestamp_and_sequence() {
872        let n = Name::root()
873            .append("sensor")
874            .append_timestamp(1_700_000)
875            .append_sequence_num(5)
876            .append_byte_offset(4096);
877        assert_eq!(n.len(), 4);
878        assert_eq!(n.components()[1].as_timestamp(), Some(1_700_000));
879        assert_eq!(n.components()[2].as_sequence_num(), Some(5));
880        assert_eq!(n.components()[3].as_byte_offset(), Some(4096));
881    }
882
883    // ── Display with typed components ────────────────────────────────────────
884
885    #[test]
886    fn display_segment() {
887        let n = Name::root().append("data").append_segment(42);
888        assert_eq!(n.to_string(), "/data/seg=42");
889    }
890
891    #[test]
892    fn display_version() {
893        let n = Name::root().append("data").append_version(3);
894        assert_eq!(n.to_string(), "/data/v=3");
895    }
896
897    #[test]
898    fn display_timestamp() {
899        let n = Name::root().append("data").append_timestamp(1000);
900        assert_eq!(n.to_string(), "/data/t=1000");
901    }
902
903    #[test]
904    fn display_sequence_num() {
905        let n = Name::root().append("data").append_sequence_num(7);
906        assert_eq!(n.to_string(), "/data/seq=7");
907    }
908
909    #[test]
910    fn display_byte_offset() {
911        let n = Name::root().append("data").append_byte_offset(512);
912        assert_eq!(n.to_string(), "/data/off=512");
913    }
914
915    #[test]
916    fn display_keyword() {
917        let n = Name::root().append_component(NameComponent::keyword(Bytes::from_static(b"test")));
918        assert_eq!(n.to_string(), "/keyword=test");
919    }
920
921    #[test]
922    fn display_sha256digest() {
923        let digest = [0xABu8; 32];
924        let n = Name::root().append_component(NameComponent::new(
925            tlv_type::IMPLICIT_SHA256,
926            Bytes::copy_from_slice(&digest),
927        ));
928        let expected_hex: String = digest.iter().map(|b| format!("{b:02x}")).collect();
929        assert_eq!(n.to_string(), format!("/sha256digest={expected_hex}"));
930    }
931
932    #[test]
933    fn display_params_sha256() {
934        let digest = [0xCDu8; 32];
935        let n = Name::root().append_component(NameComponent::new(
936            tlv_type::PARAMETERS_SHA256,
937            Bytes::copy_from_slice(&digest),
938        ));
939        let expected_hex: String = digest.iter().map(|b| format!("{b:02x}")).collect();
940        assert_eq!(n.to_string(), format!("/params-sha256={expected_hex}"));
941    }
942
943    #[test]
944    fn display_mixed_typed_and_generic() {
945        let n = Name::root()
946            .append("ndn")
947            .append("data")
948            .append_version(3)
949            .append_segment(0);
950        assert_eq!(n.to_string(), "/ndn/data/v=3/seg=0");
951    }
952}