use crate::error::Result;
use crate::protocol::header::Header;
use std::collections::HashMap;
pub trait Message: Sized {
fn message_type() -> &'static str;
fn encode_content(&self) -> Result<Vec<u8>>;
fn decode_content(data: &[u8]) -> Result<Self>;
}
#[derive(Debug)]
pub struct IgtlMessage<T: Message> {
pub header: Header,
pub extended_header: Option<Vec<u8>>,
pub content: T,
pub metadata: Option<HashMap<String, String>>,
}
impl<T: Message> IgtlMessage<T> {
pub fn new(content: T, device_name: &str) -> Result<Self> {
use crate::protocol::header::{DeviceName, Timestamp, TypeName};
let timestamp = Timestamp::now();
let content_bytes = content.encode_content()?;
let body_size = content_bytes.len() as u64;
let header = Header {
version: 2, type_name: TypeName::new(T::message_type())?,
device_name: DeviceName::new(device_name)?,
timestamp,
body_size,
crc: 0, };
Ok(IgtlMessage {
header,
extended_header: None,
content,
metadata: None,
})
}
pub fn set_extended_header(&mut self, data: Vec<u8>) {
self.extended_header = Some(data);
if self.header.version < 3 {
self.header.version = 3;
}
}
pub fn get_extended_header(&self) -> Option<&[u8]> {
self.extended_header.as_deref()
}
pub fn clear_extended_header(&mut self) {
self.extended_header = None;
if self.metadata.is_none() && self.header.version == 3 {
self.header.version = 2;
}
}
pub fn set_metadata(&mut self, metadata: HashMap<String, String>) {
self.metadata = Some(metadata);
if self.header.version < 3 {
self.header.version = 3;
}
}
pub fn add_metadata(&mut self, key: String, value: String) {
if self.metadata.is_none() {
self.metadata = Some(HashMap::new());
if self.header.version < 3 {
self.header.version = 3;
}
}
self.metadata.as_mut().unwrap().insert(key, value);
}
pub fn get_metadata(&self) -> Option<&HashMap<String, String>> {
self.metadata.as_ref()
}
pub fn clear_metadata(&mut self) {
self.metadata = None;
if self.extended_header.is_none() && self.header.version == 3 {
self.header.version = 2;
}
}
pub fn encode(&self) -> Result<Vec<u8>> {
use crate::protocol::crc::calculate_crc;
let content_bytes = self.content.encode_content()?;
let metadata_bytes = if self.header.version >= 3 && self.metadata.is_some() {
let metadata = self.metadata.as_ref().unwrap();
let mut meta_buf = Vec::new();
let meta_header_size = (metadata.len() as u16).to_be_bytes();
meta_buf.extend_from_slice(&meta_header_size);
for (key, value) in metadata.iter() {
let key_bytes = key.as_bytes();
meta_buf.extend_from_slice(&(key_bytes.len() as u16).to_be_bytes());
meta_buf.extend_from_slice(key_bytes);
let value_bytes = value.as_bytes();
meta_buf.extend_from_slice(&(value_bytes.len() as u16).to_be_bytes());
meta_buf.extend_from_slice(value_bytes);
}
meta_buf
} else {
Vec::new()
};
let body_bytes = if self.header.version >= 3 && self.extended_header.is_some() {
let ext_header = self.extended_header.as_ref().unwrap();
let ext_header_size = ext_header.len() as u16;
let mut body = Vec::with_capacity(
2 + ext_header.len() + content_bytes.len() + metadata_bytes.len(),
);
body.extend_from_slice(&ext_header_size.to_be_bytes());
body.extend_from_slice(ext_header);
body.extend_from_slice(&content_bytes);
body.extend_from_slice(&metadata_bytes);
body
} else if self.header.version >= 3 && !metadata_bytes.is_empty() {
let mut body = Vec::with_capacity(2 + content_bytes.len() + metadata_bytes.len());
body.extend_from_slice(&0u16.to_be_bytes());
body.extend_from_slice(&content_bytes);
body.extend_from_slice(&metadata_bytes);
body
} else {
content_bytes
};
let mut header = self.header.clone();
header.body_size = body_bytes.len() as u64;
header.crc = calculate_crc(&body_bytes);
let mut buf = Vec::with_capacity(Header::SIZE + body_bytes.len());
buf.extend_from_slice(&header.encode());
buf.extend_from_slice(&body_bytes);
Ok(buf)
}
pub fn decode(data: &[u8]) -> Result<Self> {
Self::decode_with_options(data, true)
}
pub fn decode_with_options(data: &[u8], verify_crc: bool) -> Result<Self> {
use crate::error::IgtlError;
use crate::protocol::crc::calculate_crc;
if data.len() < Header::SIZE {
return Err(IgtlError::InvalidSize {
expected: Header::SIZE,
actual: data.len(),
});
}
let header = Header::decode(&data[..Header::SIZE])?;
let body_start = Header::SIZE;
let body_end = body_start + header.body_size as usize;
if data.len() < body_end {
return Err(IgtlError::InvalidSize {
expected: body_end,
actual: data.len(),
});
}
let body_bytes = &data[body_start..body_end];
if verify_crc {
let calculated_crc = calculate_crc(body_bytes);
if calculated_crc != header.crc {
return Err(IgtlError::CrcMismatch {
expected: header.crc,
actual: calculated_crc,
});
}
}
let (extended_header, remaining_bytes, has_ext_header_field) =
if header.version >= 3 && body_bytes.len() >= 2 {
let ext_header_size = u16::from_be_bytes([body_bytes[0], body_bytes[1]]) as usize;
if ext_header_size > 0 && body_bytes.len() >= 2 + ext_header_size {
let ext_header_data = body_bytes[2..2 + ext_header_size].to_vec();
let content_start = 2 + ext_header_size;
(Some(ext_header_data), &body_bytes[content_start..], true)
} else if ext_header_size == 0 && body_bytes.len() >= 2 {
(Some(Vec::new()), &body_bytes[2..], true)
} else {
return Err(IgtlError::InvalidSize {
expected: 2 + ext_header_size,
actual: body_bytes.len(),
});
}
} else {
(None, body_bytes, false)
};
let (content_bytes, metadata) = if header.version >= 3 && has_ext_header_field {
match T::decode_content(remaining_bytes) {
Ok(content) => {
let actual_content_size = content.encode_content()?.len();
if remaining_bytes.len() > actual_content_size {
let content_part = &remaining_bytes[..actual_content_size];
let metadata_part = &remaining_bytes[actual_content_size..];
let parsed_metadata = Self::decode_metadata(metadata_part)?;
(content_part, parsed_metadata)
} else {
(remaining_bytes, None)
}
}
Err(IgtlError::InvalidSize { expected, .. })
if remaining_bytes.len() > expected =>
{
let content_part = &remaining_bytes[..expected];
let metadata_part = &remaining_bytes[expected..];
if T::decode_content(content_part).is_ok() {
let parsed_metadata = Self::decode_metadata(metadata_part)?;
(content_part, parsed_metadata)
} else {
(remaining_bytes, None)
}
}
Err(_) => {
(remaining_bytes, None)
}
}
} else {
(remaining_bytes, None)
};
let content = T::decode_content(content_bytes)?;
Ok(IgtlMessage {
header,
extended_header,
content,
metadata,
})
}
fn decode_metadata(data: &[u8]) -> Result<Option<HashMap<String, String>>> {
use crate::error::IgtlError;
if data.len() < 2 {
return Ok(None);
}
let metadata_count = u16::from_be_bytes([data[0], data[1]]) as usize;
if metadata_count == 0 {
return Ok(None);
}
let mut metadata = HashMap::new();
let mut offset = 2;
for _ in 0..metadata_count {
if offset + 2 > data.len() {
return Err(IgtlError::InvalidSize {
expected: offset + 2,
actual: data.len(),
});
}
let key_size = u16::from_be_bytes([data[offset], data[offset + 1]]) as usize;
offset += 2;
if offset + key_size > data.len() {
return Err(IgtlError::InvalidSize {
expected: offset + key_size,
actual: data.len(),
});
}
let key = String::from_utf8(data[offset..offset + key_size].to_vec())?;
offset += key_size;
if offset + 2 > data.len() {
return Err(IgtlError::InvalidSize {
expected: offset + 2,
actual: data.len(),
});
}
let value_size = u16::from_be_bytes([data[offset], data[offset + 1]]) as usize;
offset += 2;
if offset + value_size > data.len() {
return Err(IgtlError::InvalidSize {
expected: offset + value_size,
actual: data.len(),
});
}
let value = String::from_utf8(data[offset..offset + value_size].to_vec())?;
offset += value_size;
metadata.insert(key, value);
}
Ok(Some(metadata))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::protocol::types::{CapabilityMessage, StatusMessage, TransformMessage};
struct TestMessage {
data: Vec<u8>,
}
impl Message for TestMessage {
fn message_type() -> &'static str {
"TEST"
}
fn encode_content(&self) -> Result<Vec<u8>> {
Ok(self.data.clone())
}
fn decode_content(data: &[u8]) -> Result<Self> {
Ok(TestMessage {
data: data.to_vec(),
})
}
}
#[test]
fn test_message_trait() {
assert_eq!(TestMessage::message_type(), "TEST");
}
#[test]
fn test_message_encode_decode() {
let original = TestMessage {
data: vec![1, 2, 3, 4, 5],
};
let encoded = original.encode_content().unwrap();
let decoded = TestMessage::decode_content(&encoded).unwrap();
assert_eq!(original.data, decoded.data);
}
#[test]
fn test_full_message_roundtrip_transform() {
let transform = TransformMessage::identity();
let msg = IgtlMessage::new(transform.clone(), "TestDevice").unwrap();
let encoded = msg.encode().unwrap();
let decoded = IgtlMessage::<TransformMessage>::decode(&encoded).unwrap();
assert_eq!(decoded.header.version, 2);
assert_eq!(decoded.header.type_name.as_str().unwrap(), "TRANSFORM");
assert_eq!(decoded.header.device_name.as_str().unwrap(), "TestDevice");
assert_eq!(decoded.header.body_size, 48);
assert_eq!(decoded.content, transform);
}
#[test]
fn test_full_message_roundtrip_status() {
let status = StatusMessage::ok("Operation successful");
let msg = IgtlMessage::new(status.clone(), "StatusDevice").unwrap();
let encoded = msg.encode().unwrap();
let decoded = IgtlMessage::<StatusMessage>::decode(&encoded).unwrap();
assert_eq!(decoded.header.type_name.as_str().unwrap(), "STATUS");
assert_eq!(decoded.content, status);
}
#[test]
fn test_full_message_roundtrip_capability() {
let capability = CapabilityMessage::new(vec![
"TRANSFORM".to_string(),
"STATUS".to_string(),
"IMAGE".to_string(),
]);
let msg = IgtlMessage::new(capability.clone(), "CapDevice").unwrap();
let encoded = msg.encode().unwrap();
let decoded = IgtlMessage::<CapabilityMessage>::decode(&encoded).unwrap();
assert_eq!(decoded.header.type_name.as_str().unwrap(), "CAPABILITY");
assert_eq!(decoded.content, capability);
}
#[test]
fn test_timestamp_reasonable() {
let transform = TransformMessage::identity();
let msg = IgtlMessage::new(transform, "TestDevice").unwrap();
let now = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_secs() as u32;
let one_year_ago = now - (365 * 24 * 60 * 60);
assert!(msg.header.timestamp.seconds >= one_year_ago);
assert!(msg.header.timestamp.seconds <= now + 1); }
#[test]
fn test_crc_verification() {
let transform = TransformMessage::identity();
let msg = IgtlMessage::new(transform, "TestDevice").unwrap();
let mut encoded = msg.encode().unwrap();
let content_start = Header::SIZE;
encoded[content_start] ^= 0xFF;
let result = IgtlMessage::<TransformMessage>::decode(&encoded);
assert!(matches!(
result,
Err(crate::error::IgtlError::CrcMismatch { .. })
));
}
#[test]
fn test_message_size_calculation() {
let transform = TransformMessage::identity();
let msg = IgtlMessage::new(transform, "TestDevice").unwrap();
let encoded = msg.encode().unwrap();
assert_eq!(encoded.len(), 106);
}
#[test]
fn test_decode_short_buffer() {
let short_data = vec![0u8; 30];
let result = IgtlMessage::<TransformMessage>::decode(&short_data);
assert!(matches!(
result,
Err(crate::error::IgtlError::InvalidSize { .. })
));
}
#[test]
fn test_extended_header_set_get() {
let transform = TransformMessage::identity();
let mut msg = IgtlMessage::new(transform, "TestDevice").unwrap();
assert_eq!(msg.get_extended_header(), None);
assert_eq!(msg.header.version, 2);
let ext_header = vec![0x01, 0x02, 0x03, 0x04];
msg.set_extended_header(ext_header.clone());
assert_eq!(msg.header.version, 3);
assert_eq!(msg.get_extended_header(), Some(ext_header.as_slice()));
}
#[test]
fn test_extended_header_clear() {
let transform = TransformMessage::identity();
let mut msg = IgtlMessage::new(transform, "TestDevice").unwrap();
msg.set_extended_header(vec![0x01, 0x02, 0x03, 0x04]);
assert_eq!(msg.header.version, 3);
msg.clear_extended_header();
assert_eq!(msg.get_extended_header(), None);
assert_eq!(msg.header.version, 2);
}
#[test]
fn test_version3_encode_decode_with_extended_header() {
let transform = TransformMessage::identity();
let mut msg = IgtlMessage::new(transform.clone(), "TestDevice").unwrap();
let ext_header = vec![0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF];
msg.set_extended_header(ext_header.clone());
let encoded = msg.encode().unwrap();
let decoded = IgtlMessage::<TransformMessage>::decode(&encoded).unwrap();
assert_eq!(decoded.header.version, 3);
assert_eq!(decoded.get_extended_header(), Some(ext_header.as_slice()));
assert_eq!(decoded.content, transform);
}
#[test]
fn test_version3_encode_decode_without_extended_header() {
let status = StatusMessage::ok("Test message");
let msg = IgtlMessage::new(status.clone(), "TestDevice").unwrap();
let encoded = msg.encode().unwrap();
let decoded = IgtlMessage::<StatusMessage>::decode(&encoded).unwrap();
assert_eq!(decoded.header.version, 2);
assert_eq!(decoded.get_extended_header(), None);
assert_eq!(decoded.content, status);
}
#[test]
fn test_extended_header_body_size_calculation() {
let transform = TransformMessage::identity();
let mut msg = IgtlMessage::new(transform, "TestDevice").unwrap();
let ext_header = vec![0x01, 0x02, 0x03, 0x04]; msg.set_extended_header(ext_header);
let encoded = msg.encode().unwrap();
assert_eq!(encoded.len(), 112);
let decoded = IgtlMessage::<TransformMessage>::decode(&encoded).unwrap();
assert_eq!(decoded.header.body_size, 2 + 4 + 48); }
#[test]
fn test_extended_header_empty() {
let transform = TransformMessage::identity();
let mut msg = IgtlMessage::new(transform.clone(), "TestDevice").unwrap();
msg.set_extended_header(vec![]);
let encoded = msg.encode().unwrap();
let decoded = IgtlMessage::<TransformMessage>::decode(&encoded).unwrap();
assert_eq!(decoded.header.version, 3);
assert_eq!(decoded.get_extended_header(), Some(&[] as &[u8]));
assert_eq!(decoded.content, transform);
}
#[test]
fn test_extended_header_large() {
let status = StatusMessage::ok("Test");
let mut msg = IgtlMessage::new(status.clone(), "TestDevice").unwrap();
let ext_header = vec![0xAB; 1024];
msg.set_extended_header(ext_header.clone());
let encoded = msg.encode().unwrap();
let decoded = IgtlMessage::<StatusMessage>::decode(&encoded).unwrap();
assert_eq!(decoded.header.version, 3);
assert_eq!(decoded.get_extended_header(), Some(ext_header.as_slice()));
assert_eq!(decoded.content, status);
}
#[test]
fn test_version3_crc_includes_extended_header() {
let transform = TransformMessage::identity();
let mut msg = IgtlMessage::new(transform, "TestDevice").unwrap();
msg.set_extended_header(vec![0x01, 0x02, 0x03, 0x04]);
let mut encoded = msg.encode().unwrap();
encoded[Header::SIZE + 2] ^= 0xFF;
let result = IgtlMessage::<TransformMessage>::decode(&encoded);
assert!(matches!(
result,
Err(crate::error::IgtlError::CrcMismatch { .. })
));
}
#[test]
fn test_backward_compatibility_version2() {
let capability = CapabilityMessage::new(vec!["TRANSFORM".to_string()]);
let msg = IgtlMessage::new(capability.clone(), "Device").unwrap();
assert_eq!(msg.header.version, 2);
let encoded = msg.encode().unwrap();
let decoded = IgtlMessage::<CapabilityMessage>::decode(&encoded).unwrap();
assert_eq!(decoded.header.version, 2);
assert_eq!(decoded.get_extended_header(), None);
assert_eq!(decoded.content, capability);
}
#[test]
fn test_metadata_set_get() {
let transform = TransformMessage::identity();
let mut msg = IgtlMessage::new(transform, "TestDevice").unwrap();
assert_eq!(msg.get_metadata(), None);
assert_eq!(msg.header.version, 2);
let mut metadata = HashMap::new();
metadata.insert("priority".to_string(), "high".to_string());
metadata.insert("sequence".to_string(), "42".to_string());
msg.set_metadata(metadata.clone());
assert_eq!(msg.header.version, 3);
assert_eq!(msg.get_metadata(), Some(&metadata));
}
#[test]
fn test_metadata_add() {
let status = StatusMessage::ok("Test");
let mut msg = IgtlMessage::new(status, "TestDevice").unwrap();
assert_eq!(msg.header.version, 2);
msg.add_metadata("key1".to_string(), "value1".to_string());
assert_eq!(msg.header.version, 3);
msg.add_metadata("key2".to_string(), "value2".to_string());
let metadata = msg.get_metadata().unwrap();
assert_eq!(metadata.get("key1"), Some(&"value1".to_string()));
assert_eq!(metadata.get("key2"), Some(&"value2".to_string()));
}
#[test]
fn test_metadata_clear() {
let transform = TransformMessage::identity();
let mut msg = IgtlMessage::new(transform, "TestDevice").unwrap();
msg.add_metadata("test".to_string(), "value".to_string());
assert_eq!(msg.header.version, 3);
msg.clear_metadata();
assert_eq!(msg.get_metadata(), None);
assert_eq!(msg.header.version, 2);
}
#[test]
fn test_version3_encode_decode_with_metadata() {
let transform = TransformMessage::identity();
let mut msg = IgtlMessage::new(transform.clone(), "TestDevice").unwrap();
let mut metadata = HashMap::new();
metadata.insert("priority".to_string(), "high".to_string());
metadata.insert("timestamp".to_string(), "123456".to_string());
msg.set_metadata(metadata.clone());
let encoded = msg.encode().unwrap();
let decoded = IgtlMessage::<TransformMessage>::decode(&encoded).unwrap();
assert_eq!(decoded.header.version, 3);
let decoded_metadata = decoded.get_metadata().unwrap();
assert_eq!(decoded_metadata.get("priority"), Some(&"high".to_string()));
assert_eq!(
decoded_metadata.get("timestamp"),
Some(&"123456".to_string())
);
assert_eq!(decoded.content, transform);
}
#[test]
fn test_version3_with_extended_header_and_metadata() {
let status = StatusMessage::ok("Test message");
let mut msg = IgtlMessage::new(status.clone(), "TestDevice").unwrap();
msg.set_extended_header(vec![0xAA, 0xBB, 0xCC, 0xDD]);
msg.add_metadata("key1".to_string(), "value1".to_string());
msg.add_metadata("key2".to_string(), "value2".to_string());
let encoded = msg.encode().unwrap();
let decoded = IgtlMessage::<StatusMessage>::decode(&encoded).unwrap();
assert_eq!(decoded.header.version, 3);
let expected_ext_header: &[u8] = &[0xAA, 0xBB, 0xCC, 0xDD];
assert_eq!(decoded.get_extended_header(), Some(expected_ext_header));
let metadata = decoded.get_metadata().unwrap();
assert_eq!(metadata.get("key1"), Some(&"value1".to_string()));
assert_eq!(metadata.get("key2"), Some(&"value2".to_string()));
assert_eq!(decoded.content, status);
}
#[test]
fn test_metadata_empty() {
let transform = TransformMessage::identity();
let mut msg = IgtlMessage::new(transform.clone(), "TestDevice").unwrap();
msg.set_metadata(HashMap::new());
let encoded = msg.encode().unwrap();
let decoded = IgtlMessage::<TransformMessage>::decode(&encoded).unwrap();
assert_eq!(decoded.get_metadata(), None);
assert_eq!(decoded.content, transform);
}
#[test]
fn test_metadata_utf8_values() {
let status = StatusMessage::ok("Test");
let mut msg = IgtlMessage::new(status.clone(), "TestDevice").unwrap();
msg.add_metadata("name".to_string(), "日本語".to_string());
msg.add_metadata("emoji".to_string(), "🎉✨".to_string());
let encoded = msg.encode().unwrap();
let decoded = IgtlMessage::<StatusMessage>::decode(&encoded).unwrap();
let metadata = decoded.get_metadata().unwrap();
assert_eq!(metadata.get("name"), Some(&"日本語".to_string()));
assert_eq!(metadata.get("emoji"), Some(&"🎉✨".to_string()));
}
#[test]
fn test_decode_with_crc_verification_enabled() {
let transform = TransformMessage::identity();
let msg = IgtlMessage::new(transform.clone(), "TestDevice").unwrap();
let encoded = msg.encode().unwrap();
let decoded = IgtlMessage::<TransformMessage>::decode_with_options(&encoded, true).unwrap();
assert_eq!(decoded.content, transform);
}
#[test]
fn test_decode_with_crc_verification_disabled() {
let transform = TransformMessage::identity();
let msg = IgtlMessage::new(transform.clone(), "TestDevice").unwrap();
let mut encoded = msg.encode().unwrap();
encoded[Header::SIZE] ^= 0xFF;
let result_with_crc = IgtlMessage::<TransformMessage>::decode_with_options(&encoded, true);
assert!(matches!(
result_with_crc,
Err(crate::error::IgtlError::CrcMismatch { .. })
));
let result_without_crc =
IgtlMessage::<TransformMessage>::decode_with_options(&encoded, false);
assert!(result_without_crc.is_ok());
}
#[test]
fn test_decode_default_uses_crc_verification() {
let transform = TransformMessage::identity();
let msg = IgtlMessage::new(transform, "TestDevice").unwrap();
let mut encoded = msg.encode().unwrap();
encoded[Header::SIZE] ^= 0xFF;
let result = IgtlMessage::<TransformMessage>::decode(&encoded);
assert!(matches!(
result,
Err(crate::error::IgtlError::CrcMismatch { .. })
));
}
#[test]
fn test_crc_skip_performance() {
let status = StatusMessage::ok("Performance test");
let msg = IgtlMessage::new(status.clone(), "TestDevice").unwrap();
let encoded = msg.encode().unwrap();
let decoded_with_crc =
IgtlMessage::<StatusMessage>::decode_with_options(&encoded, true).unwrap();
let decoded_without_crc =
IgtlMessage::<StatusMessage>::decode_with_options(&encoded, false).unwrap();
assert_eq!(decoded_with_crc.content, decoded_without_crc.content);
assert_eq!(decoded_with_crc.content, status);
}
#[test]
fn test_version3_crc_skip_with_extended_header() {
let transform = TransformMessage::identity();
let mut msg = IgtlMessage::new(transform.clone(), "TestDevice").unwrap();
msg.set_extended_header(vec![0x01, 0x02, 0x03, 0x04]);
let encoded = msg.encode().unwrap();
let decoded =
IgtlMessage::<TransformMessage>::decode_with_options(&encoded, false).unwrap();
assert_eq!(decoded.header.version, 3);
let expected: &[u8] = &[0x01, 0x02, 0x03, 0x04];
assert_eq!(decoded.get_extended_header(), Some(expected));
assert_eq!(decoded.content, transform);
}
#[test]
fn test_version3_crc_skip_with_metadata() {
let status = StatusMessage::ok("Test");
let mut msg = IgtlMessage::new(status.clone(), "TestDevice").unwrap();
msg.add_metadata("key1".to_string(), "value1".to_string());
msg.add_metadata("key2".to_string(), "value2".to_string());
let encoded = msg.encode().unwrap();
let decoded = IgtlMessage::<StatusMessage>::decode_with_options(&encoded, false).unwrap();
assert_eq!(decoded.header.version, 3);
let metadata = decoded.get_metadata().unwrap();
assert_eq!(metadata.get("key1"), Some(&"value1".to_string()));
assert_eq!(metadata.get("key2"), Some(&"value2".to_string()));
assert_eq!(decoded.content, status);
}
}