clasp_core/
codec.rs

1//! MessagePack encoding/decoding for Clasp messages
2
3use crate::{Error, Frame, Message, QoS, Result};
4use bytes::Bytes;
5
6/// Encode a message to MessagePack bytes
7pub fn encode_message(message: &Message) -> Result<Bytes> {
8    let bytes = rmp_serde::to_vec_named(message)?;
9    Ok(Bytes::from(bytes))
10}
11
12/// Decode a message from MessagePack bytes
13pub fn decode_message(bytes: &[u8]) -> Result<Message> {
14    let message = rmp_serde::from_slice(bytes)?;
15    Ok(message)
16}
17
18/// Encode a message into a complete frame
19pub fn encode(message: &Message) -> Result<Bytes> {
20    let payload = encode_message(message)?;
21    let frame = Frame::new(payload).with_qos(message.default_qos());
22    frame.encode()
23}
24
25/// Encode a message with options
26pub fn encode_with_options(
27    message: &Message,
28    qos: Option<QoS>,
29    timestamp: Option<u64>,
30) -> Result<Bytes> {
31    let payload = encode_message(message)?;
32    let mut frame = Frame::new(payload);
33
34    if let Some(qos) = qos {
35        frame = frame.with_qos(qos);
36    } else {
37        frame = frame.with_qos(message.default_qos());
38    }
39
40    if let Some(ts) = timestamp {
41        frame = frame.with_timestamp(ts);
42    }
43
44    frame.encode()
45}
46
47/// Decode a frame and extract the message
48pub fn decode(bytes: &[u8]) -> Result<(Message, Frame)> {
49    let frame = Frame::decode(bytes)?;
50    let message = decode_message(&frame.payload)?;
51    Ok((message, frame))
52}
53
54/// Helper to encode just the message payload (without frame)
55pub fn encode_payload(message: &Message) -> Result<Vec<u8>> {
56    let bytes = rmp_serde::to_vec_named(message)?;
57    Ok(bytes)
58}
59
60/// Helper to decode just a message payload (without frame)
61pub fn decode_payload(bytes: &[u8]) -> Result<Message> {
62    decode_message(bytes)
63}
64
65#[cfg(test)]
66mod tests {
67    use super::*;
68    use crate::types::*;
69
70    #[test]
71    fn test_hello_roundtrip() {
72        let msg = Message::Hello(HelloMessage {
73            version: 2,
74            name: "Test Client".to_string(),
75            features: vec!["param".to_string(), "event".to_string()],
76            capabilities: None,
77            token: None,
78        });
79
80        let encoded = encode(&msg).unwrap();
81        let (decoded, frame) = decode(&encoded).unwrap();
82
83        match decoded {
84            Message::Hello(hello) => {
85                assert_eq!(hello.version, 2);
86                assert_eq!(hello.name, "Test Client");
87                assert_eq!(hello.features.len(), 2);
88            }
89            _ => panic!("Expected Hello message"),
90        }
91
92        assert_eq!(frame.flags.qos, QoS::Fire);
93    }
94
95    #[test]
96    fn test_set_roundtrip() {
97        let msg = Message::Set(SetMessage {
98            address: "/test/value".to_string(),
99            value: Value::Float(0.75),
100            revision: Some(42),
101            lock: false,
102            unlock: false,
103        });
104
105        let encoded = encode(&msg).unwrap();
106        let (decoded, frame) = decode(&encoded).unwrap();
107
108        match decoded {
109            Message::Set(set) => {
110                assert_eq!(set.address, "/test/value");
111                assert_eq!(set.value.as_f64(), Some(0.75));
112                assert_eq!(set.revision, Some(42));
113            }
114            _ => panic!("Expected Set message"),
115        }
116
117        assert_eq!(frame.flags.qos, QoS::Confirm);
118    }
119
120    #[test]
121    fn test_bundle_roundtrip() {
122        let msg = Message::Bundle(BundleMessage {
123            timestamp: Some(1000000),
124            messages: vec![
125                Message::Set(SetMessage {
126                    address: "/light/1".to_string(),
127                    value: Value::Float(1.0),
128                    revision: None,
129                    lock: false,
130                    unlock: false,
131                }),
132                Message::Set(SetMessage {
133                    address: "/light/2".to_string(),
134                    value: Value::Float(0.0),
135                    revision: None,
136                    lock: false,
137                    unlock: false,
138                }),
139            ],
140        });
141
142        let encoded = encode(&msg).unwrap();
143        let (decoded, _) = decode(&encoded).unwrap();
144
145        match decoded {
146            Message::Bundle(bundle) => {
147                assert_eq!(bundle.timestamp, Some(1000000));
148                assert_eq!(bundle.messages.len(), 2);
149            }
150            _ => panic!("Expected Bundle message"),
151        }
152    }
153
154    #[test]
155    fn test_value_types() {
156        // Test various value types
157        let values = vec![
158            Value::Null,
159            Value::Bool(true),
160            Value::Int(42),
161            Value::Float(3.14),
162            Value::String("hello".to_string()),
163            Value::Array(vec![Value::Int(1), Value::Int(2), Value::Int(3)]),
164        ];
165
166        for value in values {
167            let msg = Message::Set(SetMessage {
168                address: "/test".to_string(),
169                value: value.clone(),
170                revision: None,
171                lock: false,
172                unlock: false,
173            });
174
175            let encoded = encode(&msg).unwrap();
176            let (decoded, _) = decode(&encoded).unwrap();
177
178            match decoded {
179                Message::Set(set) => {
180                    assert_eq!(set.value, value);
181                }
182                _ => panic!("Expected Set message"),
183            }
184        }
185    }
186}