1use crate::module_exports::{ModuleContext, ModuleExports, ModuleFunction, ModuleParam};
7use shape_value::ValueWord;
8use std::sync::Arc;
9
10fn json_value_to_valueword(value: serde_json::Value) -> ValueWord {
14 match value {
15 serde_json::Value::Null => ValueWord::none(),
16 serde_json::Value::Bool(b) => ValueWord::from_bool(b),
17 serde_json::Value::Number(n) => {
18 if let Some(i) = n.as_i64() {
19 ValueWord::from_i64(i)
20 } else {
21 ValueWord::from_f64(n.as_f64().unwrap_or(0.0))
22 }
23 }
24 serde_json::Value::String(s) => ValueWord::from_string(Arc::new(s)),
25 serde_json::Value::Array(arr) => {
26 let items: Vec<ValueWord> = arr.into_iter().map(json_value_to_valueword).collect();
27 ValueWord::from_array(Arc::new(items))
28 }
29 serde_json::Value::Object(map) => {
30 let mut keys = Vec::with_capacity(map.len());
31 let mut values = Vec::with_capacity(map.len());
32 for (k, v) in map.into_iter() {
33 keys.push(ValueWord::from_string(Arc::new(k)));
34 values.push(json_value_to_valueword(v));
35 }
36 ValueWord::from_hashmap_pairs(keys, values)
37 }
38 }
39}
40
41pub fn create_msgpack_module() -> ModuleExports {
43 let mut module = ModuleExports::new("msgpack");
44 module.description = "MessagePack binary serialization".to_string();
45
46 module.add_function_with_schema(
49 "encode",
50 |args: &[ValueWord], _ctx: &ModuleContext| {
51 let value = args
52 .first()
53 .ok_or_else(|| "msgpack.encode() requires a value argument".to_string())?;
54
55 let json_value = value.to_json_value();
56 let bytes = rmp_serde::to_vec(&json_value)
57 .map_err(|e| format!("msgpack.encode() failed: {}", e))?;
58 let hex_str = hex::encode(&bytes);
59
60 Ok(ValueWord::from_ok(ValueWord::from_string(Arc::new(
61 hex_str,
62 ))))
63 },
64 ModuleFunction {
65 description: "Encode a value to MessagePack (hex-encoded string)".to_string(),
66 params: vec![ModuleParam {
67 name: "value".to_string(),
68 type_name: "any".to_string(),
69 required: true,
70 description: "Value to encode".to_string(),
71 ..Default::default()
72 }],
73 return_type: Some("Result<string>".to_string()),
74 },
75 );
76
77 module.add_function_with_schema(
80 "decode",
81 |args: &[ValueWord], _ctx: &ModuleContext| {
82 let hex_str = args
83 .first()
84 .and_then(|a| a.as_str())
85 .ok_or_else(|| "msgpack.decode() requires a string argument".to_string())?;
86
87 let bytes =
88 hex::decode(hex_str).map_err(|e| format!("msgpack.decode() invalid hex: {}", e))?;
89 let json_value: serde_json::Value = rmp_serde::from_slice(&bytes)
90 .map_err(|e| format!("msgpack.decode() failed: {}", e))?;
91
92 Ok(ValueWord::from_ok(json_value_to_valueword(json_value)))
93 },
94 ModuleFunction {
95 description: "Decode a hex-encoded MessagePack string to a value".to_string(),
96 params: vec![ModuleParam {
97 name: "data".to_string(),
98 type_name: "string".to_string(),
99 required: true,
100 description: "Hex-encoded MessagePack data".to_string(),
101 ..Default::default()
102 }],
103 return_type: Some("Result<any>".to_string()),
104 },
105 );
106
107 module.add_function_with_schema(
110 "encode_bytes",
111 |args: &[ValueWord], _ctx: &ModuleContext| {
112 let value = args
113 .first()
114 .ok_or_else(|| "msgpack.encode_bytes() requires a value argument".to_string())?;
115
116 let json_value = value.to_json_value();
117 let bytes = rmp_serde::to_vec(&json_value)
118 .map_err(|e| format!("msgpack.encode_bytes() failed: {}", e))?;
119
120 let items: Vec<ValueWord> = bytes
121 .iter()
122 .map(|&b| ValueWord::from_i64(b as i64))
123 .collect();
124
125 Ok(ValueWord::from_ok(ValueWord::from_array(Arc::new(items))))
126 },
127 ModuleFunction {
128 description: "Encode a value to MessagePack as a byte array".to_string(),
129 params: vec![ModuleParam {
130 name: "value".to_string(),
131 type_name: "any".to_string(),
132 required: true,
133 description: "Value to encode".to_string(),
134 ..Default::default()
135 }],
136 return_type: Some("Result<Array<int>>".to_string()),
137 },
138 );
139
140 module.add_function_with_schema(
143 "decode_bytes",
144 |args: &[ValueWord], _ctx: &ModuleContext| {
145 let arr = args.first().and_then(|a| a.as_any_array()).ok_or_else(|| {
146 "msgpack.decode_bytes() requires an Array<int> argument".to_string()
147 })?;
148
149 let generic = arr.to_generic();
150 let bytes: Result<Vec<u8>, String> = generic
151 .iter()
152 .enumerate()
153 .map(|(i, v)| {
154 v.as_i64()
155 .or_else(|| v.as_f64().map(|f| f as i64))
156 .and_then(|n| u8::try_from(n).ok())
157 .ok_or_else(|| {
158 format!(
159 "msgpack.decode_bytes() element at index {} is not a valid byte",
160 i
161 )
162 })
163 })
164 .collect();
165 let bytes = bytes?;
166
167 let json_value: serde_json::Value = rmp_serde::from_slice(&bytes)
168 .map_err(|e| format!("msgpack.decode_bytes() failed: {}", e))?;
169
170 Ok(ValueWord::from_ok(json_value_to_valueword(json_value)))
171 },
172 ModuleFunction {
173 description: "Decode MessagePack from a byte array to a value".to_string(),
174 params: vec![ModuleParam {
175 name: "data".to_string(),
176 type_name: "Array<int>".to_string(),
177 required: true,
178 description: "Array of byte values (0-255)".to_string(),
179 ..Default::default()
180 }],
181 return_type: Some("Result<any>".to_string()),
182 },
183 );
184
185 module
186}
187
188#[cfg(test)]
189mod tests {
190 use super::*;
191
192 fn test_ctx() -> crate::module_exports::ModuleContext<'static> {
193 let registry = Box::leak(Box::new(crate::type_schema::TypeSchemaRegistry::new()));
194 crate::module_exports::ModuleContext {
195 schemas: registry,
196 invoke_callable: None,
197 raw_invoker: None,
198 function_hashes: None,
199 vm_state: None,
200 granted_permissions: None,
201 scope_constraints: None,
202 set_pending_resume: None,
203 set_pending_frame_resume: None,
204 }
205 }
206
207 #[test]
208 fn test_msgpack_module_creation() {
209 let module = create_msgpack_module();
210 assert_eq!(module.name, "msgpack");
211 assert!(module.has_export("encode"));
212 assert!(module.has_export("decode"));
213 assert!(module.has_export("encode_bytes"));
214 assert!(module.has_export("decode_bytes"));
215 }
216
217 #[test]
218 fn test_encode_decode_roundtrip_string() {
219 let module = create_msgpack_module();
220 let encode_fn = module.get_export("encode").unwrap();
221 let decode_fn = module.get_export("decode").unwrap();
222 let ctx = test_ctx();
223
224 let input = ValueWord::from_string(Arc::new("hello".to_string()));
225 let encoded = encode_fn(&[input], &ctx).unwrap();
226 let hex_str = encoded.as_ok_inner().expect("should be Ok");
227
228 let decoded = decode_fn(&[hex_str.clone()], &ctx).unwrap();
229 let inner = decoded.as_ok_inner().expect("should be Ok");
230 assert_eq!(inner.as_str(), Some("hello"));
231 }
232
233 #[test]
234 fn test_encode_decode_roundtrip_number() {
235 let module = create_msgpack_module();
236 let encode_fn = module.get_export("encode").unwrap();
237 let decode_fn = module.get_export("decode").unwrap();
238 let ctx = test_ctx();
239
240 let input = ValueWord::from_f64(42.5);
241 let encoded = encode_fn(&[input], &ctx).unwrap();
242 let hex_str = encoded.as_ok_inner().expect("should be Ok");
243
244 let decoded = decode_fn(&[hex_str.clone()], &ctx).unwrap();
245 let inner = decoded.as_ok_inner().expect("should be Ok");
246 assert_eq!(inner.as_f64(), Some(42.5));
247 }
248
249 #[test]
250 fn test_encode_decode_roundtrip_bool() {
251 let module = create_msgpack_module();
252 let encode_fn = module.get_export("encode").unwrap();
253 let decode_fn = module.get_export("decode").unwrap();
254 let ctx = test_ctx();
255
256 let input = ValueWord::from_bool(true);
257 let encoded = encode_fn(&[input], &ctx).unwrap();
258 let hex_str = encoded.as_ok_inner().expect("should be Ok");
259
260 let decoded = decode_fn(&[hex_str.clone()], &ctx).unwrap();
261 let inner = decoded.as_ok_inner().expect("should be Ok");
262 assert_eq!(inner.as_bool(), Some(true));
263 }
264
265 #[test]
266 fn test_encode_decode_roundtrip_null() {
267 let module = create_msgpack_module();
268 let encode_fn = module.get_export("encode").unwrap();
269 let decode_fn = module.get_export("decode").unwrap();
270 let ctx = test_ctx();
271
272 let input = ValueWord::none();
273 let encoded = encode_fn(&[input], &ctx).unwrap();
274 let hex_str = encoded.as_ok_inner().expect("should be Ok");
275
276 let decoded = decode_fn(&[hex_str.clone()], &ctx).unwrap();
277 let inner = decoded.as_ok_inner().expect("should be Ok");
278 assert!(inner.is_none());
279 }
280
281 #[test]
282 fn test_encode_decode_roundtrip_array() {
283 let module = create_msgpack_module();
284 let encode_fn = module.get_export("encode").unwrap();
285 let decode_fn = module.get_export("decode").unwrap();
286 let ctx = test_ctx();
287
288 let input = ValueWord::from_array(Arc::new(vec![
289 ValueWord::from_i64(1),
290 ValueWord::from_i64(2),
291 ValueWord::from_i64(3),
292 ]));
293 let encoded = encode_fn(&[input], &ctx).unwrap();
294 let hex_str = encoded.as_ok_inner().expect("should be Ok");
295
296 let decoded = decode_fn(&[hex_str.clone()], &ctx).unwrap();
297 let inner = decoded.as_ok_inner().expect("should be Ok");
298 let arr = inner.as_any_array().expect("should be array").to_generic();
299 assert_eq!(arr.len(), 3);
300 assert_eq!(arr[0].as_i64(), Some(1));
301 assert_eq!(arr[1].as_i64(), Some(2));
302 assert_eq!(arr[2].as_i64(), Some(3));
303 }
304
305 #[test]
306 fn test_encode_decode_roundtrip_object() {
307 let module = create_msgpack_module();
308 let encode_fn = module.get_export("encode").unwrap();
309 let decode_fn = module.get_export("decode").unwrap();
310 let ctx = test_ctx();
311
312 let input = ValueWord::from_hashmap_pairs(
313 vec![
314 ValueWord::from_string(Arc::new("name".to_string())),
315 ValueWord::from_string(Arc::new("age".to_string())),
316 ],
317 vec![
318 ValueWord::from_string(Arc::new("Alice".to_string())),
319 ValueWord::from_i64(30),
320 ],
321 );
322 let encoded = encode_fn(&[input], &ctx).unwrap();
323 let hex_str = encoded.as_ok_inner().expect("should be Ok");
324
325 let decoded = decode_fn(&[hex_str.clone()], &ctx).unwrap();
326 let inner = decoded.as_ok_inner().expect("should be Ok");
327 let (keys, _values, _index) = inner.as_hashmap().expect("should be hashmap");
328 assert_eq!(keys.len(), 2);
329 }
330
331 #[test]
332 fn test_encode_bytes_decode_bytes_roundtrip() {
333 let module = create_msgpack_module();
334 let encode_bytes_fn = module.get_export("encode_bytes").unwrap();
335 let decode_bytes_fn = module.get_export("decode_bytes").unwrap();
336 let ctx = test_ctx();
337
338 let input = ValueWord::from_string(Arc::new("test".to_string()));
339 let encoded = encode_bytes_fn(&[input], &ctx).unwrap();
340 let byte_arr = encoded.as_ok_inner().expect("should be Ok");
341
342 let arr = byte_arr
344 .as_any_array()
345 .expect("should be array")
346 .to_generic();
347 assert!(!arr.is_empty());
348 for v in arr.iter() {
349 let byte_val = v.as_i64().expect("should be int");
350 assert!((0..=255).contains(&byte_val));
351 }
352
353 let decoded = decode_bytes_fn(&[byte_arr.clone()], &ctx).unwrap();
354 let inner = decoded.as_ok_inner().expect("should be Ok");
355 assert_eq!(inner.as_str(), Some("test"));
356 }
357
358 #[test]
359 fn test_decode_invalid_hex() {
360 let module = create_msgpack_module();
361 let decode_fn = module.get_export("decode").unwrap();
362 let ctx = test_ctx();
363
364 let input = ValueWord::from_string(Arc::new("not_valid_hex!@#".to_string()));
365 let result = decode_fn(&[input], &ctx);
366 assert!(result.is_err());
367 }
368
369 #[test]
370 fn test_decode_invalid_msgpack() {
371 let module = create_msgpack_module();
372 let decode_fn = module.get_export("decode").unwrap();
373 let ctx = test_ctx();
374
375 let input = ValueWord::from_string(Arc::new("deadbeef".to_string()));
377 let result = decode_fn(&[input], &ctx);
378 assert!(result.is_err());
379 }
380
381 #[test]
382 fn test_decode_requires_string() {
383 let module = create_msgpack_module();
384 let decode_fn = module.get_export("decode").unwrap();
385 let ctx = test_ctx();
386
387 let result = decode_fn(&[ValueWord::from_f64(42.0)], &ctx);
388 assert!(result.is_err());
389 }
390
391 #[test]
392 fn test_decode_bytes_requires_array() {
393 let module = create_msgpack_module();
394 let decode_bytes_fn = module.get_export("decode_bytes").unwrap();
395 let ctx = test_ctx();
396
397 let result = decode_bytes_fn(&[ValueWord::from_f64(42.0)], &ctx);
398 assert!(result.is_err());
399 }
400
401 #[test]
402 fn test_decode_bytes_invalid_byte_values() {
403 let module = create_msgpack_module();
404 let decode_bytes_fn = module.get_export("decode_bytes").unwrap();
405 let ctx = test_ctx();
406
407 let input = ValueWord::from_array(Arc::new(vec![ValueWord::from_i64(300)]));
409 let result = decode_bytes_fn(&[input], &ctx);
410 assert!(result.is_err());
411 }
412
413 #[test]
414 fn test_hex_and_bytes_encode_same_data() {
415 let module = create_msgpack_module();
416 let encode_fn = module.get_export("encode").unwrap();
417 let encode_bytes_fn = module.get_export("encode_bytes").unwrap();
418 let ctx = test_ctx();
419
420 let input = ValueWord::from_i64(42);
421 let hex_result = encode_fn(&[input.clone()], &ctx).unwrap();
422 let hex_str = hex_result.as_ok_inner().expect("should be Ok");
423 let hex_bytes = hex::decode(hex_str.as_str().unwrap()).unwrap();
424
425 let bytes_result = encode_bytes_fn(&[input], &ctx).unwrap();
426 let byte_arr = bytes_result.as_ok_inner().expect("should be Ok");
427 let arr = byte_arr
428 .as_any_array()
429 .expect("should be array")
430 .to_generic();
431 let arr_bytes: Vec<u8> = arr.iter().map(|v| v.as_i64().unwrap() as u8).collect();
432
433 assert_eq!(hex_bytes, arr_bytes);
434 }
435
436 #[test]
437 fn test_schemas() {
438 let module = create_msgpack_module();
439
440 let encode_schema = module.get_schema("encode").unwrap();
441 assert_eq!(encode_schema.params.len(), 1);
442 assert_eq!(encode_schema.params[0].name, "value");
443 assert!(encode_schema.params[0].required);
444 assert_eq!(encode_schema.return_type.as_deref(), Some("Result<string>"));
445
446 let decode_schema = module.get_schema("decode").unwrap();
447 assert_eq!(decode_schema.params.len(), 1);
448 assert_eq!(decode_schema.params[0].name, "data");
449 assert!(decode_schema.params[0].required);
450 assert_eq!(decode_schema.return_type.as_deref(), Some("Result<any>"));
451
452 let encode_bytes_schema = module.get_schema("encode_bytes").unwrap();
453 assert_eq!(encode_bytes_schema.params.len(), 1);
454 assert_eq!(
455 encode_bytes_schema.return_type.as_deref(),
456 Some("Result<Array<int>>")
457 );
458
459 let decode_bytes_schema = module.get_schema("decode_bytes").unwrap();
460 assert_eq!(decode_bytes_schema.params.len(), 1);
461 assert_eq!(decode_bytes_schema.params[0].type_name, "Array<int>");
462 assert_eq!(
463 decode_bytes_schema.return_type.as_deref(),
464 Some("Result<any>")
465 );
466 }
467
468 #[test]
469 fn test_encode_decode_nested_structure() {
470 let module = create_msgpack_module();
471 let encode_fn = module.get_export("encode").unwrap();
472 let decode_fn = module.get_export("decode").unwrap();
473 let ctx = test_ctx();
474
475 let user1 = ValueWord::from_hashmap_pairs(
477 vec![ValueWord::from_string(Arc::new("name".to_string()))],
478 vec![ValueWord::from_string(Arc::new("Alice".to_string()))],
479 );
480 let user2 = ValueWord::from_hashmap_pairs(
481 vec![ValueWord::from_string(Arc::new("name".to_string()))],
482 vec![ValueWord::from_string(Arc::new("Bob".to_string()))],
483 );
484 let input = ValueWord::from_hashmap_pairs(
485 vec![ValueWord::from_string(Arc::new("users".to_string()))],
486 vec![ValueWord::from_array(Arc::new(vec![user1, user2]))],
487 );
488
489 let encoded = encode_fn(&[input], &ctx).unwrap();
490 let hex_str = encoded.as_ok_inner().expect("should be Ok");
491
492 let decoded = decode_fn(&[hex_str.clone()], &ctx).unwrap();
493 let inner = decoded.as_ok_inner().expect("should be Ok");
494 let (keys, _values, _index) = inner.as_hashmap().expect("should be hashmap");
495 assert_eq!(keys.len(), 1);
496 assert_eq!(keys[0].as_str(), Some("users"));
497 }
498}