1use crate::error::TypeError;
2use crate::fields::{decode_bytes_value, decode_field_header, encode_bytes_field, skip_field};
3
4#[derive(Clone, Debug, PartialEq, Eq)]
26pub struct ExtensionBlock {
27 pub namespace: String,
28 pub type_name: String,
29 pub content: Vec<u8>,
30}
31
32impl ExtensionBlock {
33 pub fn encode_body(&self) -> Vec<u8> {
35 let mut buf = Vec::new();
36 encode_bytes_field(&mut buf, 1, self.namespace.as_bytes());
37 encode_bytes_field(&mut buf, 2, self.type_name.as_bytes());
38 encode_bytes_field(&mut buf, 3, &self.content);
39 buf
40 }
41
42 pub fn decode_body(mut buf: &[u8]) -> Result<Self, TypeError> {
44 let mut namespace: Option<String> = None;
45 let mut type_name: Option<String> = None;
46 let mut content: Option<Vec<u8>> = None;
47
48 while !buf.is_empty() {
49 let (header, n) = decode_field_header(buf)?;
50 buf = &buf[n..];
51
52 match header.field_id {
53 1 => {
54 let (data, n) = decode_bytes_value(buf)?;
55 buf = &buf[n..];
56 namespace = Some(String::from_utf8_lossy(data).into_owned());
57 }
58 2 => {
59 let (data, n) = decode_bytes_value(buf)?;
60 buf = &buf[n..];
61 type_name = Some(String::from_utf8_lossy(data).into_owned());
62 }
63 3 => {
64 let (data, n) = decode_bytes_value(buf)?;
65 buf = &buf[n..];
66 content = Some(data.to_vec());
67 }
68 _ => {
69 let n = skip_field(buf, header.wire_type)?;
70 buf = &buf[n..];
71 }
72 }
73 }
74
75 Ok(Self {
76 namespace: namespace.ok_or(TypeError::MissingRequiredField { field: "namespace" })?,
77 type_name: type_name.ok_or(TypeError::MissingRequiredField { field: "type_name" })?,
78 content: content.ok_or(TypeError::MissingRequiredField { field: "content" })?,
79 })
80 }
81}
82
83#[cfg(test)]
84mod tests {
85 use super::*;
86
87 #[test]
88 fn roundtrip_extension() {
89 let block = ExtensionBlock {
90 namespace: "myorg".to_string(),
91 type_name: "custom_metric".to_string(),
92 content: b"{\"latency_ms\": 42}".to_vec(),
93 };
94 let body = block.encode_body();
95 let decoded = ExtensionBlock::decode_body(&body).unwrap();
96 assert_eq!(decoded, block);
97 }
98
99 #[test]
100 fn roundtrip_empty_content() {
101 let block = ExtensionBlock {
102 namespace: "test".to_string(),
103 type_name: "marker".to_string(),
104 content: vec![],
105 };
106 let body = block.encode_body();
107 let decoded = ExtensionBlock::decode_body(&body).unwrap();
108 assert_eq!(decoded, block);
109 }
110}