1use crate::enums::MediaType;
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 ImageBlock {
27 pub media_type: MediaType,
28 pub alt_text: String,
29 pub data: Vec<u8>,
33}
34
35impl ImageBlock {
36 pub fn encode_body(&self) -> Vec<u8> {
38 let mut buf = Vec::new();
39 encode_varint_field(&mut buf, 1, u64::from(self.media_type.to_wire_byte()));
40 encode_bytes_field(&mut buf, 2, self.alt_text.as_bytes());
41 encode_bytes_field(&mut buf, 3, &self.data);
42 buf
43 }
44
45 pub fn decode_body(mut buf: &[u8]) -> Result<Self, TypeError> {
47 let mut media_type: Option<MediaType> = None;
48 let mut alt_text: Option<String> = None;
49 let mut data: 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 media_type = Some(MediaType::from_wire_byte(v as u8)?);
60 }
61 2 => {
62 let (d, n) = decode_bytes_value(buf)?;
63 buf = &buf[n..];
64 alt_text = Some(String::from_utf8_lossy(d).into_owned());
65 }
66 3 => {
67 let (d, n) = decode_bytes_value(buf)?;
68 buf = &buf[n..];
69 data = Some(d.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 media_type: media_type.ok_or(TypeError::MissingRequiredField {
80 field: "media_type",
81 })?,
82 alt_text: alt_text.ok_or(TypeError::MissingRequiredField { field: "alt_text" })?,
83 data: data.ok_or(TypeError::MissingRequiredField { field: "data" })?,
84 })
85 }
86}
87
88#[cfg(test)]
89mod tests {
90 use super::*;
91
92 #[test]
93 fn roundtrip_inline_png() {
94 let block = ImageBlock {
95 media_type: MediaType::Png,
96 alt_text: "A screenshot of the app".to_string(),
97 data: vec![0x89, 0x50, 0x4E, 0x47], };
99 let body = block.encode_body();
100 let decoded = ImageBlock::decode_body(&body).unwrap();
101 assert_eq!(decoded, block);
102 }
103
104 #[test]
105 fn roundtrip_svg_reference() {
106 let block = ImageBlock {
107 media_type: MediaType::Svg,
108 alt_text: "Architecture diagram".to_string(),
109 data: b"https://example.com/diagram.svg".to_vec(),
110 };
111 let body = block.encode_body();
112 let decoded = ImageBlock::decode_body(&body).unwrap();
113 assert_eq!(decoded, block);
114 }
115}