Skip to main content

shape_wire/
codec.rs

1//! Encoding and decoding functions
2//!
3//! This module provides the core serialization functions for the wire format.
4//! Primary format is MessagePack (via rmp-serde) for performance, with JSON
5//! wrappers for debugging and external tool interoperability.
6
7use crate::envelope::ValueEnvelope;
8use crate::error::{Result, WireError};
9
10/// Encode an envelope to binary format (MessagePack)
11///
12/// This is the primary serialization format for performance-critical paths
13/// like REPL communication and fchart interop.
14///
15/// Uses named encoding (MessagePack map format) for better compatibility
16/// and forward/backward compatibility with schema changes.
17///
18/// # Example
19/// ```
20/// use shape_wire::{encode, ValueEnvelope};
21///
22/// let env = ValueEnvelope::number(42.0);
23/// let bytes = encode(&env);
24/// assert!(!bytes.is_empty());
25/// ```
26pub fn encode(envelope: &ValueEnvelope) -> Vec<u8> {
27    rmp_serde::to_vec_named(envelope).expect("Failed to serialize envelope - this is a bug")
28}
29
30/// Decode an envelope from binary format (MessagePack)
31///
32/// # Example
33/// ```
34/// use shape_wire::{encode, decode, ValueEnvelope};
35///
36/// let env = ValueEnvelope::number(42.0);
37/// let bytes = encode(&env);
38/// let decoded = decode(&bytes).unwrap();
39/// assert_eq!(env.value, decoded.value);
40/// ```
41pub fn decode(bytes: &[u8]) -> Result<ValueEnvelope> {
42    rmp_serde::from_slice(bytes).map_err(|e| WireError::DeserializationError(e.to_string()))
43}
44
45/// Convert an envelope to JSON (for debugging/external tools)
46///
47/// JSON is less efficient than MessagePack but more portable and human-readable.
48///
49/// # Example
50/// ```
51/// use shape_wire::{to_json, ValueEnvelope};
52///
53/// let env = ValueEnvelope::string("hello");
54/// let json = to_json(&env);
55/// assert!(json["value"]["String"].is_string());
56/// ```
57pub fn to_json(envelope: &ValueEnvelope) -> serde_json::Value {
58    serde_json::to_value(envelope).expect("Failed to convert to JSON - this is a bug")
59}
60
61/// Parse an envelope from JSON
62///
63/// # Example
64/// ```
65/// use shape_wire::{to_json, from_json, ValueEnvelope};
66///
67/// let env = ValueEnvelope::bool(true);
68/// let json = to_json(&env);
69/// let decoded = from_json(&json).unwrap();
70/// assert_eq!(env.value, decoded.value);
71/// ```
72pub fn from_json(json: &serde_json::Value) -> Result<ValueEnvelope> {
73    serde_json::from_value(json.clone()).map_err(|e| WireError::DeserializationError(e.to_string()))
74}
75
76/// Encode to JSON string
77pub fn to_json_string(envelope: &ValueEnvelope) -> String {
78    serde_json::to_string(envelope).expect("Failed to serialize to JSON string - this is a bug")
79}
80
81/// Encode to pretty JSON string (for debugging)
82pub fn to_json_string_pretty(envelope: &ValueEnvelope) -> String {
83    serde_json::to_string_pretty(envelope)
84        .expect("Failed to serialize to JSON string - this is a bug")
85}
86
87/// Decode from JSON string
88pub fn from_json_string(s: &str) -> Result<ValueEnvelope> {
89    serde_json::from_str(s).map_err(|e| WireError::DeserializationError(e.to_string()))
90}
91
92/// Get the size of an encoded envelope in bytes
93///
94/// Useful for debugging and monitoring serialization overhead.
95pub fn encoded_size(envelope: &ValueEnvelope) -> usize {
96    encode(envelope).len()
97}
98
99// =========================================================================
100// Generic encode/decode — for any Serialize/Deserialize type
101// =========================================================================
102
103/// Encode any serializable value to MessagePack bytes.
104///
105/// This is the generic version of [`encode`] — it works with any type that
106/// implements `serde::Serialize`, not just `ValueEnvelope`. Use this for
107/// remote execution messages (`RemoteCallRequest`, `RemoteCallResponse`, etc.).
108pub fn encode_message<T: serde::Serialize>(message: &T) -> Result<Vec<u8>> {
109    rmp_serde::to_vec_named(message)
110        .map_err(|e| WireError::DeserializationError(format!("encode failed: {}", e)))
111}
112
113/// Decode any deserializable value from MessagePack bytes.
114///
115/// This is the generic version of [`decode`] — it works with any type that
116/// implements `serde::Deserialize`, not just `ValueEnvelope`.
117pub fn decode_message<'a, T: serde::Deserialize<'a>>(bytes: &'a [u8]) -> Result<T> {
118    rmp_serde::from_slice(bytes).map_err(|e| WireError::DeserializationError(e.to_string()))
119}
120
121#[cfg(test)]
122mod tests {
123    use super::*;
124    use crate::value::WireValue;
125
126    #[test]
127    fn test_msgpack_roundtrip() {
128        let env = ValueEnvelope::from_value(WireValue::Number(42.0));
129        let bytes = encode(&env);
130        let decoded = decode(&bytes).unwrap();
131        assert_eq!(env, decoded);
132    }
133
134    #[test]
135    fn test_json_roundtrip() {
136        let env = ValueEnvelope::from_value(WireValue::String("hello world".to_string()));
137        let json = to_json(&env);
138        let decoded = from_json(&json).unwrap();
139        assert_eq!(env, decoded);
140    }
141
142    #[test]
143    fn test_complex_value_roundtrip() {
144        use crate::value::WireTable;
145
146        let table = WireTable {
147            ipc_bytes: vec![1, 2, 3, 4],
148            type_name: Some("TestTable".to_string()),
149            schema_id: Some(1),
150            row_count: 3,
151            column_count: 1,
152        };
153
154        let env = ValueEnvelope::from_value(WireValue::Table(table));
155
156        // MessagePack roundtrip
157        let bytes = encode(&env);
158        let decoded = decode(&bytes).unwrap();
159        assert_eq!(env, decoded);
160
161        // JSON roundtrip
162        let json = to_json(&env);
163        let decoded = from_json(&json).unwrap();
164        assert_eq!(env, decoded);
165    }
166
167    #[test]
168    fn test_json_string_roundtrip() {
169        let env = ValueEnvelope::timestamp(1704067200000);
170        let json_str = to_json_string(&env);
171        let decoded = from_json_string(&json_str).unwrap();
172        assert_eq!(env, decoded);
173    }
174
175    #[test]
176    fn test_encoded_size() {
177        let small = ValueEnvelope::number(1.0);
178        let large = ValueEnvelope::from_value(WireValue::Array(
179            (0..1000).map(|i| WireValue::Number(i as f64)).collect(),
180        ));
181
182        let small_size = encoded_size(&small);
183        let large_size = encoded_size(&large);
184
185        // Small values should be smaller than large arrays
186        assert!(small_size < large_size);
187        // Named encoding includes field names, so it's larger but still reasonable
188        // The envelope includes type metadata so expect ~1KB for a simple value
189        assert!(small_size < 2000);
190    }
191
192    #[test]
193    fn test_invalid_msgpack() {
194        let result = decode(&[0xFF, 0xFF, 0xFF]);
195        assert!(result.is_err());
196    }
197
198    #[test]
199    fn test_invalid_json() {
200        let json = serde_json::json!({"invalid": "structure"});
201        let result = from_json(&json);
202        assert!(result.is_err());
203    }
204}