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 OutOfRange,
328 TransientAlreadyPersisted,
329 Parse,
330 Thrown(Box<SerializedValue>),
331}
332
333pub fn serialize(v: &Value) -> Result<SerializedValue, CloneError> {
338 match v {
339 Value::WithMeta(inner, meta) => Ok(SerializedValue::WithMeta {
341 value: Box::new(serialize(inner)?),
342 meta: Box::new(serialize(meta)?),
343 }),
344 Value::Reduced(inner) => Ok(SerializedValue::Reduced(Box::new(serialize(inner)?))),
345
346 Value::Nil => Ok(SerializedValue::Nil),
348 Value::Bool(b) => Ok(SerializedValue::Bool(*b)),
349 Value::Long(n) => Ok(SerializedValue::Long(*n)),
350 Value::Double(d) => Ok(SerializedValue::Double(*d)),
351 Value::Char(c) => Ok(SerializedValue::Char(*c)),
352 Value::Uuid(u) => Ok(SerializedValue::Uuid(*u)),
353
354 Value::BigInt(p) => Ok(SerializedValue::BigInt(p.get().clone())),
355 Value::BigDecimal(p) => Ok(SerializedValue::BigDecimal(p.get().clone())),
356 Value::Ratio(p) => Ok(SerializedValue::Ratio(p.get().clone())),
357 Value::Str(p) => Ok(SerializedValue::Str(p.get().clone())),
358 Value::Pattern(p) => Ok(SerializedValue::Pattern(p.get().as_str().to_owned())),
359
360 Value::Symbol(p) => {
362 let s = p.get();
363 Ok(SerializedValue::Symbol {
364 namespace: s.namespace.clone(),
365 name: s.name.clone(),
366 version: s.version.clone(),
367 })
368 }
369 Value::Keyword(p) => {
370 let k = p.get();
371 Ok(SerializedValue::Keyword {
372 namespace: k.namespace.clone(),
373 name: k.name.clone(),
374 })
375 }
376
377 Value::List(p) => {
379 let items: Result<Vec<_>, _> = p.get().iter().map(serialize).collect();
380 Ok(SerializedValue::List(items?))
381 }
382 Value::Vector(p) => {
383 let items: Result<Vec<_>, _> = p.get().iter().map(serialize).collect();
384 Ok(SerializedValue::Vector(items?))
385 }
386 Value::Map(m) => serialize_map(m),
387 Value::Set(s) => serialize_set(s),
388 Value::Queue(p) => {
389 let items: Result<Vec<_>, _> = p.get().iter().map(serialize).collect();
390 Ok(SerializedValue::Queue(items?))
391 }
392 Value::Cons(p) => {
393 let c = p.get();
394 Ok(SerializedValue::Cons {
395 head: Box::new(serialize(&c.head)?),
396 tail: Box::new(serialize(&c.tail)?),
397 })
398 }
399
400 Value::TypeInstance(p) => {
402 let ti = p.get();
403 let fields = serialize_map_pairs(&ti.fields)?;
404 Ok(SerializedValue::TypeInstance {
405 type_tag: ti.type_tag.clone(),
406 fields,
407 })
408 }
409
410 Value::Error(p) => Ok(SerializedValue::Error(Box::new(serialize_error(p.get())?))),
412
413 Value::BooleanArray(p) => Ok(SerializedValue::BooleanArray(
415 p.get().lock().unwrap().clone(),
416 )),
417 Value::ByteArray(p) => Ok(SerializedValue::ByteArray(p.get().lock().unwrap().clone())),
418 Value::ShortArray(p) => Ok(SerializedValue::ShortArray(p.get().lock().unwrap().clone())),
419 Value::IntArray(p) => Ok(SerializedValue::IntArray(p.get().lock().unwrap().clone())),
420 Value::LongArray(p) => Ok(SerializedValue::LongArray(p.get().lock().unwrap().clone())),
421 Value::FloatArray(p) => Ok(SerializedValue::FloatArray(p.get().lock().unwrap().clone())),
422 Value::DoubleArray(p) => Ok(SerializedValue::DoubleArray(
423 p.get().lock().unwrap().clone(),
424 )),
425 Value::CharArray(p) => Ok(SerializedValue::CharArray(p.get().lock().unwrap().clone())),
426 Value::ObjectArray(p) => {
427 let guard = p.get().0.lock().unwrap();
428 let items: Result<Vec<_>, _> = guard.iter().map(serialize).collect();
429 Ok(SerializedValue::ObjectArray(items?))
430 }
431
432 Value::LazySeq(p) => serialize(&p.get().realize()),
434
435 Value::Delay(p) => {
437 let state = p.get().state.lock().unwrap();
438 if let DelayState::Forced(v) = &*state {
439 serialize(v)
440 } else {
441 Err(not_shareable("delay"))
442 }
443 }
444
445 Value::Resource(_) => Err(not_shareable("resource")),
447 Value::NativeObject(_) => Err(not_shareable("native-object")),
448 Value::Matcher(_) => Err(not_shareable("matcher")),
449
450 Value::Fn(_) | Value::Macro(_) => Err(not_shareable("fn")),
451 Value::BoundFn(_) => Err(not_shareable("fn")),
452 Value::NativeFunction(_) => Err(not_shareable("fn")),
453 Value::ProtocolFn(_) => Err(not_shareable("fn")),
454 Value::MultiFn(_) => Err(not_shareable("fn")),
455
456 Value::Var(p) => {
458 let var = p.get();
459 let shared_empty = var.shared_root.load().is_none();
466 if shared_empty && var.is_bound() {
467 return Err(not_shareable("var"));
468 }
469 Ok(SerializedValue::Var {
470 namespace: var.namespace.clone(),
471 name: var.name.clone(),
472 is_macro: var.is_macro,
473 shared_root: var.shared_root.clone(),
474 })
475 }
476 Value::Atom(_) => Err(not_shareable("atom")),
477 Value::Volatile(_) => Err(not_shareable("volatile")),
478 Value::Promise(_) => Err(not_shareable("promise")),
479 Value::Future(_) => Err(not_shareable("future")),
480 Value::Agent(_) => Err(not_shareable("agent")),
481
482 Value::Namespace(_) => Err(not_shareable("namespace")),
483 Value::Protocol(_) => Err(not_shareable("protocol")),
484
485 Value::TransientMap(_) => Err(not_shareable("transient-map")),
486 Value::TransientSet(_) => Err(not_shareable("transient-set")),
487 Value::TransientVector(_) => Err(not_shareable("transient-vector")),
488
489 Value::SharedAtom(a) => Ok(SerializedValue::SharedAtom(a.clone())),
491 Value::ByteBlob(b) => Ok(SerializedValue::ByteBlob(b.clone())),
492 }
493}
494
495fn serialize_map(m: &MapValue) -> Result<SerializedValue, CloneError> {
496 let pairs = serialize_map_pairs(m)?;
497 Ok(match m {
498 MapValue::Array(_) => SerializedValue::ArrayMap(pairs),
499 MapValue::Hash(_) => SerializedValue::HashMap(pairs),
500 MapValue::Sorted(_) => SerializedValue::SortedMap(pairs),
501 })
502}
503
504fn serialize_map_pairs(
505 m: &MapValue,
506) -> Result<Vec<(SerializedValue, SerializedValue)>, CloneError> {
507 let mut pairs = Vec::with_capacity(m.count());
508 let mut err: Option<CloneError> = None;
509 m.for_each(|k, v| {
510 if err.is_some() {
511 return;
512 }
513 match (serialize(k), serialize(v)) {
514 (Ok(sk), Ok(sv)) => pairs.push((sk, sv)),
515 (Err(e), _) | (_, Err(e)) => err = Some(e),
516 }
517 });
518 if let Some(e) = err { Err(e) } else { Ok(pairs) }
519}
520
521fn serialize_set(s: &SetValue) -> Result<SerializedValue, CloneError> {
522 let items: Result<Vec<_>, _> = s.iter().map(serialize).collect();
523 Ok(match s {
524 SetValue::Hash(_) => SerializedValue::HashSet(items?),
525 SetValue::Sorted(_) => SerializedValue::SortedSet(items?),
526 })
527}
528
529fn serialize_error(e: &crate::error::ExceptionInfo) -> Result<SerializedError, CloneError> {
530 let kind = match &e.error {
531 ValueError::WrongType { expected, got } => SerializedErrorKind::WrongType {
532 expected,
533 got: got.clone(),
534 },
535 ValueError::IndexOutOfBounds { idx, count } => SerializedErrorKind::IndexOutOfBounds {
536 idx: *idx,
537 count: *count,
538 },
539 ValueError::ArityError {
540 name,
541 expected,
542 got,
543 } => SerializedErrorKind::ArityError {
544 name: name.clone(),
545 expected: expected.clone(),
546 got: *got,
547 },
548 ValueError::NotCallable { value } => SerializedErrorKind::NotCallable {
549 value: value.clone(),
550 },
551 ValueError::OddMap { count } => SerializedErrorKind::OddMap { count: *count },
552 ValueError::Unsupported => SerializedErrorKind::Unsupported,
553 ValueError::Other(s) => SerializedErrorKind::Other(s.clone()),
554 ValueError::OutOfRange => SerializedErrorKind::OutOfRange,
555 ValueError::TransientAlreadyPersisted => SerializedErrorKind::TransientAlreadyPersisted,
556 ValueError::Parse => SerializedErrorKind::Parse,
557 ValueError::Thrown(v) => SerializedErrorKind::Thrown(Box::new(serialize(v)?)),
558 };
559
560 let data = e.data.as_ref().map(serialize_map_pairs).transpose()?;
561
562 let cause = e
563 .cause
564 .as_ref()
565 .map(|c| serialize_error(c.get()).map(Box::new))
566 .transpose()?;
567
568 Ok(SerializedError {
569 kind,
570 message: e.message.clone(),
571 data,
572 cause,
573 })
574}
575
576pub fn deserialize(sv: SerializedValue) -> Value {
582 match sv {
583 SerializedValue::WithMeta { value, meta } => {
584 Value::WithMeta(Box::new(deserialize(*value)), Box::new(deserialize(*meta)))
585 }
586 SerializedValue::Reduced(inner) => Value::Reduced(Box::new(deserialize(*inner))),
587
588 SerializedValue::Nil => Value::Nil,
589 SerializedValue::Bool(b) => Value::Bool(b),
590 SerializedValue::Long(n) => Value::Long(n),
591 SerializedValue::Double(d) => Value::Double(d),
592 SerializedValue::Char(c) => Value::Char(c),
593 SerializedValue::Uuid(u) => Value::Uuid(u),
594
595 SerializedValue::BigInt(n) => Value::BigInt(GcPtr::new(n)),
596 SerializedValue::BigDecimal(d) => Value::BigDecimal(GcPtr::new(d)),
597 SerializedValue::Ratio(r) => Value::Ratio(GcPtr::new(r)),
598 SerializedValue::Str(s) => Value::Str(GcPtr::new(s)),
599 SerializedValue::Pattern(src) => Value::Pattern(GcPtr::new(
600 regex::Regex::new(&src).expect("pattern was valid at serialize time"),
601 )),
602
603 SerializedValue::Symbol {
604 namespace,
605 name,
606 version,
607 } => Value::Symbol(GcPtr::new(Symbol {
608 namespace,
609 name,
610 version,
611 })),
612 SerializedValue::Keyword { namespace, name } => {
613 Value::Keyword(GcPtr::new(Keyword { namespace, name }))
614 }
615
616 SerializedValue::List(items) => Value::List(GcPtr::new(PersistentList::from_iter(
617 items.into_iter().map(deserialize),
618 ))),
619 SerializedValue::Vector(items) => Value::Vector(GcPtr::new(PersistentVector::from_iter(
620 items.into_iter().map(deserialize),
621 ))),
622 SerializedValue::ArrayMap(pairs) => Value::Map(MapValue::from_pairs(
623 pairs
624 .into_iter()
625 .map(|(k, v)| (deserialize(k), deserialize(v)))
626 .collect(),
627 )),
628 SerializedValue::HashMap(pairs) => Value::Map(MapValue::from_pairs(
629 pairs
630 .into_iter()
631 .map(|(k, v)| (deserialize(k), deserialize(v)))
632 .collect(),
633 )),
634 SerializedValue::SortedMap(pairs) => {
635 let items: Vec<(Value, Value)> = pairs
637 .into_iter()
638 .map(|(k, v)| (deserialize(k), deserialize(v)))
639 .collect();
640 let sm = SortedMap::from_pairs(items);
641 Value::Map(MapValue::Sorted(GcPtr::new(sm)))
642 }
643 SerializedValue::HashSet(items) => {
644 let mut hs = PersistentHashSet::empty();
645 for item in items.into_iter().map(deserialize) {
646 hs = hs.conj(item);
647 }
648 Value::Set(SetValue::Hash(GcPtr::new(hs)))
649 }
650 SerializedValue::SortedSet(items) => {
651 let mut ss = SortedSet::empty();
652 for item in items.into_iter().map(deserialize) {
653 ss = ss.conj(item);
654 }
655 Value::Set(SetValue::Sorted(GcPtr::new(ss)))
656 }
657 SerializedValue::Queue(items) => {
658 let mut q = PersistentQueue::empty();
659 for item in items.into_iter().map(deserialize) {
660 q = q.conj(item);
661 }
662 Value::Queue(GcPtr::new(q))
663 }
664 SerializedValue::Cons { head, tail } => {
665 use crate::types::CljxCons;
666 Value::Cons(GcPtr::new(CljxCons {
667 head: deserialize(*head),
668 tail: deserialize(*tail),
669 }))
670 }
671
672 SerializedValue::TypeInstance { type_tag, fields } => {
673 use crate::value::TypeInstance;
674 let pairs: Vec<(Value, Value)> = fields
675 .into_iter()
676 .map(|(k, v)| (deserialize(k), deserialize(v)))
677 .collect();
678 Value::TypeInstance(GcPtr::new(TypeInstance {
679 type_tag,
680 fields: MapValue::from_pairs(pairs),
681 }))
682 }
683
684 SerializedValue::Error(se) => Value::Error(GcPtr::new(deserialize_error(*se))),
685
686 SerializedValue::BooleanArray(v) => {
687 Value::BooleanArray(GcPtr::new(std::sync::Mutex::new(v)))
688 }
689 SerializedValue::ByteArray(v) => Value::ByteArray(GcPtr::new(std::sync::Mutex::new(v))),
690 SerializedValue::ShortArray(v) => Value::ShortArray(GcPtr::new(std::sync::Mutex::new(v))),
691 SerializedValue::IntArray(v) => Value::IntArray(GcPtr::new(std::sync::Mutex::new(v))),
692 SerializedValue::LongArray(v) => Value::LongArray(GcPtr::new(std::sync::Mutex::new(v))),
693 SerializedValue::FloatArray(v) => Value::FloatArray(GcPtr::new(std::sync::Mutex::new(v))),
694 SerializedValue::DoubleArray(v) => Value::DoubleArray(GcPtr::new(std::sync::Mutex::new(v))),
695 SerializedValue::CharArray(v) => Value::CharArray(GcPtr::new(std::sync::Mutex::new(v))),
696 SerializedValue::ObjectArray(items) => {
697 use crate::value::ObjectArray;
698 Value::ObjectArray(GcPtr::new(ObjectArray::new(
699 items.into_iter().map(deserialize).collect(),
700 )))
701 }
702
703 SerializedValue::SharedAtom(a) => Value::SharedAtom(a),
705 SerializedValue::ByteBlob(b) => Value::ByteBlob(b),
706 SerializedValue::Var {
707 namespace,
708 name,
709 is_macro,
710 shared_root,
711 } => Value::Var(GcPtr::new(crate::types::Var::from_shared_root(
712 namespace,
713 name,
714 is_macro,
715 shared_root,
716 ))),
717 }
718}
719
720fn deserialize_error(se: SerializedError) -> crate::error::ExceptionInfo {
721 let error = match se.kind {
722 SerializedErrorKind::WrongType { expected, got } => ValueError::WrongType { expected, got },
723 SerializedErrorKind::IndexOutOfBounds { idx, count } => {
724 ValueError::IndexOutOfBounds { idx, count }
725 }
726 SerializedErrorKind::ArityError {
727 name,
728 expected,
729 got,
730 } => ValueError::ArityError {
731 name,
732 expected,
733 got,
734 },
735 SerializedErrorKind::NotCallable { value } => ValueError::NotCallable { value },
736 SerializedErrorKind::OddMap { count } => ValueError::OddMap { count },
737 SerializedErrorKind::Unsupported => ValueError::Unsupported,
738 SerializedErrorKind::Other(s) => ValueError::Other(s),
739 SerializedErrorKind::OutOfRange => ValueError::OutOfRange,
740 SerializedErrorKind::TransientAlreadyPersisted => ValueError::TransientAlreadyPersisted,
741 SerializedErrorKind::Parse => ValueError::Parse,
742 SerializedErrorKind::Thrown(sv) => ValueError::Thrown(deserialize(*sv)),
743 };
744
745 let data = se.data.map(|pairs| {
746 MapValue::from_pairs(
747 pairs
748 .into_iter()
749 .map(|(k, v)| (deserialize(k), deserialize(v)))
750 .collect(),
751 )
752 });
753
754 let cause = se.cause.map(|c| GcPtr::new(deserialize_error(*c)));
755
756 crate::error::ExceptionInfo::new(error, se.message, data, cause)
757}
758
759#[cfg(test)]
762mod tests {
763 use super::*;
764
765 fn roundtrip(v: &Value) -> Value {
766 deserialize(serialize(v).expect("serialize"))
767 }
768
769 #[test]
770 fn scalars_roundtrip() {
771 assert_eq!(roundtrip(&Value::Nil), Value::Nil);
772 assert_eq!(roundtrip(&Value::Bool(true)), Value::Bool(true));
773 assert_eq!(roundtrip(&Value::Long(42)), Value::Long(42));
774 assert_eq!(roundtrip(&Value::Char('x')), Value::Char('x'));
775 assert_eq!(roundtrip(&Value::Uuid(12345)), Value::Uuid(12345));
776 }
777
778 #[test]
779 fn string_roundtrip() {
780 let v = Value::string("hello, world");
781 assert_eq!(roundtrip(&v), v);
782 }
783
784 #[test]
785 fn keyword_roundtrip() {
786 let v = Value::keyword(Keyword::simple("foo"));
787 assert_eq!(roundtrip(&v), v);
788 let v2 = Value::keyword(Keyword::qualified("clojure.core", "map"));
789 assert_eq!(roundtrip(&v2), v2);
790 }
791
792 #[test]
793 fn symbol_roundtrip() {
794 let v = Value::symbol(Symbol::simple("my-fn"));
795 assert_eq!(roundtrip(&v), v);
796 }
797
798 #[test]
799 fn list_roundtrip() {
800 let v = Value::List(GcPtr::new(PersistentList::from_iter([
801 Value::Long(1),
802 Value::Long(2),
803 Value::Long(3),
804 ])));
805 assert_eq!(roundtrip(&v), v);
806 }
807
808 #[test]
809 fn vector_roundtrip() {
810 let v = Value::Vector(GcPtr::new(PersistentVector::from_iter([
811 Value::string("a"),
812 Value::Bool(false),
813 Value::Nil,
814 ])));
815 assert_eq!(roundtrip(&v), v);
816 }
817
818 #[test]
819 fn nested_map_roundtrip() {
820 let inner = Value::Vector(GcPtr::new(PersistentVector::from_iter([Value::Long(1)])));
821 let v = MapValue::from_pairs(vec![(Value::keyword(Keyword::simple("k")), inner)]);
822 let v = Value::Map(v);
823 assert_eq!(roundtrip(&v), v);
824 }
825
826 #[test]
827 fn resource_not_shareable() {
828 use crate::resource::ResourceHandle;
829 use std::any::Any;
830 use std::sync::Arc;
831 #[derive(Debug)]
832 struct FakeResource;
833 impl crate::resource::Resource for FakeResource {
834 fn resource_type(&self) -> &'static str {
835 "fake"
836 }
837 fn close(&self) -> crate::error::ValueResult<()> {
838 Ok(())
839 }
840 fn is_closed(&self) -> bool {
841 false
842 }
843 fn as_any(&self) -> &dyn Any {
844 self
845 }
846 }
847 let r = Value::Resource(ResourceHandle(Arc::new(FakeResource)));
848 assert!(matches!(
849 serialize(&r),
850 Err(CloneError::NotShareable {
851 type_name: "resource"
852 })
853 ));
854 }
855
856 #[test]
857 fn atom_not_shareable() {
858 use crate::types::Atom;
859 let a = Value::Atom(GcPtr::new(Atom::new(Value::Nil)));
860 assert!(matches!(
861 serialize(&a),
862 Err(CloneError::NotShareable { type_name: "atom" })
863 ));
864 }
865
866 #[test]
867 fn fn_not_shareable() {
868 use crate::types::{Arity, NativeFn};
869 let nf = Value::NativeFunction(GcPtr::new(NativeFn::new("test", Arity::Fixed(0), |_| {
870 Ok(Value::Nil)
871 })));
872 assert!(matches!(
873 serialize(&nf),
874 Err(CloneError::NotShareable { type_name: "fn" })
875 ));
876 }
877
878 #[test]
879 fn lazy_seq_realized_roundtrip() {
880 use crate::types::{LazySeq, Thunk};
881 struct DoneThunk;
882 impl std::fmt::Debug for DoneThunk {
883 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
884 write!(f, "DoneThunk")
885 }
886 }
887 impl cljrs_gc::Trace for DoneThunk {
888 fn trace(&self, _: &mut cljrs_gc::MarkVisitor) {}
889 }
890 impl Thunk for DoneThunk {
891 fn force(&self) -> Result<Value, String> {
892 Ok(Value::Long(99))
893 }
894 }
895 let ls = LazySeq::new(Box::new(DoneThunk));
896 let _ = ls.realize(); let v = Value::LazySeq(GcPtr::new(ls));
898 assert_eq!(roundtrip(&v), Value::Long(99));
899 }
900
901 #[test]
902 fn with_meta_roundtrip() {
903 let v = Value::Long(7).with_meta(Value::Map(MapValue::empty()));
904 let rt = roundtrip(&v);
905 assert_eq!(rt, Value::Long(7));
907 }
908
909 #[test]
910 fn reduced_roundtrip() {
911 let v = Value::Reduced(Box::new(Value::Long(55)));
912 assert_eq!(roundtrip(&v), Value::Reduced(Box::new(Value::Long(55))));
913 }
914
915 #[test]
916 fn byte_size_counts_string_payload() {
917 let small = serialize(&Value::string("hi")).unwrap();
918 let large = serialize(&Value::string("x".repeat(1000))).unwrap();
919 assert!(large.byte_size() > small.byte_size() + 900);
921 }
922
923 #[test]
924 fn byte_size_grows_with_collection() {
925 let small = serialize(&Value::Vector(GcPtr::new(PersistentVector::from_iter([
926 Value::Long(1),
927 ]))))
928 .unwrap();
929 let large = serialize(&Value::Vector(GcPtr::new(PersistentVector::from_iter(
930 (0..100).map(Value::Long),
931 ))))
932 .unwrap();
933 assert!(large.byte_size() > small.byte_size());
934 }
935
936 #[test]
937 fn var_with_promotable_root_roundtrips() {
938 use crate::types::Var;
939 let var = Var::new("user", "answer");
941 var.bind(Value::Long(42));
942 let v = Value::Var(GcPtr::new(var));
943
944 let crossed = roundtrip(&v);
945 if let Value::Var(p) = crossed {
946 let got = p.get();
947 assert_eq!(got.namespace.as_ref(), "user");
948 assert_eq!(got.name.as_ref(), "answer");
949 assert_eq!(got.deref(), Some(Value::Long(42)));
951 } else {
952 panic!("expected a Var on the receiving side");
953 }
954 }
955
956 #[test]
957 fn var_keyword_root_preserves_identity() {
958 use crate::types::Var;
959 let var = Var::new("user", "k");
960 var.bind(Value::keyword(Keyword::qualified("ns", "kw")));
961 let v = Value::Var(GcPtr::new(var));
962
963 let crossed = roundtrip(&v);
964 let Value::Var(p) = crossed else {
965 panic!("expected Var")
966 };
967 assert_eq!(
969 p.get().deref(),
970 Some(Value::keyword(Keyword::qualified("ns", "kw")))
971 );
972 }
973
974 #[test]
975 fn var_shares_root_cell_across_boundary() {
976 use crate::types::Var;
977 let var = GcPtr::new(Var::new("user", "shared"));
980 var.get().bind(Value::Long(1));
981 let v = Value::Var(var.clone());
982
983 let Value::Var(recv) = roundtrip(&v) else {
984 panic!("expected Var")
985 };
986 assert_eq!(recv.get().deref_shared(), Some(Value::Long(1)));
987
988 var.get().bind(Value::Long(2));
990 assert_eq!(recv.get().deref_shared(), Some(Value::Long(2)));
991 }
992
993 #[test]
994 fn var_with_nonpromotable_root_is_not_shareable() {
995 use crate::types::{Arity, NativeFn, Var};
996 let var = Var::new("user", "f");
998 var.bind(Value::NativeFunction(GcPtr::new(NativeFn::new(
999 "f",
1000 Arity::Fixed(0),
1001 |_| Ok(Value::Nil),
1002 ))));
1003 let v = Value::Var(GcPtr::new(var));
1004 assert!(matches!(
1005 serialize(&v),
1006 Err(CloneError::NotShareable { type_name: "var" })
1007 ));
1008 }
1009
1010 #[test]
1011 fn unbound_var_crosses_as_unbound() {
1012 use crate::types::Var;
1013 let v = Value::Var(GcPtr::new(Var::new("user", "later")));
1014 let Value::Var(p) = roundtrip(&v) else {
1015 panic!("expected Var")
1016 };
1017 assert!(!p.get().is_bound());
1018 }
1019
1020 #[test]
1021 fn byte_size_scalar_is_node_sized() {
1022 let node = std::mem::size_of::<SerializedValue>();
1024 assert_eq!(serialize(&Value::Long(7)).unwrap().byte_size(), node);
1025 assert_eq!(serialize(&Value::Nil).unwrap().byte_size(), node);
1026 }
1027}