ndn_packet/
meta_info.rs

1use core::time::Duration;
2
3use bytes::Bytes;
4
5use crate::{PacketError, tlv_type};
6use ndn_tlv::TlvReader;
7
8/// NDN content types.
9#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
10pub enum ContentType {
11    #[default]
12    Blob,
13    Link,
14    Key,
15    Nack,
16    Other(u64),
17}
18
19impl ContentType {
20    pub fn code(&self) -> u64 {
21        match self {
22            ContentType::Blob => 0,
23            ContentType::Link => 1,
24            ContentType::Key => 2,
25            ContentType::Nack => 3,
26            ContentType::Other(c) => *c,
27        }
28    }
29}
30
31/// Metadata carried in a Data packet's MetaInfo TLV.
32#[derive(Clone, Debug, Default)]
33pub struct MetaInfo {
34    pub content_type: ContentType,
35    pub freshness_period: Option<Duration>,
36    pub final_block_id: Option<Bytes>,
37}
38
39impl MetaInfo {
40    pub fn decode(value: Bytes) -> Result<Self, PacketError> {
41        let mut info = MetaInfo::default();
42        let mut reader = TlvReader::new(value);
43        while !reader.is_empty() {
44            let (typ, val) = reader.read_tlv()?;
45            match typ {
46                t if t == tlv_type::CONTENT_TYPE => {
47                    let mut code = 0u64;
48                    for &b in val.iter() {
49                        code = (code << 8) | b as u64;
50                    }
51                    info.content_type = match code {
52                        0 => ContentType::Blob,
53                        1 => ContentType::Link,
54                        2 => ContentType::Key,
55                        3 => ContentType::Nack,
56                        c => ContentType::Other(c),
57                    };
58                }
59                t if t == tlv_type::FRESHNESS_PERIOD => {
60                    let mut ms = 0u64;
61                    for &b in val.iter() {
62                        ms = (ms << 8) | b as u64;
63                    }
64                    info.freshness_period = Some(Duration::from_millis(ms));
65                }
66                t if t == tlv_type::FINAL_BLOCK_ID => {
67                    info.final_block_id = Some(val);
68                }
69                _ => {}
70            }
71        }
72        Ok(info)
73    }
74}
75
76#[cfg(test)]
77mod tests {
78    use super::*;
79    use ndn_tlv::TlvWriter;
80
81    fn build_meta_info(
82        content_type: Option<u64>,
83        freshness_ms: Option<u64>,
84        final_block: Option<&[u8]>,
85    ) -> bytes::Bytes {
86        let mut w = TlvWriter::new();
87        if let Some(ct) = content_type {
88            w.write_tlv(crate::tlv_type::CONTENT_TYPE, &ct.to_be_bytes());
89        }
90        if let Some(ms) = freshness_ms {
91            w.write_tlv(crate::tlv_type::FRESHNESS_PERIOD, &ms.to_be_bytes());
92        }
93        if let Some(fb) = final_block {
94            w.write_tlv(crate::tlv_type::FINAL_BLOCK_ID, fb);
95        }
96        w.finish()
97    }
98
99    #[test]
100    fn decode_empty_meta_info() {
101        let mi = MetaInfo::decode(bytes::Bytes::new()).unwrap();
102        assert_eq!(mi.content_type, ContentType::Blob);
103        assert_eq!(mi.freshness_period, None);
104        assert_eq!(mi.final_block_id, None);
105    }
106
107    #[test]
108    fn decode_freshness_period() {
109        let raw = build_meta_info(None, Some(5000), None);
110        let mi = MetaInfo::decode(raw).unwrap();
111        assert_eq!(
112            mi.freshness_period,
113            Some(std::time::Duration::from_millis(5000))
114        );
115    }
116
117    #[test]
118    fn decode_content_type_blob() {
119        let raw = build_meta_info(Some(0), None, None);
120        let mi = MetaInfo::decode(raw).unwrap();
121        assert_eq!(mi.content_type, ContentType::Blob);
122    }
123
124    #[test]
125    fn decode_content_type_key() {
126        let raw = build_meta_info(Some(2), None, None);
127        let mi = MetaInfo::decode(raw).unwrap();
128        assert_eq!(mi.content_type, ContentType::Key);
129    }
130
131    #[test]
132    fn decode_content_type_other() {
133        let raw = build_meta_info(Some(99), None, None);
134        let mi = MetaInfo::decode(raw).unwrap();
135        assert_eq!(mi.content_type, ContentType::Other(99));
136    }
137
138    #[test]
139    fn decode_final_block_id() {
140        let raw = build_meta_info(None, None, Some(&[0x08, 0x01, b'5']));
141        let mi = MetaInfo::decode(raw).unwrap();
142        assert!(mi.final_block_id.is_some());
143        assert_eq!(mi.final_block_id.unwrap().as_ref(), &[0x08, 0x01, b'5']);
144    }
145
146    #[test]
147    fn content_type_code_roundtrip() {
148        let types = [
149            (ContentType::Blob, 0),
150            (ContentType::Link, 1),
151            (ContentType::Key, 2),
152            (ContentType::Nack, 3),
153            (ContentType::Other(42), 42),
154        ];
155        for (ct, code) in types {
156            assert_eq!(ct.code(), code);
157        }
158    }
159}