lxmf_core/
payload_fields.rs1use crate::constants::FIELD_COMMANDS;
2use crate::LxmfError;
3use alloc::collections::BTreeMap;
4use alloc::string::ToString;
5use alloc::vec;
6use alloc::vec::Vec;
7
8#[derive(Debug, Clone, PartialEq, Eq)]
9pub struct CommandEntry {
10 pub command_id: u8,
11 pub payload: Vec<u8>,
12}
13
14impl CommandEntry {
15 pub fn from_text(command_id: u8, payload: &str) -> Self {
16 Self { command_id, payload: payload.as_bytes().to_vec() }
17 }
18
19 pub fn from_bytes(command_id: u8, payload: Vec<u8>) -> Self {
20 Self { command_id, payload }
21 }
22}
23
24#[derive(Debug, Clone, Default, PartialEq)]
25pub struct WireFields {
26 entries: BTreeMap<u8, rmpv::Value>,
27}
28
29impl WireFields {
30 pub fn new() -> Self {
31 Self::default()
32 }
33
34 pub fn insert_field(&mut self, field_id: u8, value: rmpv::Value) -> &mut Self {
35 self.entries.insert(field_id, value);
36 self
37 }
38
39 pub fn set_commands<I>(&mut self, commands: I) -> &mut Self
40 where
41 I: IntoIterator<Item = CommandEntry>,
42 {
43 let mut out = Vec::new();
44 for command in commands {
45 let entry = rmpv::Value::Map(vec![(
46 rmpv::Value::Integer((command.command_id as i64).into()),
47 rmpv::Value::Binary(command.payload),
48 )]);
49 out.push(entry);
50 }
51 self.entries.insert(FIELD_COMMANDS, rmpv::Value::Array(out));
52 self
53 }
54
55 pub fn to_rmpv(&self) -> rmpv::Value {
56 let mut entries = Vec::with_capacity(self.entries.len());
57 for (field_id, value) in &self.entries {
58 entries.push((rmpv::Value::Integer((*field_id as i64).into()), value.clone()));
59 }
60 rmpv::Value::Map(entries)
61 }
62
63 pub fn encode_msgpack(&self) -> Result<Vec<u8>, LxmfError> {
64 rmp_serde::to_vec(&self.to_rmpv()).map_err(|err| LxmfError::Encode(err.to_string()))
65 }
66}
67
68#[cfg(test)]
69mod tests {
70 use super::{CommandEntry, WireFields};
71 use crate::constants::FIELD_COMMANDS;
72
73 #[test]
74 fn commands_encode_with_integer_field_ids() {
75 let mut fields = WireFields::new();
76 fields.set_commands(vec![
77 CommandEntry::from_text(0x01, "ping"),
78 CommandEntry::from_bytes(0x02, vec![0xAA, 0xBB]),
79 ]);
80
81 let rmpv::Value::Map(entries) = fields.to_rmpv() else { panic!("expected map") };
82 assert_eq!(entries.len(), 1);
83 assert_eq!(entries[0].0.as_i64(), Some(FIELD_COMMANDS as i64));
84 let Some(cmds) = entries[0].1.as_array().cloned() else {
85 panic!("commands array expected")
86 };
87 assert_eq!(cmds.len(), 2);
88 }
89}