ndn_pipeline/
context.rs

1use std::sync::Arc;
2
3use bytes::Bytes;
4use smallvec::SmallVec;
5
6use ndn_packet::{Data, Interest, Nack, Name};
7use ndn_store::PitToken;
8use ndn_transport::{AnyMap, FaceId};
9
10/// The packet as it progresses through decode stages.
11pub enum DecodedPacket {
12    /// Not yet decoded — the raw bytes are still in `PacketContext::raw_bytes`.
13    Raw,
14    Interest(Box<Interest>),
15    Data(Box<Data>),
16    Nack(Box<Nack>),
17}
18
19/// Per-packet state passed by value through pipeline stages.
20///
21/// Passing by value (rather than `&mut`) makes ownership explicit:
22/// a stage that short-circuits simply does not return the context,
23/// so Rust's ownership system prevents use-after-hand-off at compile time.
24pub struct PacketContext {
25    /// Wire-format bytes of the original packet.
26    pub raw_bytes: Bytes,
27    /// Face the packet arrived on.
28    pub face_id: FaceId,
29    /// Decoded name — hoisted to top level because every stage needs it.
30    /// `None` until `TlvDecodeStage` runs.
31    pub name: Option<Arc<Name>>,
32    /// Decoded packet — starts as `Raw`, transitions after TlvDecodeStage.
33    pub packet: DecodedPacket,
34    /// PIT token — written by PitCheckStage, `None` before that stage runs.
35    pub pit_token: Option<PitToken>,
36    /// NDNLPv2 PIT token (opaque, 1-32 bytes) from the incoming LP header.
37    /// Distinct from the internal `pit_token` hash — this is the wire-protocol
38    /// hop-by-hop token that must be echoed in Data/Nack responses.
39    pub lp_pit_token: Option<Bytes>,
40    /// Faces selected for forwarding by the strategy stage.
41    pub out_faces: SmallVec<[FaceId; 4]>,
42    /// Set to `true` by CsLookupStage on a cache hit.
43    pub cs_hit: bool,
44    /// Set to `true` by the security validation stage.
45    pub verified: bool,
46    /// Arrival time in nanoseconds since the Unix epoch (set by the face task).
47    pub arrival: u64,
48    /// Escape hatch for inter-stage communication not covered by explicit fields.
49    /// Use sparingly; prefer explicit fields for anything the core pipeline touches.
50    pub tags: AnyMap,
51}
52
53impl PacketContext {
54    pub fn new(raw_bytes: Bytes, face_id: FaceId, arrival: u64) -> Self {
55        Self {
56            raw_bytes,
57            face_id,
58            name: None,
59            packet: DecodedPacket::Raw,
60            pit_token: None,
61            lp_pit_token: None,
62            out_faces: SmallVec::new(),
63            cs_hit: false,
64            verified: false,
65            arrival,
66            tags: AnyMap::new(),
67        }
68    }
69}
70
71#[cfg(test)]
72mod tests {
73    use super::*;
74    use bytes::Bytes;
75    use ndn_transport::FaceId;
76
77    #[test]
78    fn packet_context_new_defaults() {
79        let raw = Bytes::from_static(b"\x05\x01\x00");
80        let ctx = PacketContext::new(raw.clone(), FaceId(7), 12345);
81        assert_eq!(ctx.raw_bytes, raw);
82        assert_eq!(ctx.face_id, FaceId(7));
83        assert_eq!(ctx.arrival, 12345);
84        assert!(ctx.name.is_none());
85        assert!(ctx.pit_token.is_none());
86        assert!(ctx.out_faces.is_empty());
87        assert!(!ctx.cs_hit);
88        assert!(!ctx.verified);
89        assert!(matches!(ctx.packet, DecodedPacket::Raw));
90    }
91}