1use osc_ir::IrValue;
60
61pub type EncodeResult<T> = Result<T, rmp_serde::encode::Error>;
62pub type DecodeResult<T> = Result<T, rmp_serde::decode::Error>;
63
64pub fn try_to_msgpack(v: &IrValue) -> EncodeResult<Vec<u8>> {
65 rmp_serde::to_vec_named(v)
66}
67
68pub fn to_msgpack(v: &IrValue) -> Vec<u8> {
69 try_to_msgpack(v).expect("serialize")
70}
71
72pub fn try_from_msgpack(bytes: &[u8]) -> DecodeResult<IrValue> {
73 rmp_serde::from_slice::<IrValue>(bytes)
74}
75
76pub fn from_msgpack(bytes: &[u8]) -> IrValue {
77 try_from_msgpack(bytes).expect("deserialize")
78}
79
80#[cfg(test)]
81mod tests {
82 use super::*;
83 use osc_ir::{IrTimestamp, IrBundle, IrTimetag};
84
85 #[test]
86 fn roundtrip_timestamp() {
87 let value = IrValue::from(IrTimestamp {
88 seconds: 123,
89 nanos: 456,
90 });
91 let bytes = try_to_msgpack(&value).expect("encode");
92 let decoded = try_from_msgpack(&bytes).expect("decode");
93 assert_eq!(value, decoded);
94 }
95
96 #[test]
97 fn roundtrip_complex_structure() {
98 let value = IrValue::Map(vec![
99 ("msg".into(), IrValue::from("hello")),
100 ("bin".into(), IrValue::from(vec![1_u8, 2, 3])),
101 (
102 "ext".into(),
103 IrValue::Ext {
104 type_id: -4,
105 data: vec![0x10, 0x20, 0x30],
106 },
107 ),
108 ]);
109
110 let bytes = to_msgpack(&value);
111 let decoded = from_msgpack(&bytes);
112 assert_eq!(decoded, value);
113 }
114
115 #[test]
116 fn roundtrip_bundle() {
117 let mut bundle = IrBundle::new(IrTimetag::from_ntp(12345));
118 bundle.add_message(IrValue::from("hello"));
119 bundle.add_message(IrValue::from(42));
120
121 let mut nested_bundle = IrBundle::immediate();
122 nested_bundle.add_message(IrValue::from(true));
123
124 bundle.add_bundle(nested_bundle);
125
126 let value = IrValue::Bundle(bundle);
127 let bytes = to_msgpack(&value);
128 let decoded = from_msgpack(&bytes);
129 assert_eq!(decoded, value);
130 }
131
132 #[test]
133 fn roundtrip_deeply_nested_bundle() {
134 let mut root = IrBundle::immediate();
136 root.add_message(IrValue::from("root"));
137
138 let mut level1 = IrBundle::new(IrTimetag::from_ntp(1000));
139 level1.add_message(IrValue::from("level1"));
140
141 let mut level2 = IrBundle::new(IrTimetag::from_ntp(2000));
142 level2.add_message(IrValue::from("level2"));
143
144 level1.add_bundle(level2);
145 root.add_bundle(level1);
146
147 let value = IrValue::Bundle(root);
148
149 let bytes = to_msgpack(&value);
151 let decoded = from_msgpack(&bytes);
152 assert_eq!(value, decoded);
153 }
154
155 #[test]
156 fn roundtrip_osc_message_like() {
157 let value = IrValue::Map(vec![
159 ("$type".into(), IrValue::from("osc.message")),
160 ("address".into(), IrValue::from("/test")),
161 (
162 "args".into(),
163 IrValue::Array(vec![
164 IrValue::Integer(7),
165 IrValue::Float(1.5),
166 IrValue::from("text"),
167 IrValue::Binary(vec![1_u8, 2, 3]),
168 ]),
169 ),
170 ]);
171
172 let bytes = to_msgpack(&value);
173 let decoded = from_msgpack(&bytes);
174
175 assert_eq!(decoded, value);
177
178 let map = decoded.as_map().expect("expected map");
180 let address = map.iter().find(|(k, _)| k == "address").unwrap().1.as_str();
181 assert_eq!(address, Some("/test"));
182
183 let args = map
184 .iter()
185 .find(|(k, _)| k == "args")
186 .unwrap()
187 .1
188 .as_array()
189 .expect("expected args array");
190
191 assert_eq!(args.len(), 4);
192 assert_eq!(args[0].as_integer(), Some(7));
193 assert!((args[1].as_float().unwrap() - 1.5).abs() < f64::EPSILON);
194 assert_eq!(args[2].as_str(), Some("text"));
195 assert_eq!(args[3].as_binary(), Some(&[1_u8, 2, 3][..]));
196 }
197
198 #[test]
199 fn msgpack_bytes_are_valid_and_match_contents() {
200 use std::io::Cursor;
201 use rmpv::{decode::read_value, Value};
202
203 let value = IrValue::Map(vec![
205 ("$type".into(), IrValue::from("osc.message")),
206 ("address".into(), IrValue::from("/validate")),
207 (
208 "args".into(),
209 IrValue::Array(vec![
210 IrValue::Integer(123),
211 IrValue::Float(-2.5),
212 IrValue::from("ok"),
213 IrValue::Binary(vec![0xAA, 0xBB]),
214 ]),
215 ),
216 ]);
217
218 let bytes = to_msgpack(&value);
220
221 let mut cursor = Cursor::new(&bytes);
223 let root = read_value(&mut cursor).expect("must decode as msgpack Value");
224
225 fn unwrap_enum(v: &Value) -> (&str, &Value) {
227 match v {
228 Value::Map(kv) if kv.len() == 1 => {
230 let (k, v) = &kv[0];
231 let name = match k { Value::String(s) => s.as_str().expect("variant name"), _ => panic!("invalid enum key") };
232 (name, v)
233 }
234 Value::Array(items) if items.len() == 2 => {
236 let name = match &items[0] { Value::String(s) => s.as_str().expect("variant name"), _ => panic!("invalid enum tag array") };
237 (name, &items[1])
238 }
239 other => panic!("unexpected enum encoding: {:?}", other),
240 }
241 }
242
243 let (root_variant, root_payload) = unwrap_enum(&root);
245 assert_eq!(root_variant, "Map");
246
247 let entries = match root_payload { Value::Array(a) => a, other => panic!("expected entries array, got {:?}", other) };
249
250 let get = |key: &str| -> &Value {
252 entries
253 .iter()
254 .find_map(|entry| match entry {
255 Value::Array(items) if items.len() == 2 => match (&items[0], &items[1]) {
256 (Value::String(s), v) if s.as_str() == Some(key) => Some(v),
257 _ => None,
258 },
259 _ => None,
260 })
261 .expect("entry not found")
262 };
263
264 let (ty_variant, ty_payload) = unwrap_enum(get("$type"));
266 assert_eq!(ty_variant, "String");
267 assert!(matches!(ty_payload, Value::String(s) if s.as_str() == Some("osc.message")));
268
269 let (addr_variant, addr_payload) = unwrap_enum(get("address"));
271 assert_eq!(addr_variant, "String");
272 assert!(matches!(addr_payload, Value::String(s) if s.as_str() == Some("/validate")));
273
274 let (args_variant, args_payload) = unwrap_enum(get("args"));
276 assert_eq!(args_variant, "Array");
277 let args = match args_payload { Value::Array(a) => a, other => panic!("expected args payload array, got {:?}", other) };
278 assert_eq!(args.len(), 4);
279
280 let (v0_variant, v0_payload) = unwrap_enum(&args[0]);
282 assert_eq!(v0_variant, "Integer");
283 assert!(matches!(v0_payload, Value::Integer(i) if i.as_i64() == Some(123)));
284
285 let (v1_variant, v1_payload) = unwrap_enum(&args[1]);
287 assert_eq!(v1_variant, "Float");
288 assert!(matches!(v1_payload, Value::F64(x) if (*x + 2.5).abs() < f64::EPSILON));
289
290 let (v2_variant, v2_payload) = unwrap_enum(&args[2]);
292 assert_eq!(v2_variant, "String");
293 assert!(matches!(v2_payload, Value::String(s) if s.as_str() == Some("ok")));
294
295 let (v3_variant, v3_payload) = unwrap_enum(&args[3]);
297 assert_eq!(v3_variant, "Binary");
298 assert!(matches!(v3_payload, Value::Binary(b) if b.as_slice() == [0xAA, 0xBB]));
299 }
300}