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