1use core::time::Duration;
2
3use bytes::Bytes;
4
5use crate::{PacketError, tlv_type};
6use ndn_tlv::TlvReader;
7
8#[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#[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}