ndn_packet/
interest.rs

1use core::time::Duration;
2
3#[cfg(not(feature = "std"))]
4use alloc::{sync::Arc, vec::Vec};
5#[cfg(feature = "std")]
6use std::sync::Arc;
7
8#[cfg(not(feature = "std"))]
9use core::cell::OnceCell as OnceLock;
10#[cfg(feature = "std")]
11use std::sync::OnceLock;
12
13use bytes::Bytes;
14
15use crate::tlv_type;
16use crate::{Name, PacketError, SignatureInfo};
17use ndn_tlv::TlvReader;
18
19/// Selectors that control Interest-Data matching.
20#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
21pub struct Selector {
22    pub can_be_prefix: bool,
23    pub must_be_fresh: bool,
24}
25
26/// An NDN Interest packet.
27///
28/// Fields beyond the name and selectors are lazily decoded via `OnceLock`
29/// so that pipeline stages that short-circuit (e.g., CS hit) pay no decode cost
30/// for fields they never access.
31#[derive(Debug)]
32pub struct Interest {
33    /// Wire-format bytes of the full Interest TLV.
34    pub(crate) raw: Bytes,
35
36    /// Name — always decoded eagerly (every stage needs it).
37    pub name: Arc<Name>,
38
39    /// Selectors — decoded on first access.
40    selectors: OnceLock<Selector>,
41
42    /// Nonce — decoded on first access.
43    nonce: OnceLock<Option<u32>>,
44
45    /// Interest lifetime — decoded on first access.
46    lifetime: OnceLock<Option<Duration>>,
47
48    /// ApplicationParameters (TLV 0x24) — decoded on first access.
49    app_params: OnceLock<Option<Bytes>>,
50
51    /// HopLimit (TLV 0x22) — decoded on first access.
52    hop_limit: OnceLock<Option<u8>>,
53
54    /// ForwardingHint (TLV 0x1E) — list of delegation Names, decoded on first access.
55    forwarding_hint: OnceLock<Option<Vec<Arc<Name>>>>,
56
57    /// InterestSignatureInfo (TLV 0x2C) — decoded on first access.
58    sig_info: OnceLock<Option<SignatureInfo>>,
59
60    /// InterestSignatureValue (TLV 0x2E) — raw signature bytes, decoded on first access.
61    sig_value: OnceLock<Option<Bytes>>,
62}
63
64impl Interest {
65    /// Construct a minimal Interest with only a name (for testing / app use).
66    pub fn new(name: Name) -> Self {
67        Self {
68            raw: Bytes::new(),
69            name: Arc::new(name),
70            selectors: OnceLock::new(),
71            nonce: OnceLock::new(),
72            lifetime: OnceLock::new(),
73            app_params: OnceLock::new(),
74            hop_limit: OnceLock::new(),
75            forwarding_hint: OnceLock::new(),
76            sig_info: OnceLock::new(),
77            sig_value: OnceLock::new(),
78        }
79    }
80
81    /// Decode an Interest from raw wire bytes.
82    pub fn decode(raw: Bytes) -> Result<Self, PacketError> {
83        let mut reader = TlvReader::new(raw.clone());
84        let (typ, value) = reader.read_tlv()?;
85        if typ != tlv_type::INTEREST {
86            return Err(PacketError::UnknownPacketType(typ));
87        }
88        let mut inner = TlvReader::new(value);
89
90        // Name is mandatory and must come first.
91        let (name_typ, name_val) = inner.read_tlv()?;
92        if name_typ != tlv_type::NAME {
93            return Err(PacketError::UnknownPacketType(name_typ));
94        }
95        let name = Name::decode(name_val)?;
96
97        // NDN Packet Format v0.3 §2: Interest/Data must have at least one
98        // name component.
99        if name.is_empty() {
100            return Err(PacketError::MalformedPacket(
101                "Interest Name must have at least one component".into(),
102            ));
103        }
104
105        // ParametersSha256DigestComponent digest validation is an
106        // application-layer trust concern, not a routing concern.  Forwarders
107        // must accept and forward Signed Interests (NDNts v0.3, ndn-cxx) without
108        // validating the digest — rejecting them would silently drop management
109        // commands.  Applications that need integrity checking should call a
110        // dedicated verify method.
111
112        Ok(Self {
113            raw,
114            name: Arc::new(name),
115            selectors: OnceLock::new(),
116            nonce: OnceLock::new(),
117            lifetime: OnceLock::new(),
118            app_params: OnceLock::new(),
119            hop_limit: OnceLock::new(),
120            forwarding_hint: OnceLock::new(),
121            sig_info: OnceLock::new(),
122            sig_value: OnceLock::new(),
123        })
124    }
125
126    pub fn selectors(&self) -> &Selector {
127        self.selectors
128            .get_or_init(|| decode_selectors(&self.raw).unwrap_or_default())
129    }
130
131    pub fn nonce(&self) -> Option<u32> {
132        *self
133            .nonce
134            .get_or_init(|| decode_nonce(&self.raw).ok().flatten())
135    }
136
137    pub fn lifetime(&self) -> Option<Duration> {
138        *self
139            .lifetime
140            .get_or_init(|| decode_lifetime(&self.raw).ok().flatten())
141    }
142
143    /// The `ApplicationParameters` TLV value (type 0x24), if present.
144    ///
145    /// Returns `None` when the Interest was constructed without parameters (e.g.
146    /// via `Interest::new`) or when the wire format contains no 0x24 TLV.
147    pub fn app_parameters(&self) -> Option<&Bytes> {
148        self.app_params
149            .get_or_init(|| decode_app_params(&self.raw).ok().flatten())
150            .as_ref()
151    }
152
153    /// ForwardingHint delegation names, if present.
154    ///
155    /// Per NDN Packet Format v0.3 §5.2, ForwardingHint (TLV 0x1E) contains
156    /// one or more Name TLVs representing delegation prefixes that a
157    /// forwarder can use to reach the Data producer.
158    pub fn forwarding_hint(&self) -> Option<&[Arc<Name>]> {
159        self.forwarding_hint
160            .get_or_init(|| decode_forwarding_hint(&self.raw).ok().flatten())
161            .as_deref()
162    }
163
164    /// HopLimit value (0–255), if present in the wire format.
165    ///
166    /// Per NDN Packet Format v0.3 §5.2, this is a 1-byte field.
167    /// The forwarder must decrement before forwarding and drop if zero.
168    pub fn hop_limit(&self) -> Option<u8> {
169        *self
170            .hop_limit
171            .get_or_init(|| decode_hop_limit(&self.raw).ok().flatten())
172    }
173
174    /// InterestSignatureInfo, if present (Signed Interest per NDN Packet Format v0.3 §5.4).
175    pub fn sig_info(&self) -> Option<&SignatureInfo> {
176        self.sig_info
177            .get_or_init(|| decode_interest_sig_info(&self.raw).ok().flatten())
178            .as_ref()
179    }
180
181    /// InterestSignatureValue bytes, if present.
182    pub fn sig_value(&self) -> Option<&Bytes> {
183        self.sig_value
184            .get_or_init(|| decode_interest_sig_value(&self.raw).ok().flatten())
185            .as_ref()
186    }
187
188    /// The signed region of a Signed Interest — from the start of Name TLV
189    /// through the end of InterestSignatureInfo TLV (inclusive).
190    ///
191    /// Returns `None` if InterestSignatureInfo is not present.
192    pub fn signed_region(&self) -> Option<&[u8]> {
193        compute_interest_signed_region(&self.raw).ok().flatten()
194    }
195
196    pub fn raw(&self) -> &Bytes {
197        &self.raw
198    }
199}
200
201fn decode_selectors(raw: &Bytes) -> Result<Selector, PacketError> {
202    let mut sel = Selector::default();
203    let mut reader = TlvReader::new(raw.clone());
204    let (_, value) = reader.read_tlv()?; // outer Interest TLV
205    let mut inner = TlvReader::new(value);
206    while !inner.is_empty() {
207        let (typ, _) = inner.read_tlv()?;
208        match typ {
209            t if t == tlv_type::CAN_BE_PREFIX => sel.can_be_prefix = true,
210            t if t == tlv_type::MUST_BE_FRESH => sel.must_be_fresh = true,
211            _ => {}
212        }
213    }
214    Ok(sel)
215}
216
217fn decode_nonce(raw: &Bytes) -> Result<Option<u32>, PacketError> {
218    let mut reader = TlvReader::new(raw.clone());
219    let (_, value) = reader.read_tlv()?;
220    let mut inner = TlvReader::new(value);
221    while !inner.is_empty() {
222        let (typ, val) = inner.read_tlv()?;
223        if typ == tlv_type::NONCE {
224            if val.len() != 4 {
225                return Ok(None);
226            }
227            let n = u32::from_be_bytes([val[0], val[1], val[2], val[3]]);
228            return Ok(Some(n));
229        }
230    }
231    Ok(None)
232}
233
234fn decode_app_params(raw: &Bytes) -> Result<Option<Bytes>, PacketError> {
235    if raw.is_empty() {
236        return Ok(None);
237    }
238    let mut reader = TlvReader::new(raw.clone());
239    let (_, value) = reader.read_tlv()?;
240    let mut inner = TlvReader::new(value);
241    while !inner.is_empty() {
242        let (typ, val) = inner.read_tlv()?;
243        if typ == tlv_type::APP_PARAMETERS {
244            return Ok(Some(val));
245        }
246    }
247    Ok(None)
248}
249
250fn decode_forwarding_hint(raw: &Bytes) -> Result<Option<Vec<Arc<Name>>>, PacketError> {
251    if raw.is_empty() {
252        return Ok(None);
253    }
254    let mut reader = TlvReader::new(raw.clone());
255    let (_, value) = reader.read_tlv()?;
256    let mut inner = TlvReader::new(value);
257    while !inner.is_empty() {
258        let (typ, val) = inner.read_tlv()?;
259        if typ == tlv_type::FORWARDING_HINT {
260            // ForwardingHint value contains one or more Name TLVs.
261            let mut hint_reader = TlvReader::new(val);
262            let mut names = Vec::new();
263            while !hint_reader.is_empty() {
264                let (t, v) = hint_reader.read_tlv()?;
265                if t == tlv_type::NAME {
266                    names.push(Arc::new(Name::decode(v)?));
267                }
268            }
269            if names.is_empty() {
270                return Ok(None);
271            }
272            return Ok(Some(names));
273        }
274    }
275    Ok(None)
276}
277
278fn decode_hop_limit(raw: &Bytes) -> Result<Option<u8>, PacketError> {
279    if raw.is_empty() {
280        return Ok(None);
281    }
282    let mut reader = TlvReader::new(raw.clone());
283    let (_, value) = reader.read_tlv()?;
284    let mut inner = TlvReader::new(value);
285    while !inner.is_empty() {
286        let (typ, val) = inner.read_tlv()?;
287        if typ == tlv_type::HOP_LIMIT {
288            if val.len() == 1 {
289                return Ok(Some(val[0]));
290            }
291            return Ok(None);
292        }
293    }
294    Ok(None)
295}
296
297fn decode_interest_sig_info(raw: &Bytes) -> Result<Option<SignatureInfo>, PacketError> {
298    if raw.is_empty() {
299        return Ok(None);
300    }
301    let mut reader = TlvReader::new(raw.clone());
302    let (_, value) = reader.read_tlv()?;
303    let mut inner = TlvReader::new(value);
304    while !inner.is_empty() {
305        let (typ, val) = inner.read_tlv()?;
306        if typ == tlv_type::INTEREST_SIGNATURE_INFO {
307            return Ok(Some(SignatureInfo::decode(val)?));
308        }
309    }
310    Ok(None)
311}
312
313fn decode_interest_sig_value(raw: &Bytes) -> Result<Option<Bytes>, PacketError> {
314    if raw.is_empty() {
315        return Ok(None);
316    }
317    let mut reader = TlvReader::new(raw.clone());
318    let (_, value) = reader.read_tlv()?;
319    let mut inner = TlvReader::new(value);
320    while !inner.is_empty() {
321        let (typ, val) = inner.read_tlv()?;
322        if typ == tlv_type::INTEREST_SIGNATURE_VALUE {
323            return Ok(Some(val));
324        }
325    }
326    Ok(None)
327}
328
329/// Compute the signed region for a Signed Interest.
330///
331/// Per NDN Packet Format v0.3 §5.4, the signed portion covers from the first
332/// byte of the Name TLV through the last byte of the InterestSignatureInfo TLV,
333/// all relative to the Interest's inner value (after the outer TLV header).
334fn compute_interest_signed_region(raw: &Bytes) -> Result<Option<&[u8]>, PacketError> {
335    if raw.is_empty() {
336        return Ok(None);
337    }
338    let mut reader = TlvReader::new(raw.clone());
339    let (_, value) = reader.read_tlv()?;
340    let outer_header_len = raw.len() - value.len();
341    let mut inner = TlvReader::new(value);
342    let mut sig_info_end = 0usize;
343    while !inner.is_empty() {
344        let (typ, _) = inner.read_tlv()?;
345        if typ == tlv_type::INTEREST_SIGNATURE_INFO {
346            sig_info_end = outer_header_len + inner.position();
347            break;
348        }
349    }
350    if sig_info_end == 0 {
351        return Ok(None);
352    }
353    // Signed region: from start of Name (first byte of inner value) to end of SigInfo.
354    Ok(Some(&raw[outer_header_len..sig_info_end]))
355}
356
357fn decode_lifetime(raw: &Bytes) -> Result<Option<Duration>, PacketError> {
358    let mut reader = TlvReader::new(raw.clone());
359    let (_, value) = reader.read_tlv()?;
360    let mut inner = TlvReader::new(value);
361    while !inner.is_empty() {
362        let (typ, val) = inner.read_tlv()?;
363        if typ == tlv_type::INTEREST_LIFETIME {
364            let mut ms = 0u64;
365            for &b in val.iter() {
366                ms = (ms << 8) | b as u64;
367            }
368            return Ok(Some(Duration::from_millis(ms)));
369        }
370    }
371    Ok(None)
372}
373
374#[cfg(test)]
375mod tests {
376    use super::*;
377    use ndn_tlv::TlvWriter;
378
379    /// Build a complete Interest wire packet for testing.
380    fn build_interest(
381        components: &[&[u8]],
382        nonce: Option<u32>,
383        lifetime_ms: Option<u64>,
384        can_be_prefix: bool,
385        must_be_fresh: bool,
386    ) -> Bytes {
387        build_interest_full(
388            components,
389            nonce,
390            lifetime_ms,
391            can_be_prefix,
392            must_be_fresh,
393            None,
394        )
395    }
396
397    fn build_interest_full(
398        components: &[&[u8]],
399        nonce: Option<u32>,
400        lifetime_ms: Option<u64>,
401        can_be_prefix: bool,
402        must_be_fresh: bool,
403        hop_limit: Option<u8>,
404    ) -> Bytes {
405        let mut w = TlvWriter::new();
406        w.write_nested(tlv_type::INTEREST, |w| {
407            w.write_nested(tlv_type::NAME, |w| {
408                for comp in components {
409                    w.write_tlv(tlv_type::NAME_COMPONENT, comp);
410                }
411            });
412            if can_be_prefix {
413                w.write_tlv(tlv_type::CAN_BE_PREFIX, &[]);
414            }
415            if must_be_fresh {
416                w.write_tlv(tlv_type::MUST_BE_FRESH, &[]);
417            }
418            if let Some(n) = nonce {
419                w.write_tlv(tlv_type::NONCE, &n.to_be_bytes());
420            }
421            if let Some(ms) = lifetime_ms {
422                w.write_tlv(tlv_type::INTEREST_LIFETIME, &ms.to_be_bytes());
423            }
424            if let Some(h) = hop_limit {
425                w.write_tlv(tlv_type::HOP_LIMIT, &[h]);
426            }
427        });
428        w.finish()
429    }
430
431    // ── Interest::new ─────────────────────────────────────────────────────────
432
433    #[test]
434    fn new_stores_name() {
435        let name =
436            Name::from_components([crate::NameComponent::generic(Bytes::from_static(b"test"))]);
437        let i = Interest::new(name.clone());
438        assert_eq!(*i.name, name);
439    }
440
441    #[test]
442    fn new_has_no_nonce_or_lifetime() {
443        let i = Interest::new(Name::root());
444        assert_eq!(i.nonce(), None);
445        assert_eq!(i.lifetime(), None);
446    }
447
448    // ── Interest::decode ──────────────────────────────────────────────────────
449
450    #[test]
451    fn decode_name_only() {
452        let raw = build_interest(&[b"edu", b"ucla"], None, None, false, false);
453        let i = Interest::decode(raw).unwrap();
454        assert_eq!(i.name.len(), 2);
455        assert_eq!(i.name.components()[0].value.as_ref(), b"edu");
456        assert_eq!(i.name.components()[1].value.as_ref(), b"ucla");
457    }
458
459    #[test]
460    fn decode_with_nonce() {
461        let raw = build_interest(&[b"test"], Some(0xDEAD_BEEF), None, false, false);
462        let i = Interest::decode(raw).unwrap();
463        assert_eq!(i.nonce(), Some(0xDEAD_BEEF));
464    }
465
466    #[test]
467    fn decode_with_lifetime() {
468        let raw = build_interest(&[b"test"], None, Some(4000), false, false);
469        let i = Interest::decode(raw).unwrap();
470        assert_eq!(i.lifetime(), Some(Duration::from_millis(4000)));
471    }
472
473    #[test]
474    fn decode_with_can_be_prefix() {
475        let raw = build_interest(&[b"test"], None, None, true, false);
476        let i = Interest::decode(raw).unwrap();
477        assert!(i.selectors().can_be_prefix);
478        assert!(!i.selectors().must_be_fresh);
479    }
480
481    #[test]
482    fn decode_with_must_be_fresh() {
483        let raw = build_interest(&[b"test"], None, None, false, true);
484        let i = Interest::decode(raw).unwrap();
485        assert!(!i.selectors().can_be_prefix);
486        assert!(i.selectors().must_be_fresh);
487    }
488
489    #[test]
490    fn decode_with_all_fields() {
491        let raw = build_interest(
492            &[b"edu", b"ucla", b"data"],
493            Some(0x1234_5678),
494            Some(8000),
495            true,
496            true,
497        );
498        let i = Interest::decode(raw).unwrap();
499        assert_eq!(i.name.len(), 3);
500        assert_eq!(i.nonce(), Some(0x1234_5678));
501        assert_eq!(i.lifetime(), Some(Duration::from_millis(8000)));
502        assert!(i.selectors().can_be_prefix);
503        assert!(i.selectors().must_be_fresh);
504    }
505
506    #[test]
507    fn decode_raw_field_preserved() {
508        let raw = build_interest(&[b"test"], Some(42), None, false, false);
509        let i = Interest::decode(raw.clone()).unwrap();
510        assert_eq!(i.raw(), &raw);
511    }
512
513    #[test]
514    fn decode_wrong_outer_type_errors() {
515        // Start with DATA type (0x06) instead of INTEREST (0x05).
516        let mut w = TlvWriter::new();
517        w.write_nested(tlv_type::DATA, |w| {
518            w.write_nested(tlv_type::NAME, |w| {
519                w.write_tlv(tlv_type::NAME_COMPONENT, b"test");
520            });
521        });
522        let raw = w.finish();
523        assert!(matches!(
524            Interest::decode(raw).unwrap_err(),
525            crate::PacketError::UnknownPacketType(0x06)
526        ));
527    }
528
529    #[test]
530    fn decode_with_forwarding_hint() {
531        let mut w = TlvWriter::new();
532        w.write_nested(tlv_type::INTEREST, |w| {
533            w.write_nested(tlv_type::NAME, |w| {
534                w.write_tlv(tlv_type::NAME_COMPONENT, b"test");
535            });
536            w.write_nested(tlv_type::FORWARDING_HINT, |w| {
537                w.write_nested(tlv_type::NAME, |w| {
538                    w.write_tlv(tlv_type::NAME_COMPONENT, b"ndn");
539                    w.write_tlv(tlv_type::NAME_COMPONENT, b"gateway");
540                });
541            });
542        });
543        let raw = w.finish();
544        let i = Interest::decode(raw).unwrap();
545        let hints = i.forwarding_hint().expect("forwarding_hint present");
546        assert_eq!(hints.len(), 1);
547        assert_eq!(hints[0].len(), 2);
548        assert_eq!(hints[0].components()[0].value.as_ref(), b"ndn");
549    }
550
551    #[test]
552    fn decode_without_forwarding_hint() {
553        let raw = build_interest(&[b"test"], None, None, false, false);
554        let i = Interest::decode(raw).unwrap();
555        assert!(i.forwarding_hint().is_none());
556    }
557
558    #[test]
559    fn decode_app_params_wrong_digest_accepted() {
560        // Forwarders must accept Signed Interests even with a mismatched
561        // ParametersSha256DigestComponent — digest validation is an app concern.
562        let mut w = TlvWriter::new();
563        w.write_nested(tlv_type::INTEREST, |w| {
564            w.write_nested(tlv_type::NAME, |w| {
565                w.write_tlv(tlv_type::NAME_COMPONENT, b"test");
566                w.write_tlv(tlv_type::PARAMETERS_SHA256, &[0u8; 32]); // wrong digest
567            });
568            w.write_tlv(tlv_type::APP_PARAMETERS, b"hello");
569        });
570        let raw = w.finish();
571        let i = Interest::decode(raw).expect("should accept despite wrong digest");
572        assert_eq!(
573            i.app_parameters().map(|b| b.as_ref()),
574            Some(b"hello".as_ref())
575        );
576    }
577
578    #[test]
579    fn decode_app_params_without_digest_accepted() {
580        // Forwarders must accept Interests with ApplicationParameters even if
581        // the name has no ParametersSha256DigestComponent.
582        let mut w = TlvWriter::new();
583        w.write_nested(tlv_type::INTEREST, |w| {
584            w.write_nested(tlv_type::NAME, |w| {
585                w.write_tlv(tlv_type::NAME_COMPONENT, b"test");
586            });
587            w.write_tlv(tlv_type::APP_PARAMETERS, b"hello");
588        });
589        let raw = w.finish();
590        let i = Interest::decode(raw).expect("should accept without digest component");
591        assert_eq!(
592            i.app_parameters().map(|b| b.as_ref()),
593            Some(b"hello".as_ref())
594        );
595    }
596
597    #[test]
598    fn decode_empty_name_rejected() {
599        // Interest with zero name components should fail.
600        let mut w = TlvWriter::new();
601        w.write_nested(tlv_type::INTEREST, |w| {
602            w.write_tlv(tlv_type::NAME, &[]); // empty Name
603        });
604        let raw = w.finish();
605        assert!(Interest::decode(raw).is_err());
606    }
607
608    #[test]
609    fn decode_truncated_errors() {
610        let raw = Bytes::from_static(&[0x05, 0x10, 0x07]); // length claims 16 bytes, only 1 follows
611        assert!(Interest::decode(raw).is_err());
612    }
613
614    #[test]
615    fn decode_with_hop_limit() {
616        let raw = build_interest_full(&[b"test"], None, None, false, false, Some(64));
617        let i = Interest::decode(raw).unwrap();
618        assert_eq!(i.hop_limit(), Some(64));
619    }
620
621    #[test]
622    fn decode_without_hop_limit() {
623        let raw = build_interest(&[b"test"], None, None, false, false);
624        let i = Interest::decode(raw).unwrap();
625        assert_eq!(i.hop_limit(), None);
626    }
627
628    #[test]
629    fn decode_hop_limit_zero() {
630        let raw = build_interest_full(&[b"test"], None, None, false, false, Some(0));
631        let i = Interest::decode(raw).unwrap();
632        assert_eq!(i.hop_limit(), Some(0));
633    }
634
635    // ── Signed Interest ────────────────────────────────────────────────────
636
637    fn build_signed_interest(components: &[&[u8]], sig_type_code: u8, sig_value: &[u8]) -> Bytes {
638        let mut w = TlvWriter::new();
639        w.write_nested(tlv_type::INTEREST, |w| {
640            w.write_nested(tlv_type::NAME, |w| {
641                for comp in components {
642                    w.write_tlv(tlv_type::NAME_COMPONENT, comp);
643                }
644            });
645            w.write_nested(tlv_type::INTEREST_SIGNATURE_INFO, |w| {
646                w.write_tlv(tlv_type::SIGNATURE_TYPE, &[sig_type_code]);
647            });
648            w.write_tlv(tlv_type::INTEREST_SIGNATURE_VALUE, sig_value);
649        });
650        w.finish()
651    }
652
653    #[test]
654    fn decode_signed_interest_sig_info() {
655        let raw = build_signed_interest(&[b"test"], 5, &[0xAB, 0xCD]);
656        let i = Interest::decode(raw).unwrap();
657        let si = i.sig_info().expect("sig_info present");
658        assert_eq!(si.sig_type, crate::SignatureType::SignatureEd25519);
659    }
660
661    #[test]
662    fn decode_signed_interest_sig_value() {
663        let raw = build_signed_interest(&[b"test"], 5, &[0xDE, 0xAD]);
664        let i = Interest::decode(raw).unwrap();
665        let sv = i.sig_value().expect("sig_value present");
666        assert_eq!(sv.as_ref(), &[0xDE, 0xAD]);
667    }
668
669    #[test]
670    fn decode_signed_interest_signed_region() {
671        let raw = build_signed_interest(&[b"test"], 5, &[0xAB, 0xCD]);
672        let i = Interest::decode(raw.clone()).unwrap();
673        let region = i.signed_region().expect("signed region present");
674        // Region must not be empty.
675        assert!(!region.is_empty());
676        // Region must not contain the signature value bytes.
677        assert!(!region.ends_with(&[0xAB, 0xCD]));
678        // Region must start with the Name TLV type (0x07).
679        assert_eq!(region[0], tlv_type::NAME as u8);
680    }
681
682    #[test]
683    fn unsigned_interest_has_no_sig_fields() {
684        let raw = build_interest(&[b"test"], None, None, false, false);
685        let i = Interest::decode(raw).unwrap();
686        assert!(i.sig_info().is_none());
687        assert!(i.sig_value().is_none());
688        assert!(i.signed_region().is_none());
689    }
690
691    #[test]
692    fn signed_interest_with_key_locator() {
693        let mut w = TlvWriter::new();
694        w.write_nested(tlv_type::INTEREST, |w| {
695            w.write_nested(tlv_type::NAME, |w| {
696                w.write_tlv(tlv_type::NAME_COMPONENT, b"test");
697            });
698            w.write_nested(tlv_type::INTEREST_SIGNATURE_INFO, |w| {
699                w.write_tlv(tlv_type::SIGNATURE_TYPE, &[5]);
700                w.write_nested(tlv_type::KEY_LOCATOR, |w| {
701                    w.write_nested(tlv_type::NAME, |w| {
702                        w.write_tlv(tlv_type::NAME_COMPONENT, b"key1");
703                    });
704                });
705            });
706            w.write_tlv(tlv_type::INTEREST_SIGNATURE_VALUE, &[0xFF]);
707        });
708        let raw = w.finish();
709        let i = Interest::decode(raw).unwrap();
710        let si = i.sig_info().unwrap();
711        let kl = si.key_locator.as_ref().expect("key_locator present");
712        assert_eq!(kl.components()[0].value.as_ref(), b"key1");
713    }
714
715    #[test]
716    fn lazy_fields_decoded_once_and_cached() {
717        // Access each lazy field twice; result should be identical.
718        let raw = build_interest(&[b"x"], Some(99), Some(1000), true, false);
719        let i = Interest::decode(raw).unwrap();
720        assert_eq!(i.nonce(), i.nonce());
721        assert_eq!(i.lifetime(), i.lifetime());
722        assert_eq!(i.selectors(), i.selectors());
723    }
724}