Skip to main content

bcp_types/
annotation.rs

1use crate::enums::AnnotationKind;
2use crate::error::TypeError;
3use crate::fields::{
4    decode_bytes_value, decode_field_header, decode_varint_value, encode_bytes_field,
5    encode_varint_field, skip_field,
6};
7
8/// ANNOTATION block — metadata overlay for other blocks.
9///
10/// Annotations are secondary blocks that attach metadata to a primary
11/// block identified by `target_block_id` (the zero-based index of the
12/// target block in the stream). The `kind` field determines how the
13/// `value` payload should be interpreted:
14///
15/// - `Priority`: value is a [`Priority`](crate::enums::Priority) byte
16/// - `Summary`: value is UTF-8 text summarizing the target block
17/// - `Tag`: value is a UTF-8 label/tag string
18///
19/// Field layout within body:
20///
21/// ```text
22/// ┌──────────┬───────────┬─────────────────┬──────────────────────┐
23/// │ Field ID │ Wire Type │ Name            │ Description          │
24/// ├──────────┼───────────┼─────────────────┼──────────────────────┤
25/// │ 1        │ Varint    │ target_block_id │ Index of target blk  │
26/// │ 2        │ Varint    │ kind            │ AnnotationKind byte  │
27/// │ 3        │ Bytes     │ value           │ Annotation payload   │
28/// └──────────┴───────────┴─────────────────┴──────────────────────┘
29/// ```
30#[derive(Clone, Debug, PartialEq, Eq)]
31pub struct AnnotationBlock {
32    pub target_block_id: u32,
33    pub kind: AnnotationKind,
34    pub value: Vec<u8>,
35}
36
37impl AnnotationBlock {
38    /// Serialize this block's fields into a TLV-encoded body.
39    pub fn encode_body(&self) -> Vec<u8> {
40        let mut buf = Vec::new();
41        encode_varint_field(&mut buf, 1, u64::from(self.target_block_id));
42        encode_varint_field(&mut buf, 2, u64::from(self.kind.to_wire_byte()));
43        encode_bytes_field(&mut buf, 3, &self.value);
44        buf
45    }
46
47    /// Deserialize an ANNOTATION block from a TLV-encoded body.
48    pub fn decode_body(mut buf: &[u8]) -> Result<Self, TypeError> {
49        let mut target_block_id: Option<u32> = None;
50        let mut kind: Option<AnnotationKind> = None;
51        let mut value: Option<Vec<u8>> = None;
52
53        while !buf.is_empty() {
54            let (header, n) = decode_field_header(buf)?;
55            buf = &buf[n..];
56
57            match header.field_id {
58                1 => {
59                    let (v, n) = decode_varint_value(buf)?;
60                    buf = &buf[n..];
61                    target_block_id = Some(v as u32);
62                }
63                2 => {
64                    let (v, n) = decode_varint_value(buf)?;
65                    buf = &buf[n..];
66                    kind = Some(AnnotationKind::from_wire_byte(v as u8)?);
67                }
68                3 => {
69                    let (data, n) = decode_bytes_value(buf)?;
70                    buf = &buf[n..];
71                    value = Some(data.to_vec());
72                }
73                _ => {
74                    let n = skip_field(buf, header.wire_type)?;
75                    buf = &buf[n..];
76                }
77            }
78        }
79
80        Ok(Self {
81            target_block_id: target_block_id.ok_or(TypeError::MissingRequiredField {
82                field: "target_block_id",
83            })?,
84            kind: kind.ok_or(TypeError::MissingRequiredField { field: "kind" })?,
85            value: value.ok_or(TypeError::MissingRequiredField { field: "value" })?,
86        })
87    }
88}
89
90#[cfg(test)]
91mod tests {
92    use super::*;
93
94    #[test]
95    fn roundtrip_priority_annotation() {
96        let block = AnnotationBlock {
97            target_block_id: 0,
98            kind: AnnotationKind::Priority,
99            value: vec![0x01], // Critical
100        };
101        let body = block.encode_body();
102        let decoded = AnnotationBlock::decode_body(&body).unwrap();
103        assert_eq!(decoded, block);
104    }
105
106    #[test]
107    fn roundtrip_tag_annotation() {
108        let block = AnnotationBlock {
109            target_block_id: 5,
110            kind: AnnotationKind::Tag,
111            value: b"security-critical".to_vec(),
112        };
113        let body = block.encode_body();
114        let decoded = AnnotationBlock::decode_body(&body).unwrap();
115        assert_eq!(decoded, block);
116    }
117
118    #[test]
119    fn roundtrip_summary_annotation() {
120        let block = AnnotationBlock {
121            target_block_id: 2,
122            kind: AnnotationKind::Summary,
123            value: b"Authentication middleware for JWT tokens".to_vec(),
124        };
125        let body = block.encode_body();
126        let decoded = AnnotationBlock::decode_body(&body).unwrap();
127        assert_eq!(decoded, block);
128    }
129}