bcp_types/
structured_data.rs1use crate::enums::DataFormat;
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#[derive(Clone, Debug, PartialEq, Eq)]
26pub struct StructuredDataBlock {
27 pub format: DataFormat,
28 pub schema: Option<String>,
30 pub content: Vec<u8>,
31}
32
33impl StructuredDataBlock {
34 pub fn encode_body(&self) -> Vec<u8> {
36 let mut buf = Vec::new();
37 encode_varint_field(&mut buf, 1, u64::from(self.format.to_wire_byte()));
38 if let Some(ref schema) = self.schema {
39 encode_bytes_field(&mut buf, 2, schema.as_bytes());
40 }
41 encode_bytes_field(&mut buf, 3, &self.content);
42 buf
43 }
44
45 pub fn decode_body(mut buf: &[u8]) -> Result<Self, TypeError> {
47 let mut format: Option<DataFormat> = None;
48 let mut schema: Option<String> = None;
49 let mut content: Option<Vec<u8>> = None;
50
51 while !buf.is_empty() {
52 let (header, n) = decode_field_header(buf)?;
53 buf = &buf[n..];
54
55 match header.field_id {
56 1 => {
57 let (v, n) = decode_varint_value(buf)?;
58 buf = &buf[n..];
59 format = Some(DataFormat::from_wire_byte(v as u8)?);
60 }
61 2 => {
62 let (data, n) = decode_bytes_value(buf)?;
63 buf = &buf[n..];
64 schema = Some(String::from_utf8_lossy(data).into_owned());
65 }
66 3 => {
67 let (data, n) = decode_bytes_value(buf)?;
68 buf = &buf[n..];
69 content = Some(data.to_vec());
70 }
71 _ => {
72 let n = skip_field(buf, header.wire_type)?;
73 buf = &buf[n..];
74 }
75 }
76 }
77
78 Ok(Self {
79 format: format.ok_or(TypeError::MissingRequiredField { field: "format" })?,
80 schema,
81 content: content.ok_or(TypeError::MissingRequiredField { field: "content" })?,
82 })
83 }
84}
85
86#[cfg(test)]
87mod tests {
88 use super::*;
89
90 #[test]
91 fn roundtrip_json_no_schema() {
92 let block = StructuredDataBlock {
93 format: DataFormat::Json,
94 schema: None,
95 content: b"{\"key\": \"value\"}".to_vec(),
96 };
97 let body = block.encode_body();
98 let decoded = StructuredDataBlock::decode_body(&body).unwrap();
99 assert_eq!(decoded, block);
100 }
101
102 #[test]
103 fn roundtrip_csv_with_schema() {
104 let block = StructuredDataBlock {
105 format: DataFormat::Csv,
106 schema: Some("name,age,city".to_string()),
107 content: b"Alice,30,NYC\nBob,25,LA".to_vec(),
108 };
109 let body = block.encode_body();
110 let decoded = StructuredDataBlock::decode_body(&body).unwrap();
111 assert_eq!(decoded, block);
112 }
113}