1use std::sync::Arc;
50
51use arc_swap::ArcSwap;
52use num_bigint::BigInt;
53
54use crate::collections::{PersistentHashSet, PersistentVector, SortedMap, SortedSet};
55use crate::error::ValueError;
56use crate::shared::{SharedAtom, SharedValue};
57use crate::types::DelayState;
58use crate::{Keyword, MapValue, PersistentList, PersistentQueue, SetValue, Symbol, Value};
59use cljrs_gc::GcPtr;
60
61#[derive(Debug, Clone, PartialEq, Eq)]
65pub enum CloneError {
66 NotShareable {
68 type_name: &'static str,
70 },
71 Disconnected,
73}
74
75impl std::fmt::Display for CloneError {
76 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
77 match self {
78 CloneError::NotShareable { type_name } => {
79 write!(
80 f,
81 "value of type `{type_name}` cannot cross an isolate boundary"
82 )
83 }
84 CloneError::Disconnected => write!(f, "isolate channel is disconnected"),
85 }
86 }
87}
88
89impl std::error::Error for CloneError {}
90
91fn not_shareable(type_name: &'static str) -> CloneError {
92 CloneError::NotShareable { type_name }
93}
94
95#[derive(Clone, Debug)]
101pub enum SerializedValue {
102 Nil,
104 Bool(bool),
105 Long(i64),
106 Double(f64),
107 BigInt(BigInt),
108 BigDecimal(bigdecimal::BigDecimal),
109 Ratio(num_rational::Ratio<BigInt>),
110 Char(char),
111 Str(String),
112 Uuid(u128),
113 Pattern(String),
115
116 Symbol {
118 namespace: Option<Arc<str>>,
119 name: Arc<str>,
120 version: Option<Arc<str>>,
121 },
122 Keyword {
123 namespace: Option<Arc<str>>,
124 name: Arc<str>,
125 },
126
127 List(Vec<SerializedValue>),
129 Vector(Vec<SerializedValue>),
130 ArrayMap(Vec<(SerializedValue, SerializedValue)>),
131 HashMap(Vec<(SerializedValue, SerializedValue)>),
132 SortedMap(Vec<(SerializedValue, SerializedValue)>),
133 HashSet(Vec<SerializedValue>),
134 SortedSet(Vec<SerializedValue>),
135 Queue(Vec<SerializedValue>),
136 Cons {
137 head: Box<SerializedValue>,
138 tail: Box<SerializedValue>,
139 },
140
141 TypeInstance {
143 type_tag: Arc<str>,
144 fields: Vec<(SerializedValue, SerializedValue)>,
145 },
146
147 Error(Box<SerializedError>),
149
150 BooleanArray(Vec<bool>),
152 ByteArray(Vec<i8>),
153 ShortArray(Vec<i16>),
154 IntArray(Vec<i32>),
155 LongArray(Vec<i64>),
156 FloatArray(Vec<f32>),
157 DoubleArray(Vec<f64>),
158 CharArray(Vec<char>),
159 ObjectArray(Vec<SerializedValue>),
160
161 WithMeta {
163 value: Box<SerializedValue>,
164 meta: Box<SerializedValue>,
165 },
166 Reduced(Box<SerializedValue>),
167
168 SharedAtom(Arc<SharedAtom>),
172 ByteBlob(Arc<[u8]>),
174 Var {
180 namespace: Arc<str>,
181 name: Arc<str>,
182 is_macro: bool,
183 shared_root: Arc<ArcSwap<Option<SharedValue>>>,
184 },
185}
186
187const _: () = {
189 const fn _assert_send<T: Send + Sync>() {}
190 let _ = _assert_send::<SerializedValue>;
191};
192
193impl SerializedValue {
194 pub fn byte_size(&self) -> usize {
202 const NODE: usize = std::mem::size_of::<SerializedValue>();
205
206 let payload = match self {
207 SerializedValue::Nil
208 | SerializedValue::Bool(_)
209 | SerializedValue::Long(_)
210 | SerializedValue::Double(_)
211 | SerializedValue::Char(_)
212 | SerializedValue::Uuid(_) => 0,
213
214 SerializedValue::BigInt(b) => (b.bits() as usize / 8) + 1,
215 SerializedValue::Ratio(r) => {
216 (r.numer().bits() as usize + r.denom().bits() as usize) / 8 + 2
217 }
218 SerializedValue::BigDecimal(_) => 16,
219
220 SerializedValue::Str(s) => s.len(),
221 SerializedValue::Pattern(s) => s.len(),
222
223 SerializedValue::Symbol {
224 namespace, name, ..
225 } => namespace.as_ref().map_or(0, |n| n.len()) + name.len(),
226 SerializedValue::Keyword { namespace, name } => {
227 namespace.as_ref().map_or(0, |n| n.len()) + name.len()
228 }
229
230 SerializedValue::List(items)
231 | SerializedValue::Vector(items)
232 | SerializedValue::HashSet(items)
233 | SerializedValue::SortedSet(items)
234 | SerializedValue::Queue(items)
235 | SerializedValue::ObjectArray(items) => {
236 items.iter().map(SerializedValue::byte_size).sum()
237 }
238
239 SerializedValue::ArrayMap(pairs)
240 | SerializedValue::HashMap(pairs)
241 | SerializedValue::SortedMap(pairs) => pairs
242 .iter()
243 .map(|(k, v)| k.byte_size() + v.byte_size())
244 .sum(),
245
246 SerializedValue::TypeInstance { fields, .. } => fields
247 .iter()
248 .map(|(k, v)| k.byte_size() + v.byte_size())
249 .sum(),
250
251 SerializedValue::Cons { head, tail } => head.byte_size() + tail.byte_size(),
252
253 SerializedValue::Error(e) => e.byte_size(),
254
255 SerializedValue::BooleanArray(v) => v.len(),
256 SerializedValue::ByteArray(v) => v.len(),
257 SerializedValue::ShortArray(v) => std::mem::size_of_val(v.as_slice()),
258 SerializedValue::IntArray(v) => std::mem::size_of_val(v.as_slice()),
259 SerializedValue::LongArray(v) => std::mem::size_of_val(v.as_slice()),
260 SerializedValue::FloatArray(v) => std::mem::size_of_val(v.as_slice()),
261 SerializedValue::DoubleArray(v) => std::mem::size_of_val(v.as_slice()),
262 SerializedValue::CharArray(v) => std::mem::size_of_val(v.as_slice()),
263
264 SerializedValue::WithMeta { value, meta } => value.byte_size() + meta.byte_size(),
265 SerializedValue::Reduced(inner) => inner.byte_size(),
266
267 SerializedValue::SharedAtom(_) | SerializedValue::ByteBlob(_) => 0,
269
270 SerializedValue::Var {
272 namespace, name, ..
273 } => namespace.len() + name.len(),
274 };
275
276 NODE + payload
277 }
278}
279
280impl SerializedError {
281 fn byte_size(&self) -> usize {
282 std::mem::size_of::<SerializedError>()
283 + self.message.len()
284 + self.data.as_ref().map_or(0, |pairs| {
285 pairs
286 .iter()
287 .map(|(k, v)| k.byte_size() + v.byte_size())
288 .sum()
289 })
290 + self.cause.as_ref().map_or(0, |c| c.byte_size())
291 }
292}
293
294#[derive(Clone, Debug)]
296pub struct SerializedError {
297 pub kind: SerializedErrorKind,
298 pub message: String,
299 pub data: Option<Vec<(SerializedValue, SerializedValue)>>,
300 pub cause: Option<Box<SerializedError>>,
301}
302
303#[derive(Clone, Debug)]
305pub enum SerializedErrorKind {
306 WrongType {
307 expected: &'static str,
308 got: String,
309 },
310 IndexOutOfBounds {
311 idx: usize,
312 count: usize,
313 },
314 ArityError {
315 name: String,
316 expected: String,
317 got: usize,
318 },
319 NotCallable {
320 value: String,
321 },
322 OddMap {
323 count: usize,
324 },
325 Unsupported,
326 Other(String),
327 GasExhausted,
328 OutOfRange,
329 TransientAlreadyPersisted,
330 Parse,
331 Thrown(Box<SerializedValue>),
332}
333
334pub fn serialize(v: &Value) -> Result<SerializedValue, CloneError> {
339 match v {
340 Value::WithMeta(inner, meta) => Ok(SerializedValue::WithMeta {
342 value: Box::new(serialize(inner)?),
343 meta: Box::new(serialize(meta)?),
344 }),
345 Value::Reduced(inner) => Ok(SerializedValue::Reduced(Box::new(serialize(inner)?))),
346
347 Value::Nil => Ok(SerializedValue::Nil),
349 Value::Bool(b) => Ok(SerializedValue::Bool(*b)),
350 Value::Long(n) => Ok(SerializedValue::Long(*n)),
351 Value::Double(d) => Ok(SerializedValue::Double(*d)),
352 Value::Char(c) => Ok(SerializedValue::Char(*c)),
353 Value::Uuid(u) => Ok(SerializedValue::Uuid(*u)),
354
355 Value::BigInt(p) => Ok(SerializedValue::BigInt(p.get().clone())),
356 Value::BigDecimal(p) => Ok(SerializedValue::BigDecimal(p.get().clone())),
357 Value::Ratio(p) => Ok(SerializedValue::Ratio(p.get().clone())),
358 Value::Str(p) => Ok(SerializedValue::Str(p.get().clone())),
359 Value::Pattern(p) => Ok(SerializedValue::Pattern(p.get().as_str().to_owned())),
360
361 Value::Symbol(p) => {
363 let s = p.get();
364 Ok(SerializedValue::Symbol {
365 namespace: s.namespace.clone(),
366 name: s.name.clone(),
367 version: s.version.clone(),
368 })
369 }
370 Value::Keyword(p) => {
371 let k = p.get();
372 Ok(SerializedValue::Keyword {
373 namespace: k.namespace.clone(),
374 name: k.name.clone(),
375 })
376 }
377
378 Value::List(p) => {
380 let items: Result<Vec<_>, _> = p.get().iter().map(serialize).collect();
381 Ok(SerializedValue::List(items?))
382 }
383 Value::Vector(p) => {
384 let items: Result<Vec<_>, _> = p.get().iter().map(serialize).collect();
385 Ok(SerializedValue::Vector(items?))
386 }
387 Value::Map(m) => serialize_map(m),
388 Value::Set(s) => serialize_set(s),
389 Value::Queue(p) => {
390 let items: Result<Vec<_>, _> = p.get().iter().map(serialize).collect();
391 Ok(SerializedValue::Queue(items?))
392 }
393 Value::Cons(p) => {
394 let c = p.get();
395 Ok(SerializedValue::Cons {
396 head: Box::new(serialize(&c.head)?),
397 tail: Box::new(serialize(&c.tail)?),
398 })
399 }
400
401 Value::TypeInstance(p) => {
403 let ti = p.get();
404 let fields = serialize_map_pairs(&ti.fields)?;
405 Ok(SerializedValue::TypeInstance {
406 type_tag: ti.type_tag.clone(),
407 fields,
408 })
409 }
410
411 Value::Error(p) => Ok(SerializedValue::Error(Box::new(serialize_error(p.get())?))),
413
414 Value::BooleanArray(p) => Ok(SerializedValue::BooleanArray(
416 p.get().lock().unwrap().clone(),
417 )),
418 Value::ByteArray(p) => Ok(SerializedValue::ByteArray(p.get().lock().unwrap().clone())),
419 Value::ShortArray(p) => Ok(SerializedValue::ShortArray(p.get().lock().unwrap().clone())),
420 Value::IntArray(p) => Ok(SerializedValue::IntArray(p.get().lock().unwrap().clone())),
421 Value::LongArray(p) => Ok(SerializedValue::LongArray(p.get().lock().unwrap().clone())),
422 Value::FloatArray(p) => Ok(SerializedValue::FloatArray(p.get().lock().unwrap().clone())),
423 Value::DoubleArray(p) => Ok(SerializedValue::DoubleArray(
424 p.get().lock().unwrap().clone(),
425 )),
426 Value::CharArray(p) => Ok(SerializedValue::CharArray(p.get().lock().unwrap().clone())),
427 Value::ObjectArray(p) => {
428 let guard = p.get().0.lock().unwrap();
429 let items: Result<Vec<_>, _> = guard.iter().map(serialize).collect();
430 Ok(SerializedValue::ObjectArray(items?))
431 }
432
433 Value::LazySeq(p) => serialize(&p.get().realize()),
435
436 Value::Delay(p) => {
438 let state = p.get().state.lock().unwrap();
439 if let DelayState::Forced(v) = &*state {
440 serialize(v)
441 } else {
442 Err(not_shareable("delay"))
443 }
444 }
445
446 Value::Resource(_) => Err(not_shareable("resource")),
448 Value::NativeObject(_) => Err(not_shareable("native-object")),
449 Value::Matcher(_) => Err(not_shareable("matcher")),
450
451 Value::Fn(_) | Value::Macro(_) => Err(not_shareable("fn")),
452 Value::BoundFn(_) => Err(not_shareable("fn")),
453 Value::NativeFunction(_) => Err(not_shareable("fn")),
454 Value::ProtocolFn(_) => Err(not_shareable("fn")),
455 Value::MultiFn(_) => Err(not_shareable("fn")),
456
457 Value::Var(p) => {
459 let var = p.get();
460 let shared_empty = var.shared_root.load().is_none();
467 if shared_empty && var.is_bound() {
468 return Err(not_shareable("var"));
469 }
470 Ok(SerializedValue::Var {
471 namespace: var.namespace.clone(),
472 name: var.name.clone(),
473 is_macro: var.is_macro,
474 shared_root: var.shared_root.clone(),
475 })
476 }
477 Value::Atom(_) => Err(not_shareable("atom")),
478 Value::Volatile(_) => Err(not_shareable("volatile")),
479 Value::Promise(_) => Err(not_shareable("promise")),
480 Value::Future(_) => Err(not_shareable("future")),
481 Value::Agent(_) => Err(not_shareable("agent")),
482
483 Value::Namespace(_) => Err(not_shareable("namespace")),
484 Value::Protocol(_) => Err(not_shareable("protocol")),
485
486 Value::TransientMap(_) => Err(not_shareable("transient-map")),
487 Value::TransientSet(_) => Err(not_shareable("transient-set")),
488 Value::TransientVector(_) => Err(not_shareable("transient-vector")),
489
490 Value::SharedAtom(a) => Ok(SerializedValue::SharedAtom(a.clone())),
492 Value::ByteBlob(b) => Ok(SerializedValue::ByteBlob(b.clone())),
493 }
494}
495
496fn serialize_map(m: &MapValue) -> Result<SerializedValue, CloneError> {
497 let pairs = serialize_map_pairs(m)?;
498 Ok(match m {
499 MapValue::Array(_) => SerializedValue::ArrayMap(pairs),
500 MapValue::Hash(_) => SerializedValue::HashMap(pairs),
501 MapValue::Sorted(_) => SerializedValue::SortedMap(pairs),
502 })
503}
504
505fn serialize_map_pairs(
506 m: &MapValue,
507) -> Result<Vec<(SerializedValue, SerializedValue)>, CloneError> {
508 let mut pairs = Vec::with_capacity(m.count());
509 let mut err: Option<CloneError> = None;
510 m.for_each(|k, v| {
511 if err.is_some() {
512 return;
513 }
514 match (serialize(k), serialize(v)) {
515 (Ok(sk), Ok(sv)) => pairs.push((sk, sv)),
516 (Err(e), _) | (_, Err(e)) => err = Some(e),
517 }
518 });
519 if let Some(e) = err { Err(e) } else { Ok(pairs) }
520}
521
522fn serialize_set(s: &SetValue) -> Result<SerializedValue, CloneError> {
523 let items: Result<Vec<_>, _> = s.iter().map(serialize).collect();
524 Ok(match s {
525 SetValue::Hash(_) => SerializedValue::HashSet(items?),
526 SetValue::Sorted(_) => SerializedValue::SortedSet(items?),
527 })
528}
529
530fn serialize_error(e: &crate::error::ExceptionInfo) -> Result<SerializedError, CloneError> {
531 let kind = match &e.error {
532 ValueError::WrongType { expected, got } => SerializedErrorKind::WrongType {
533 expected,
534 got: got.clone(),
535 },
536 ValueError::IndexOutOfBounds { idx, count } => SerializedErrorKind::IndexOutOfBounds {
537 idx: *idx,
538 count: *count,
539 },
540 ValueError::ArityError {
541 name,
542 expected,
543 got,
544 } => SerializedErrorKind::ArityError {
545 name: name.clone(),
546 expected: expected.clone(),
547 got: *got,
548 },
549 ValueError::NotCallable { value } => SerializedErrorKind::NotCallable {
550 value: value.clone(),
551 },
552 ValueError::OddMap { count } => SerializedErrorKind::OddMap { count: *count },
553 ValueError::Unsupported => SerializedErrorKind::Unsupported,
554 ValueError::Other(s) => SerializedErrorKind::Other(s.clone()),
555 ValueError::GasExhausted => SerializedErrorKind::GasExhausted,
556 ValueError::OutOfRange => SerializedErrorKind::OutOfRange,
557 ValueError::TransientAlreadyPersisted => SerializedErrorKind::TransientAlreadyPersisted,
558 ValueError::Parse => SerializedErrorKind::Parse,
559 ValueError::Thrown(v) => SerializedErrorKind::Thrown(Box::new(serialize(v)?)),
560 };
561
562 let data = e.data.as_ref().map(serialize_map_pairs).transpose()?;
563
564 let cause = e
565 .cause
566 .as_ref()
567 .map(|c| serialize_error(c.get()).map(Box::new))
568 .transpose()?;
569
570 Ok(SerializedError {
571 kind,
572 message: e.message.clone(),
573 data,
574 cause,
575 })
576}
577
578pub fn deserialize(sv: SerializedValue) -> Value {
584 match sv {
585 SerializedValue::WithMeta { value, meta } => {
586 Value::WithMeta(Box::new(deserialize(*value)), Box::new(deserialize(*meta)))
587 }
588 SerializedValue::Reduced(inner) => Value::Reduced(Box::new(deserialize(*inner))),
589
590 SerializedValue::Nil => Value::Nil,
591 SerializedValue::Bool(b) => Value::Bool(b),
592 SerializedValue::Long(n) => Value::Long(n),
593 SerializedValue::Double(d) => Value::Double(d),
594 SerializedValue::Char(c) => Value::Char(c),
595 SerializedValue::Uuid(u) => Value::Uuid(u),
596
597 SerializedValue::BigInt(n) => Value::BigInt(GcPtr::new(n)),
598 SerializedValue::BigDecimal(d) => Value::BigDecimal(GcPtr::new(d)),
599 SerializedValue::Ratio(r) => Value::Ratio(GcPtr::new(r)),
600 SerializedValue::Str(s) => Value::Str(GcPtr::new(s)),
601 SerializedValue::Pattern(src) => Value::Pattern(GcPtr::new(
602 regex::Regex::new(&src).expect("pattern was valid at serialize time"),
603 )),
604
605 SerializedValue::Symbol {
606 namespace,
607 name,
608 version,
609 } => Value::Symbol(GcPtr::new(Symbol {
610 namespace,
611 name,
612 version,
613 })),
614 SerializedValue::Keyword { namespace, name } => {
615 Value::Keyword(GcPtr::new(Keyword { namespace, name }))
616 }
617
618 SerializedValue::List(items) => Value::List(GcPtr::new(PersistentList::from_iter(
619 items.into_iter().map(deserialize),
620 ))),
621 SerializedValue::Vector(items) => Value::Vector(GcPtr::new(PersistentVector::from_iter(
622 items.into_iter().map(deserialize),
623 ))),
624 SerializedValue::ArrayMap(pairs) => Value::Map(MapValue::from_pairs(
625 pairs
626 .into_iter()
627 .map(|(k, v)| (deserialize(k), deserialize(v)))
628 .collect(),
629 )),
630 SerializedValue::HashMap(pairs) => Value::Map(MapValue::from_pairs(
631 pairs
632 .into_iter()
633 .map(|(k, v)| (deserialize(k), deserialize(v)))
634 .collect(),
635 )),
636 SerializedValue::SortedMap(pairs) => {
637 let items: Vec<(Value, Value)> = pairs
639 .into_iter()
640 .map(|(k, v)| (deserialize(k), deserialize(v)))
641 .collect();
642 let sm = SortedMap::from_pairs(items);
643 Value::Map(MapValue::Sorted(GcPtr::new(sm)))
644 }
645 SerializedValue::HashSet(items) => {
646 let mut hs = PersistentHashSet::empty();
647 for item in items.into_iter().map(deserialize) {
648 hs = hs.conj(item);
649 }
650 Value::Set(SetValue::Hash(GcPtr::new(hs)))
651 }
652 SerializedValue::SortedSet(items) => {
653 let mut ss = SortedSet::empty();
654 for item in items.into_iter().map(deserialize) {
655 ss = ss.conj(item);
656 }
657 Value::Set(SetValue::Sorted(GcPtr::new(ss)))
658 }
659 SerializedValue::Queue(items) => {
660 let mut q = PersistentQueue::empty();
661 for item in items.into_iter().map(deserialize) {
662 q = q.conj(item);
663 }
664 Value::Queue(GcPtr::new(q))
665 }
666 SerializedValue::Cons { head, tail } => {
667 use crate::types::CljxCons;
668 Value::Cons(GcPtr::new(CljxCons {
669 head: deserialize(*head),
670 tail: deserialize(*tail),
671 }))
672 }
673
674 SerializedValue::TypeInstance { type_tag, fields } => {
675 use crate::value::TypeInstance;
676 let pairs: Vec<(Value, Value)> = fields
677 .into_iter()
678 .map(|(k, v)| (deserialize(k), deserialize(v)))
679 .collect();
680 Value::TypeInstance(GcPtr::new(TypeInstance {
681 type_tag,
682 fields: MapValue::from_pairs(pairs),
683 }))
684 }
685
686 SerializedValue::Error(se) => Value::Error(GcPtr::new(deserialize_error(*se))),
687
688 SerializedValue::BooleanArray(v) => {
689 Value::BooleanArray(GcPtr::new(std::sync::Mutex::new(v)))
690 }
691 SerializedValue::ByteArray(v) => Value::ByteArray(GcPtr::new(std::sync::Mutex::new(v))),
692 SerializedValue::ShortArray(v) => Value::ShortArray(GcPtr::new(std::sync::Mutex::new(v))),
693 SerializedValue::IntArray(v) => Value::IntArray(GcPtr::new(std::sync::Mutex::new(v))),
694 SerializedValue::LongArray(v) => Value::LongArray(GcPtr::new(std::sync::Mutex::new(v))),
695 SerializedValue::FloatArray(v) => Value::FloatArray(GcPtr::new(std::sync::Mutex::new(v))),
696 SerializedValue::DoubleArray(v) => Value::DoubleArray(GcPtr::new(std::sync::Mutex::new(v))),
697 SerializedValue::CharArray(v) => Value::CharArray(GcPtr::new(std::sync::Mutex::new(v))),
698 SerializedValue::ObjectArray(items) => {
699 use crate::value::ObjectArray;
700 Value::ObjectArray(GcPtr::new(ObjectArray::new(
701 items.into_iter().map(deserialize).collect(),
702 )))
703 }
704
705 SerializedValue::SharedAtom(a) => Value::SharedAtom(a),
707 SerializedValue::ByteBlob(b) => Value::ByteBlob(b),
708 SerializedValue::Var {
709 namespace,
710 name,
711 is_macro,
712 shared_root,
713 } => Value::Var(GcPtr::new(crate::types::Var::from_shared_root(
714 namespace,
715 name,
716 is_macro,
717 shared_root,
718 ))),
719 }
720}
721
722fn deserialize_error(se: SerializedError) -> crate::error::ExceptionInfo {
723 let error = match se.kind {
724 SerializedErrorKind::WrongType { expected, got } => ValueError::WrongType { expected, got },
725 SerializedErrorKind::IndexOutOfBounds { idx, count } => {
726 ValueError::IndexOutOfBounds { idx, count }
727 }
728 SerializedErrorKind::ArityError {
729 name,
730 expected,
731 got,
732 } => ValueError::ArityError {
733 name,
734 expected,
735 got,
736 },
737 SerializedErrorKind::NotCallable { value } => ValueError::NotCallable { value },
738 SerializedErrorKind::OddMap { count } => ValueError::OddMap { count },
739 SerializedErrorKind::Unsupported => ValueError::Unsupported,
740 SerializedErrorKind::Other(s) => ValueError::Other(s),
741 SerializedErrorKind::GasExhausted => ValueError::GasExhausted,
742 SerializedErrorKind::OutOfRange => ValueError::OutOfRange,
743 SerializedErrorKind::TransientAlreadyPersisted => ValueError::TransientAlreadyPersisted,
744 SerializedErrorKind::Parse => ValueError::Parse,
745 SerializedErrorKind::Thrown(sv) => ValueError::Thrown(deserialize(*sv)),
746 };
747
748 let data = se.data.map(|pairs| {
749 MapValue::from_pairs(
750 pairs
751 .into_iter()
752 .map(|(k, v)| (deserialize(k), deserialize(v)))
753 .collect(),
754 )
755 });
756
757 let cause = se.cause.map(|c| GcPtr::new(deserialize_error(*c)));
758
759 crate::error::ExceptionInfo::new(error, se.message, data, cause)
760}
761
762#[cfg(test)]
765mod tests {
766 use super::*;
767
768 fn roundtrip(v: &Value) -> Value {
769 deserialize(serialize(v).expect("serialize"))
770 }
771
772 #[test]
773 fn scalars_roundtrip() {
774 assert_eq!(roundtrip(&Value::Nil), Value::Nil);
775 assert_eq!(roundtrip(&Value::Bool(true)), Value::Bool(true));
776 assert_eq!(roundtrip(&Value::Long(42)), Value::Long(42));
777 assert_eq!(roundtrip(&Value::Char('x')), Value::Char('x'));
778 assert_eq!(roundtrip(&Value::Uuid(12345)), Value::Uuid(12345));
779 }
780
781 #[test]
782 fn string_roundtrip() {
783 let v = Value::string("hello, world");
784 assert_eq!(roundtrip(&v), v);
785 }
786
787 #[test]
788 fn keyword_roundtrip() {
789 let v = Value::keyword(Keyword::simple("foo"));
790 assert_eq!(roundtrip(&v), v);
791 let v2 = Value::keyword(Keyword::qualified("clojure.core", "map"));
792 assert_eq!(roundtrip(&v2), v2);
793 }
794
795 #[test]
796 fn symbol_roundtrip() {
797 let v = Value::symbol(Symbol::simple("my-fn"));
798 assert_eq!(roundtrip(&v), v);
799 }
800
801 #[test]
802 fn list_roundtrip() {
803 let v = Value::List(GcPtr::new(PersistentList::from_iter([
804 Value::Long(1),
805 Value::Long(2),
806 Value::Long(3),
807 ])));
808 assert_eq!(roundtrip(&v), v);
809 }
810
811 #[test]
812 fn vector_roundtrip() {
813 let v = Value::Vector(GcPtr::new(PersistentVector::from_iter([
814 Value::string("a"),
815 Value::Bool(false),
816 Value::Nil,
817 ])));
818 assert_eq!(roundtrip(&v), v);
819 }
820
821 #[test]
822 fn nested_map_roundtrip() {
823 let inner = Value::Vector(GcPtr::new(PersistentVector::from_iter([Value::Long(1)])));
824 let v = MapValue::from_pairs(vec![(Value::keyword(Keyword::simple("k")), inner)]);
825 let v = Value::Map(v);
826 assert_eq!(roundtrip(&v), v);
827 }
828
829 #[test]
830 fn resource_not_shareable() {
831 use crate::resource::ResourceHandle;
832 use std::any::Any;
833 use std::sync::Arc;
834 #[derive(Debug)]
835 struct FakeResource;
836 impl crate::resource::Resource for FakeResource {
837 fn resource_type(&self) -> &'static str {
838 "fake"
839 }
840 fn close(&self) -> crate::error::ValueResult<()> {
841 Ok(())
842 }
843 fn is_closed(&self) -> bool {
844 false
845 }
846 fn as_any(&self) -> &dyn Any {
847 self
848 }
849 }
850 let r = Value::Resource(ResourceHandle(Arc::new(FakeResource)));
851 assert!(matches!(
852 serialize(&r),
853 Err(CloneError::NotShareable {
854 type_name: "resource"
855 })
856 ));
857 }
858
859 #[test]
860 fn atom_not_shareable() {
861 use crate::types::Atom;
862 let a = Value::Atom(GcPtr::new(Atom::new(Value::Nil)));
863 assert!(matches!(
864 serialize(&a),
865 Err(CloneError::NotShareable { type_name: "atom" })
866 ));
867 }
868
869 #[test]
870 fn fn_not_shareable() {
871 use crate::types::{Arity, NativeFn};
872 let nf = Value::NativeFunction(GcPtr::new(NativeFn::new("test", Arity::Fixed(0), |_| {
873 Ok(Value::Nil)
874 })));
875 assert!(matches!(
876 serialize(&nf),
877 Err(CloneError::NotShareable { type_name: "fn" })
878 ));
879 }
880
881 #[test]
882 fn lazy_seq_realized_roundtrip() {
883 use crate::types::{LazySeq, Thunk};
884 struct DoneThunk;
885 impl std::fmt::Debug for DoneThunk {
886 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
887 write!(f, "DoneThunk")
888 }
889 }
890 impl cljrs_gc::Trace for DoneThunk {
891 fn trace(&self, _: &mut cljrs_gc::MarkVisitor) {}
892 }
893 impl Thunk for DoneThunk {
894 fn force(&self) -> Result<Value, String> {
895 Ok(Value::Long(99))
896 }
897 }
898 let ls = LazySeq::new(Box::new(DoneThunk));
899 let _ = ls.realize(); let v = Value::LazySeq(GcPtr::new(ls));
901 assert_eq!(roundtrip(&v), Value::Long(99));
902 }
903
904 #[test]
905 fn with_meta_roundtrip() {
906 let v = Value::Long(7).with_meta(Value::Map(MapValue::empty()));
907 let rt = roundtrip(&v);
908 assert_eq!(rt, Value::Long(7));
910 }
911
912 #[test]
913 fn reduced_roundtrip() {
914 let v = Value::Reduced(Box::new(Value::Long(55)));
915 assert_eq!(roundtrip(&v), Value::Reduced(Box::new(Value::Long(55))));
916 }
917
918 #[test]
919 fn byte_size_counts_string_payload() {
920 let small = serialize(&Value::string("hi")).unwrap();
921 let large = serialize(&Value::string("x".repeat(1000))).unwrap();
922 assert!(large.byte_size() > small.byte_size() + 900);
924 }
925
926 #[test]
927 fn byte_size_grows_with_collection() {
928 let small = serialize(&Value::Vector(GcPtr::new(PersistentVector::from_iter([
929 Value::Long(1),
930 ]))))
931 .unwrap();
932 let large = serialize(&Value::Vector(GcPtr::new(PersistentVector::from_iter(
933 (0..100).map(Value::Long),
934 ))))
935 .unwrap();
936 assert!(large.byte_size() > small.byte_size());
937 }
938
939 #[test]
940 fn var_with_promotable_root_roundtrips() {
941 use crate::types::Var;
942 let var = Var::new("user", "answer");
944 var.bind(Value::Long(42));
945 let v = Value::Var(GcPtr::new(var));
946
947 let crossed = roundtrip(&v);
948 if let Value::Var(p) = crossed {
949 let got = p.get();
950 assert_eq!(got.namespace.as_ref(), "user");
951 assert_eq!(got.name.as_ref(), "answer");
952 assert_eq!(got.deref(), Some(Value::Long(42)));
954 } else {
955 panic!("expected a Var on the receiving side");
956 }
957 }
958
959 #[test]
960 fn var_keyword_root_preserves_identity() {
961 use crate::types::Var;
962 let var = Var::new("user", "k");
963 var.bind(Value::keyword(Keyword::qualified("ns", "kw")));
964 let v = Value::Var(GcPtr::new(var));
965
966 let crossed = roundtrip(&v);
967 let Value::Var(p) = crossed else {
968 panic!("expected Var")
969 };
970 assert_eq!(
972 p.get().deref(),
973 Some(Value::keyword(Keyword::qualified("ns", "kw")))
974 );
975 }
976
977 #[test]
978 fn var_shares_root_cell_across_boundary() {
979 use crate::types::Var;
980 let var = GcPtr::new(Var::new("user", "shared"));
983 var.get().bind(Value::Long(1));
984 let v = Value::Var(var.clone());
985
986 let Value::Var(recv) = roundtrip(&v) else {
987 panic!("expected Var")
988 };
989 assert_eq!(recv.get().deref_shared(), Some(Value::Long(1)));
990
991 var.get().bind(Value::Long(2));
993 assert_eq!(recv.get().deref_shared(), Some(Value::Long(2)));
994 }
995
996 #[test]
997 fn var_with_nonpromotable_root_is_not_shareable() {
998 use crate::types::{Arity, NativeFn, Var};
999 let var = Var::new("user", "f");
1001 var.bind(Value::NativeFunction(GcPtr::new(NativeFn::new(
1002 "f",
1003 Arity::Fixed(0),
1004 |_| Ok(Value::Nil),
1005 ))));
1006 let v = Value::Var(GcPtr::new(var));
1007 assert!(matches!(
1008 serialize(&v),
1009 Err(CloneError::NotShareable { type_name: "var" })
1010 ));
1011 }
1012
1013 #[test]
1014 fn unbound_var_crosses_as_unbound() {
1015 use crate::types::Var;
1016 let v = Value::Var(GcPtr::new(Var::new("user", "later")));
1017 let Value::Var(p) = roundtrip(&v) else {
1018 panic!("expected Var")
1019 };
1020 assert!(!p.get().is_bound());
1021 }
1022
1023 #[test]
1024 fn byte_size_scalar_is_node_sized() {
1025 let node = std::mem::size_of::<SerializedValue>();
1027 assert_eq!(serialize(&Value::Long(7)).unwrap().byte_size(), node);
1028 assert_eq!(serialize(&Value::Nil).unwrap().byte_size(), node);
1029 }
1030}