Skip to main content

lxmf_core/message/
payload.rs

1use serde::{Deserialize, Serialize};
2use serde_bytes::ByteBuf;
3
4use crate::error::LxmfError;
5use alloc::format;
6use alloc::string::ToString;
7use alloc::vec::Vec;
8
9#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
10pub struct Payload {
11    pub timestamp: f64,
12    pub content: Option<ByteBuf>,
13    pub title: Option<ByteBuf>,
14    pub fields: Option<rmpv::Value>,
15    pub stamp: Option<ByteBuf>,
16}
17
18impl Payload {
19    pub fn new(
20        timestamp: f64,
21        content: Option<Vec<u8>>,
22        title: Option<Vec<u8>>,
23        fields: Option<rmpv::Value>,
24        stamp: Option<Vec<u8>>,
25    ) -> Self {
26        Self {
27            timestamp,
28            content: content.map(ByteBuf::from),
29            title: title.map(ByteBuf::from),
30            fields,
31            stamp: stamp.map(ByteBuf::from),
32        }
33    }
34
35    pub fn to_msgpack(&self) -> Result<Vec<u8>, LxmfError> {
36        if let Some(stamp) = &self.stamp {
37            let list = (
38                self.timestamp,
39                self.title.clone(),
40                self.content.clone(),
41                self.fields.clone(),
42                stamp.clone(),
43            );
44            rmp_serde::to_vec(&list).map_err(|e| LxmfError::Encode(e.to_string()))
45        } else {
46            self.to_msgpack_without_stamp()
47        }
48    }
49
50    pub fn to_msgpack_without_stamp(&self) -> Result<Vec<u8>, LxmfError> {
51        let list = (self.timestamp, self.title.clone(), self.content.clone(), self.fields.clone());
52        rmp_serde::to_vec(&list).map_err(|e| LxmfError::Encode(e.to_string()))
53    }
54
55    pub fn from_msgpack(bytes: &[u8]) -> Result<Self, LxmfError> {
56        let value = rmp_serde::from_slice::<rmpv::Value>(bytes)
57            .map_err(|e| LxmfError::Decode(e.to_string()))?;
58        let rmpv::Value::Array(items) = value else {
59            return Err(LxmfError::Decode("invalid payload structure".into()));
60        };
61        if items.len() < 4 || items.len() > 5 {
62            return Err(LxmfError::Decode("invalid payload length".into()));
63        }
64        let timestamp = items
65            .first()
66            .and_then(|value| value.as_f64())
67            .ok_or_else(|| LxmfError::Decode("invalid payload timestamp".into()))?;
68        let title = value_to_bytes(items.get(1), "title")?.map(ByteBuf::from);
69        let content = value_to_bytes(items.get(2), "content")?.map(ByteBuf::from);
70        let fields = match items.get(3) {
71            Some(rmpv::Value::Nil) | None => None,
72            Some(value) => Some(value.clone()),
73        };
74        let stamp = if items.len() == 5 {
75            value_to_bytes(items.get(4), "stamp")?.map(ByteBuf::from)
76        } else {
77            None
78        };
79        Ok(Self { timestamp, content, title, fields, stamp })
80    }
81}
82
83fn value_to_bytes(value: Option<&rmpv::Value>, field: &str) -> Result<Option<Vec<u8>>, LxmfError> {
84    match value {
85        Some(rmpv::Value::Binary(bin)) => Ok(Some(bin.clone())),
86        Some(rmpv::Value::String(text)) => text
87            .as_str()
88            .map(|s| Some(s.as_bytes().to_vec()))
89            .ok_or_else(|| LxmfError::Decode(format!("invalid payload {field} string"))),
90        Some(rmpv::Value::Nil) | None => Ok(None),
91        _ => Err(LxmfError::Decode(format!("invalid payload {field}"))),
92    }
93}