1use crate::enums::FormatHint;
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 DocumentBlock {
27 pub title: String,
28 pub content: Vec<u8>,
29 pub format_hint: FormatHint,
30}
31
32impl DocumentBlock {
33 pub fn encode_body(&self) -> Vec<u8> {
35 let mut buf = Vec::new();
36 encode_bytes_field(&mut buf, 1, self.title.as_bytes());
37 encode_bytes_field(&mut buf, 2, &self.content);
38 encode_varint_field(&mut buf, 3, u64::from(self.format_hint.to_wire_byte()));
39 buf
40 }
41
42 pub fn decode_body(mut buf: &[u8]) -> Result<Self, TypeError> {
44 let mut title: Option<String> = None;
45 let mut content: Option<Vec<u8>> = None;
46 let mut format_hint: Option<FormatHint> = 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 title = Some(String::from_utf8_lossy(data).into_owned());
57 }
58 2 => {
59 let (data, n) = decode_bytes_value(buf)?;
60 buf = &buf[n..];
61 content = Some(data.to_vec());
62 }
63 3 => {
64 let (v, n) = decode_varint_value(buf)?;
65 buf = &buf[n..];
66 format_hint = Some(FormatHint::from_wire_byte(v as u8)?);
67 }
68 _ => {
69 let n = skip_field(buf, header.wire_type)?;
70 buf = &buf[n..];
71 }
72 }
73 }
74
75 Ok(Self {
76 title: title.ok_or(TypeError::MissingRequiredField { field: "title" })?,
77 content: content.ok_or(TypeError::MissingRequiredField { field: "content" })?,
78 format_hint: format_hint.ok_or(TypeError::MissingRequiredField {
79 field: "format_hint",
80 })?,
81 })
82 }
83}
84
85#[cfg(test)]
86mod tests {
87 use super::*;
88
89 #[test]
90 fn roundtrip_markdown_doc() {
91 let block = DocumentBlock {
92 title: "README".to_string(),
93 content: b"# Hello\n\nThis is a test.".to_vec(),
94 format_hint: FormatHint::Markdown,
95 };
96 let body = block.encode_body();
97 let decoded = DocumentBlock::decode_body(&body).unwrap();
98 assert_eq!(decoded, block);
99 }
100
101 #[test]
102 fn roundtrip_plain_text() {
103 let block = DocumentBlock {
104 title: "notes.txt".to_string(),
105 content: b"Just plain text.".to_vec(),
106 format_hint: FormatHint::Plain,
107 };
108 let body = block.encode_body();
109 let decoded = DocumentBlock::decode_body(&body).unwrap();
110 assert_eq!(decoded, block);
111 }
112}