1use crate::seqstring::global_string;
37use crate::value::{MapKey as RuntimeMapKey, Value, VariantData};
38use serde::{Deserialize, Serialize};
39use std::collections::{BTreeMap, HashMap};
40use std::sync::Arc;
41
42#[derive(Debug)]
44pub enum SerializeError {
45 QuotationNotSerializable,
47 ClosureNotSerializable,
49 ChannelNotSerializable,
51 BincodeError(Box<bincode::Error>),
53 InvalidData(String),
55 NonFiniteFloat(f64),
57}
58
59impl std::fmt::Display for SerializeError {
60 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
61 match self {
62 SerializeError::QuotationNotSerializable => {
63 write!(f, "Quotations cannot be serialized - code is not data")
64 }
65 SerializeError::ClosureNotSerializable => {
66 write!(f, "Closures cannot be serialized - code is not data")
67 }
68 SerializeError::ChannelNotSerializable => {
69 write!(f, "Channels cannot be serialized - runtime state")
70 }
71 SerializeError::BincodeError(e) => write!(f, "Bincode error: {}", e),
72 SerializeError::InvalidData(msg) => write!(f, "Invalid data: {}", msg),
73 SerializeError::NonFiniteFloat(v) => {
74 write!(f, "Cannot serialize non-finite float: {}", v)
75 }
76 }
77 }
78}
79
80impl std::error::Error for SerializeError {
81 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
82 match self {
83 SerializeError::BincodeError(e) => Some(e.as_ref()),
84 _ => None,
85 }
86 }
87}
88
89impl From<bincode::Error> for SerializeError {
90 fn from(e: bincode::Error) -> Self {
91 SerializeError::BincodeError(Box::new(e))
92 }
93}
94
95#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Hash)]
100pub enum TypedMapKey {
101 Int(i64),
102 Bool(bool),
103 String(String),
104}
105
106impl TypedMapKey {
107 pub fn to_typed_value(&self) -> TypedValue {
109 match self {
110 TypedMapKey::Int(v) => TypedValue::Int(*v),
111 TypedMapKey::Bool(v) => TypedValue::Bool(*v),
112 TypedMapKey::String(v) => TypedValue::String(v.clone()),
113 }
114 }
115
116 pub fn from_runtime(key: &RuntimeMapKey) -> Self {
118 match key {
119 RuntimeMapKey::Int(v) => TypedMapKey::Int(*v),
120 RuntimeMapKey::Bool(v) => TypedMapKey::Bool(*v),
121 RuntimeMapKey::String(s) => TypedMapKey::String(s.as_str().to_string()),
122 }
123 }
124
125 pub fn to_runtime(&self) -> RuntimeMapKey {
127 match self {
128 TypedMapKey::Int(v) => RuntimeMapKey::Int(*v),
129 TypedMapKey::Bool(v) => RuntimeMapKey::Bool(*v),
130 TypedMapKey::String(s) => RuntimeMapKey::String(global_string(s.clone())),
131 }
132 }
133}
134
135#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
140pub enum TypedValue {
141 Int(i64),
142 Float(f64),
143 Bool(bool),
144 String(String),
145 Map(BTreeMap<TypedMapKey, TypedValue>),
147 Variant {
149 tag: u32,
150 fields: Vec<TypedValue>,
151 },
152}
153
154impl TypedValue {
155 pub fn from_value(value: &Value) -> Result<Self, SerializeError> {
161 match value {
162 Value::Int(v) => Ok(TypedValue::Int(*v)),
163 Value::Float(v) => {
164 if !v.is_finite() {
165 return Err(SerializeError::NonFiniteFloat(*v));
166 }
167 Ok(TypedValue::Float(*v))
168 }
169 Value::Bool(v) => Ok(TypedValue::Bool(*v)),
170 Value::String(s) => Ok(TypedValue::String(s.as_str().to_string())),
171 Value::Map(map) => {
172 let mut typed_map = BTreeMap::new();
173 for (k, v) in map.iter() {
174 let typed_key = TypedMapKey::from_runtime(k);
175 let typed_value = TypedValue::from_value(v)?;
176 typed_map.insert(typed_key, typed_value);
177 }
178 Ok(TypedValue::Map(typed_map))
179 }
180 Value::Variant(data) => {
181 let mut typed_fields = Vec::with_capacity(data.fields.len());
182 for field in data.fields.iter() {
183 typed_fields.push(TypedValue::from_value(field)?);
184 }
185 Ok(TypedValue::Variant {
186 tag: data.tag,
187 fields: typed_fields,
188 })
189 }
190 Value::Quotation { .. } => Err(SerializeError::QuotationNotSerializable),
191 Value::Closure { .. } => Err(SerializeError::ClosureNotSerializable),
192 Value::Channel(_) => Err(SerializeError::ChannelNotSerializable),
193 Value::WeaveCtx { .. } => Err(SerializeError::ChannelNotSerializable), }
195 }
196
197 pub fn to_value(&self) -> Value {
202 match self {
203 TypedValue::Int(v) => Value::Int(*v),
204 TypedValue::Float(v) => Value::Float(*v),
205 TypedValue::Bool(v) => Value::Bool(*v),
206 TypedValue::String(s) => Value::String(global_string(s.clone())),
207 TypedValue::Map(map) => {
208 let mut runtime_map = HashMap::new();
209 for (k, v) in map.iter() {
210 runtime_map.insert(k.to_runtime(), v.to_value());
211 }
212 Value::Map(Box::new(runtime_map))
213 }
214 TypedValue::Variant { tag, fields } => {
215 let runtime_fields: Vec<Value> = fields.iter().map(|f| f.to_value()).collect();
216 Value::Variant(Arc::new(VariantData::new(*tag, runtime_fields)))
217 }
218 }
219 }
220
221 pub fn to_map_key(&self) -> Result<TypedMapKey, SerializeError> {
223 match self {
224 TypedValue::Int(v) => Ok(TypedMapKey::Int(*v)),
225 TypedValue::Bool(v) => Ok(TypedMapKey::Bool(*v)),
226 TypedValue::String(v) => Ok(TypedMapKey::String(v.clone())),
227 TypedValue::Float(_) => Err(SerializeError::InvalidData(
228 "Float cannot be a map key".to_string(),
229 )),
230 TypedValue::Map(_) => Err(SerializeError::InvalidData(
231 "Map cannot be a map key".to_string(),
232 )),
233 TypedValue::Variant { .. } => Err(SerializeError::InvalidData(
234 "Variant cannot be a map key".to_string(),
235 )),
236 }
237 }
238
239 pub fn to_bytes(&self) -> Result<Vec<u8>, SerializeError> {
241 bincode::serialize(self).map_err(SerializeError::from)
242 }
243
244 pub fn from_bytes(bytes: &[u8]) -> Result<Self, SerializeError> {
246 bincode::deserialize(bytes).map_err(SerializeError::from)
247 }
248
249 pub fn to_debug_string(&self) -> String {
251 match self {
252 TypedValue::Int(v) => format!("{}", v),
253 TypedValue::Float(v) => format!("{}", v),
254 TypedValue::Bool(v) => format!("{}", v),
255 TypedValue::String(v) => format!("{:?}", v),
256 TypedValue::Map(m) => {
257 let entries: Vec<String> = m
258 .iter()
259 .map(|(k, v)| format!("{}: {}", key_to_debug_string(k), v.to_debug_string()))
260 .collect();
261 format!("{{ {} }}", entries.join(", "))
262 }
263 TypedValue::Variant { tag, fields } => {
264 if fields.is_empty() {
265 format!("(Variant#{})", tag)
266 } else {
267 let field_strs: Vec<String> =
268 fields.iter().map(|f| f.to_debug_string()).collect();
269 format!("(Variant#{} {})", tag, field_strs.join(" "))
270 }
271 }
272 }
273 }
274}
275
276fn key_to_debug_string(key: &TypedMapKey) -> String {
277 match key {
278 TypedMapKey::Int(v) => format!("{}", v),
279 TypedMapKey::Bool(v) => format!("{}", v),
280 TypedMapKey::String(v) => format!("{:?}", v),
281 }
282}
283
284pub trait ValueSerialize {
286 fn to_typed(&self) -> Result<TypedValue, SerializeError>;
288
289 fn to_bytes(&self) -> Result<Vec<u8>, SerializeError>;
291}
292
293impl ValueSerialize for Value {
294 fn to_typed(&self) -> Result<TypedValue, SerializeError> {
295 TypedValue::from_value(self)
296 }
297
298 fn to_bytes(&self) -> Result<Vec<u8>, SerializeError> {
299 TypedValue::from_value(self)?.to_bytes()
300 }
301}
302
303#[cfg(test)]
304mod tests {
305 use super::*;
306 use crate::seqstring::global_string;
307
308 #[test]
309 fn test_int_roundtrip() {
310 let value = Value::Int(42);
311 let typed = TypedValue::from_value(&value).unwrap();
312 let back = typed.to_value();
313 assert_eq!(value, back);
314 }
315
316 #[test]
317 fn test_float_roundtrip() {
318 let value = Value::Float(1.23456);
319 let typed = TypedValue::from_value(&value).unwrap();
320 let back = typed.to_value();
321 assert_eq!(value, back);
322 }
323
324 #[test]
325 fn test_bool_roundtrip() {
326 let value = Value::Bool(true);
327 let typed = TypedValue::from_value(&value).unwrap();
328 let back = typed.to_value();
329 assert_eq!(value, back);
330 }
331
332 #[test]
333 fn test_string_roundtrip() {
334 let value = Value::String(global_string("hello".to_string()));
335 let typed = TypedValue::from_value(&value).unwrap();
336 let back = typed.to_value();
337 match (&value, &back) {
339 (Value::String(a), Value::String(b)) => assert_eq!(a.as_str(), b.as_str()),
340 _ => panic!("Expected strings"),
341 }
342 }
343
344 #[test]
345 fn test_map_roundtrip() {
346 let mut map = HashMap::new();
347 map.insert(
348 RuntimeMapKey::String(global_string("key".to_string())),
349 Value::Int(42),
350 );
351 map.insert(RuntimeMapKey::Int(1), Value::Bool(true));
352
353 let value = Value::Map(Box::new(map));
354 let typed = TypedValue::from_value(&value).unwrap();
355 let back = typed.to_value();
356
357 if let Value::Map(m) = back {
359 assert_eq!(m.len(), 2);
360 } else {
361 panic!("Expected map");
362 }
363 }
364
365 #[test]
366 fn test_variant_roundtrip() {
367 let data = VariantData::new(1, vec![Value::Int(100), Value::Bool(false)]);
368 let value = Value::Variant(Arc::new(data));
369
370 let typed = TypedValue::from_value(&value).unwrap();
371 let back = typed.to_value();
372
373 if let Value::Variant(v) = back {
374 assert_eq!(v.tag, 1);
375 assert_eq!(v.fields.len(), 2);
376 } else {
377 panic!("Expected variant");
378 }
379 }
380
381 #[test]
382 fn test_quotation_not_serializable() {
383 let value = Value::Quotation {
384 wrapper: 12345,
385 impl_: 12345,
386 };
387 let result = TypedValue::from_value(&value);
388 assert!(matches!(
389 result,
390 Err(SerializeError::QuotationNotSerializable)
391 ));
392 }
393
394 #[test]
395 fn test_closure_not_serializable() {
396 use std::sync::Arc;
397 let value = Value::Closure {
398 fn_ptr: 12345,
399 env: Arc::from(vec![Value::Int(1)].into_boxed_slice()),
400 };
401 let result = TypedValue::from_value(&value);
402 assert!(matches!(
403 result,
404 Err(SerializeError::ClosureNotSerializable)
405 ));
406 }
407
408 #[test]
409 fn test_bytes_roundtrip() {
410 let typed = TypedValue::Map(BTreeMap::from([
411 (TypedMapKey::String("x".to_string()), TypedValue::Int(10)),
412 (TypedMapKey::Int(42), TypedValue::Bool(true)),
413 ]));
414
415 let bytes = typed.to_bytes().unwrap();
416 let parsed = TypedValue::from_bytes(&bytes).unwrap();
417 assert_eq!(typed, parsed);
418 }
419
420 #[test]
421 fn test_bincode_is_compact() {
422 let typed = TypedValue::Int(42);
423 let bytes = typed.to_bytes().unwrap();
424 assert!(
425 bytes.len() < 20,
426 "Expected compact encoding, got {} bytes",
427 bytes.len()
428 );
429 }
430
431 #[test]
432 fn test_debug_string() {
433 let typed = TypedValue::String("hello".to_string());
434 assert_eq!(typed.to_debug_string(), "\"hello\"");
435
436 let typed = TypedValue::Int(42);
437 assert_eq!(typed.to_debug_string(), "42");
438 }
439
440 #[test]
441 fn test_nested_structure() {
442 let inner_variant = TypedValue::Variant {
444 tag: 2,
445 fields: vec![TypedValue::String("inner".to_string())],
446 };
447
448 let mut inner_map = BTreeMap::new();
449 inner_map.insert(TypedMapKey::String("nested".to_string()), inner_variant);
450
451 let outer = TypedValue::Map(inner_map);
452
453 let bytes = outer.to_bytes().unwrap();
454 let parsed = TypedValue::from_bytes(&bytes).unwrap();
455 assert_eq!(outer, parsed);
456 }
457
458 #[test]
459 fn test_nan_not_serializable() {
460 let value = Value::Float(f64::NAN);
461 let result = TypedValue::from_value(&value);
462 assert!(matches!(result, Err(SerializeError::NonFiniteFloat(_))));
463 }
464
465 #[test]
466 fn test_infinity_not_serializable() {
467 let value = Value::Float(f64::INFINITY);
468 let result = TypedValue::from_value(&value);
469 assert!(matches!(result, Err(SerializeError::NonFiniteFloat(_))));
470
471 let value = Value::Float(f64::NEG_INFINITY);
472 let result = TypedValue::from_value(&value);
473 assert!(matches!(result, Err(SerializeError::NonFiniteFloat(_))));
474 }
475
476 #[test]
477 fn test_corrupted_data_returns_error() {
478 let corrupted = vec![0xFF, 0xFF, 0xFF, 0xFF, 0xFF];
480 let result = TypedValue::from_bytes(&corrupted);
481 assert!(result.is_err());
482 }
483
484 #[test]
485 fn test_empty_data_returns_error() {
486 let result = TypedValue::from_bytes(&[]);
487 assert!(result.is_err());
488 }
489
490 #[test]
491 fn test_truncated_data_returns_error() {
492 let typed = TypedValue::String("hello world".to_string());
494 let bytes = typed.to_bytes().unwrap();
495 let truncated = &bytes[..bytes.len() / 2];
496 let result = TypedValue::from_bytes(truncated);
497 assert!(result.is_err());
498 }
499}