1use std::sync::Arc;
39
40use num_bigint::BigInt;
41
42use crate::collections::{PersistentHashSet, PersistentVector, SortedMap, SortedSet};
43use crate::error::ValueError;
44use crate::shared::SharedAtom;
45use crate::types::DelayState;
46use crate::{Keyword, MapValue, PersistentList, PersistentQueue, SetValue, Symbol, Value};
47use cljrs_gc::GcPtr;
48
49#[derive(Debug, Clone, PartialEq, Eq)]
53pub enum CloneError {
54 NotShareable {
56 type_name: &'static str,
58 },
59 Disconnected,
61}
62
63impl std::fmt::Display for CloneError {
64 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
65 match self {
66 CloneError::NotShareable { type_name } => {
67 write!(
68 f,
69 "value of type `{type_name}` cannot cross an isolate boundary"
70 )
71 }
72 CloneError::Disconnected => write!(f, "isolate channel is disconnected"),
73 }
74 }
75}
76
77impl std::error::Error for CloneError {}
78
79fn not_shareable(type_name: &'static str) -> CloneError {
80 CloneError::NotShareable { type_name }
81}
82
83#[derive(Clone, Debug)]
89pub enum SerializedValue {
90 Nil,
92 Bool(bool),
93 Long(i64),
94 Double(f64),
95 BigInt(BigInt),
96 BigDecimal(bigdecimal::BigDecimal),
97 Ratio(num_rational::Ratio<BigInt>),
98 Char(char),
99 Str(String),
100 Uuid(u128),
101 Pattern(String),
103
104 Symbol {
106 namespace: Option<Arc<str>>,
107 name: Arc<str>,
108 version: Option<Arc<str>>,
109 },
110 Keyword {
111 namespace: Option<Arc<str>>,
112 name: Arc<str>,
113 },
114
115 List(Vec<SerializedValue>),
117 Vector(Vec<SerializedValue>),
118 ArrayMap(Vec<(SerializedValue, SerializedValue)>),
119 HashMap(Vec<(SerializedValue, SerializedValue)>),
120 SortedMap(Vec<(SerializedValue, SerializedValue)>),
121 HashSet(Vec<SerializedValue>),
122 SortedSet(Vec<SerializedValue>),
123 Queue(Vec<SerializedValue>),
124 Cons {
125 head: Box<SerializedValue>,
126 tail: Box<SerializedValue>,
127 },
128
129 TypeInstance {
131 type_tag: Arc<str>,
132 fields: Vec<(SerializedValue, SerializedValue)>,
133 },
134
135 Error(Box<SerializedError>),
137
138 BooleanArray(Vec<bool>),
140 ByteArray(Vec<i8>),
141 ShortArray(Vec<i16>),
142 IntArray(Vec<i32>),
143 LongArray(Vec<i64>),
144 FloatArray(Vec<f32>),
145 DoubleArray(Vec<f64>),
146 CharArray(Vec<char>),
147 ObjectArray(Vec<SerializedValue>),
148
149 WithMeta {
151 value: Box<SerializedValue>,
152 meta: Box<SerializedValue>,
153 },
154 Reduced(Box<SerializedValue>),
155
156 SharedAtom(Arc<SharedAtom>),
160 ByteBlob(Arc<[u8]>),
162}
163
164const _: () = {
166 const fn _assert_send<T: Send + Sync>() {}
167 let _ = _assert_send::<SerializedValue>;
168};
169
170impl SerializedValue {
171 pub fn byte_size(&self) -> usize {
179 const NODE: usize = std::mem::size_of::<SerializedValue>();
182
183 let payload = match self {
184 SerializedValue::Nil
185 | SerializedValue::Bool(_)
186 | SerializedValue::Long(_)
187 | SerializedValue::Double(_)
188 | SerializedValue::Char(_)
189 | SerializedValue::Uuid(_) => 0,
190
191 SerializedValue::BigInt(b) => (b.bits() as usize / 8) + 1,
192 SerializedValue::Ratio(r) => {
193 (r.numer().bits() as usize + r.denom().bits() as usize) / 8 + 2
194 }
195 SerializedValue::BigDecimal(_) => 16,
196
197 SerializedValue::Str(s) => s.len(),
198 SerializedValue::Pattern(s) => s.len(),
199
200 SerializedValue::Symbol {
201 namespace, name, ..
202 } => namespace.as_ref().map_or(0, |n| n.len()) + name.len(),
203 SerializedValue::Keyword { namespace, name } => {
204 namespace.as_ref().map_or(0, |n| n.len()) + name.len()
205 }
206
207 SerializedValue::List(items)
208 | SerializedValue::Vector(items)
209 | SerializedValue::HashSet(items)
210 | SerializedValue::SortedSet(items)
211 | SerializedValue::Queue(items)
212 | SerializedValue::ObjectArray(items) => {
213 items.iter().map(SerializedValue::byte_size).sum()
214 }
215
216 SerializedValue::ArrayMap(pairs)
217 | SerializedValue::HashMap(pairs)
218 | SerializedValue::SortedMap(pairs) => pairs
219 .iter()
220 .map(|(k, v)| k.byte_size() + v.byte_size())
221 .sum(),
222
223 SerializedValue::TypeInstance { fields, .. } => fields
224 .iter()
225 .map(|(k, v)| k.byte_size() + v.byte_size())
226 .sum(),
227
228 SerializedValue::Cons { head, tail } => head.byte_size() + tail.byte_size(),
229
230 SerializedValue::Error(e) => e.byte_size(),
231
232 SerializedValue::BooleanArray(v) => v.len(),
233 SerializedValue::ByteArray(v) => v.len(),
234 SerializedValue::ShortArray(v) => std::mem::size_of_val(v.as_slice()),
235 SerializedValue::IntArray(v) => std::mem::size_of_val(v.as_slice()),
236 SerializedValue::LongArray(v) => std::mem::size_of_val(v.as_slice()),
237 SerializedValue::FloatArray(v) => std::mem::size_of_val(v.as_slice()),
238 SerializedValue::DoubleArray(v) => std::mem::size_of_val(v.as_slice()),
239 SerializedValue::CharArray(v) => std::mem::size_of_val(v.as_slice()),
240
241 SerializedValue::WithMeta { value, meta } => value.byte_size() + meta.byte_size(),
242 SerializedValue::Reduced(inner) => inner.byte_size(),
243
244 SerializedValue::SharedAtom(_) | SerializedValue::ByteBlob(_) => 0,
246 };
247
248 NODE + payload
249 }
250}
251
252impl SerializedError {
253 fn byte_size(&self) -> usize {
254 std::mem::size_of::<SerializedError>()
255 + self.message.len()
256 + self.data.as_ref().map_or(0, |pairs| {
257 pairs
258 .iter()
259 .map(|(k, v)| k.byte_size() + v.byte_size())
260 .sum()
261 })
262 + self.cause.as_ref().map_or(0, |c| c.byte_size())
263 }
264}
265
266#[derive(Clone, Debug)]
268pub struct SerializedError {
269 pub kind: SerializedErrorKind,
270 pub message: String,
271 pub data: Option<Vec<(SerializedValue, SerializedValue)>>,
272 pub cause: Option<Box<SerializedError>>,
273}
274
275#[derive(Clone, Debug)]
277pub enum SerializedErrorKind {
278 WrongType {
279 expected: &'static str,
280 got: String,
281 },
282 IndexOutOfBounds {
283 idx: usize,
284 count: usize,
285 },
286 ArityError {
287 name: String,
288 expected: String,
289 got: usize,
290 },
291 NotCallable {
292 value: String,
293 },
294 OddMap {
295 count: usize,
296 },
297 Unsupported,
298 Other(String),
299 OutOfRange,
300 TransientAlreadyPersisted,
301 Parse,
302 Thrown(Box<SerializedValue>),
303}
304
305pub fn serialize(v: &Value) -> Result<SerializedValue, CloneError> {
310 match v {
311 Value::WithMeta(inner, meta) => Ok(SerializedValue::WithMeta {
313 value: Box::new(serialize(inner)?),
314 meta: Box::new(serialize(meta)?),
315 }),
316 Value::Reduced(inner) => Ok(SerializedValue::Reduced(Box::new(serialize(inner)?))),
317
318 Value::Nil => Ok(SerializedValue::Nil),
320 Value::Bool(b) => Ok(SerializedValue::Bool(*b)),
321 Value::Long(n) => Ok(SerializedValue::Long(*n)),
322 Value::Double(d) => Ok(SerializedValue::Double(*d)),
323 Value::Char(c) => Ok(SerializedValue::Char(*c)),
324 Value::Uuid(u) => Ok(SerializedValue::Uuid(*u)),
325
326 Value::BigInt(p) => Ok(SerializedValue::BigInt(p.get().clone())),
327 Value::BigDecimal(p) => Ok(SerializedValue::BigDecimal(p.get().clone())),
328 Value::Ratio(p) => Ok(SerializedValue::Ratio(p.get().clone())),
329 Value::Str(p) => Ok(SerializedValue::Str(p.get().clone())),
330 Value::Pattern(p) => Ok(SerializedValue::Pattern(p.get().as_str().to_owned())),
331
332 Value::Symbol(p) => {
334 let s = p.get();
335 Ok(SerializedValue::Symbol {
336 namespace: s.namespace.clone(),
337 name: s.name.clone(),
338 version: s.version.clone(),
339 })
340 }
341 Value::Keyword(p) => {
342 let k = p.get();
343 Ok(SerializedValue::Keyword {
344 namespace: k.namespace.clone(),
345 name: k.name.clone(),
346 })
347 }
348
349 Value::List(p) => {
351 let items: Result<Vec<_>, _> = p.get().iter().map(serialize).collect();
352 Ok(SerializedValue::List(items?))
353 }
354 Value::Vector(p) => {
355 let items: Result<Vec<_>, _> = p.get().iter().map(serialize).collect();
356 Ok(SerializedValue::Vector(items?))
357 }
358 Value::Map(m) => serialize_map(m),
359 Value::Set(s) => serialize_set(s),
360 Value::Queue(p) => {
361 let items: Result<Vec<_>, _> = p.get().iter().map(serialize).collect();
362 Ok(SerializedValue::Queue(items?))
363 }
364 Value::Cons(p) => {
365 let c = p.get();
366 Ok(SerializedValue::Cons {
367 head: Box::new(serialize(&c.head)?),
368 tail: Box::new(serialize(&c.tail)?),
369 })
370 }
371
372 Value::TypeInstance(p) => {
374 let ti = p.get();
375 let fields = serialize_map_pairs(&ti.fields)?;
376 Ok(SerializedValue::TypeInstance {
377 type_tag: ti.type_tag.clone(),
378 fields,
379 })
380 }
381
382 Value::Error(p) => Ok(SerializedValue::Error(Box::new(serialize_error(p.get())?))),
384
385 Value::BooleanArray(p) => Ok(SerializedValue::BooleanArray(
387 p.get().lock().unwrap().clone(),
388 )),
389 Value::ByteArray(p) => Ok(SerializedValue::ByteArray(p.get().lock().unwrap().clone())),
390 Value::ShortArray(p) => Ok(SerializedValue::ShortArray(p.get().lock().unwrap().clone())),
391 Value::IntArray(p) => Ok(SerializedValue::IntArray(p.get().lock().unwrap().clone())),
392 Value::LongArray(p) => Ok(SerializedValue::LongArray(p.get().lock().unwrap().clone())),
393 Value::FloatArray(p) => Ok(SerializedValue::FloatArray(p.get().lock().unwrap().clone())),
394 Value::DoubleArray(p) => Ok(SerializedValue::DoubleArray(
395 p.get().lock().unwrap().clone(),
396 )),
397 Value::CharArray(p) => Ok(SerializedValue::CharArray(p.get().lock().unwrap().clone())),
398 Value::ObjectArray(p) => {
399 let guard = p.get().0.lock().unwrap();
400 let items: Result<Vec<_>, _> = guard.iter().map(serialize).collect();
401 Ok(SerializedValue::ObjectArray(items?))
402 }
403
404 Value::LazySeq(p) => serialize(&p.get().realize()),
406
407 Value::Delay(p) => {
409 let state = p.get().state.lock().unwrap();
410 if let DelayState::Forced(v) = &*state {
411 serialize(v)
412 } else {
413 Err(not_shareable("delay"))
414 }
415 }
416
417 Value::Resource(_) => Err(not_shareable("resource")),
419 Value::NativeObject(_) => Err(not_shareable("native-object")),
420 Value::Matcher(_) => Err(not_shareable("matcher")),
421
422 Value::Fn(_) | Value::Macro(_) => Err(not_shareable("fn")),
423 Value::BoundFn(_) => Err(not_shareable("fn")),
424 Value::NativeFunction(_) => Err(not_shareable("fn")),
425 Value::ProtocolFn(_) => Err(not_shareable("fn")),
426 Value::MultiFn(_) => Err(not_shareable("fn")),
427
428 Value::Var(_) => Err(not_shareable("var")),
429 Value::Atom(_) => Err(not_shareable("atom")),
430 Value::Volatile(_) => Err(not_shareable("volatile")),
431 Value::Promise(_) => Err(not_shareable("promise")),
432 Value::Future(_) => Err(not_shareable("future")),
433 Value::Agent(_) => Err(not_shareable("agent")),
434
435 Value::Namespace(_) => Err(not_shareable("namespace")),
436 Value::Protocol(_) => Err(not_shareable("protocol")),
437
438 Value::TransientMap(_) => Err(not_shareable("transient-map")),
439 Value::TransientSet(_) => Err(not_shareable("transient-set")),
440 Value::TransientVector(_) => Err(not_shareable("transient-vector")),
441
442 Value::SharedAtom(a) => Ok(SerializedValue::SharedAtom(a.clone())),
444 Value::ByteBlob(b) => Ok(SerializedValue::ByteBlob(b.clone())),
445 }
446}
447
448fn serialize_map(m: &MapValue) -> Result<SerializedValue, CloneError> {
449 let pairs = serialize_map_pairs(m)?;
450 Ok(match m {
451 MapValue::Array(_) => SerializedValue::ArrayMap(pairs),
452 MapValue::Hash(_) => SerializedValue::HashMap(pairs),
453 MapValue::Sorted(_) => SerializedValue::SortedMap(pairs),
454 })
455}
456
457fn serialize_map_pairs(
458 m: &MapValue,
459) -> Result<Vec<(SerializedValue, SerializedValue)>, CloneError> {
460 let mut pairs = Vec::with_capacity(m.count());
461 let mut err: Option<CloneError> = None;
462 m.for_each(|k, v| {
463 if err.is_some() {
464 return;
465 }
466 match (serialize(k), serialize(v)) {
467 (Ok(sk), Ok(sv)) => pairs.push((sk, sv)),
468 (Err(e), _) | (_, Err(e)) => err = Some(e),
469 }
470 });
471 if let Some(e) = err { Err(e) } else { Ok(pairs) }
472}
473
474fn serialize_set(s: &SetValue) -> Result<SerializedValue, CloneError> {
475 let items: Result<Vec<_>, _> = s.iter().map(serialize).collect();
476 Ok(match s {
477 SetValue::Hash(_) => SerializedValue::HashSet(items?),
478 SetValue::Sorted(_) => SerializedValue::SortedSet(items?),
479 })
480}
481
482fn serialize_error(e: &crate::error::ExceptionInfo) -> Result<SerializedError, CloneError> {
483 let kind = match &e.error {
484 ValueError::WrongType { expected, got } => SerializedErrorKind::WrongType {
485 expected,
486 got: got.clone(),
487 },
488 ValueError::IndexOutOfBounds { idx, count } => SerializedErrorKind::IndexOutOfBounds {
489 idx: *idx,
490 count: *count,
491 },
492 ValueError::ArityError {
493 name,
494 expected,
495 got,
496 } => SerializedErrorKind::ArityError {
497 name: name.clone(),
498 expected: expected.clone(),
499 got: *got,
500 },
501 ValueError::NotCallable { value } => SerializedErrorKind::NotCallable {
502 value: value.clone(),
503 },
504 ValueError::OddMap { count } => SerializedErrorKind::OddMap { count: *count },
505 ValueError::Unsupported => SerializedErrorKind::Unsupported,
506 ValueError::Other(s) => SerializedErrorKind::Other(s.clone()),
507 ValueError::OutOfRange => SerializedErrorKind::OutOfRange,
508 ValueError::TransientAlreadyPersisted => SerializedErrorKind::TransientAlreadyPersisted,
509 ValueError::Parse => SerializedErrorKind::Parse,
510 ValueError::Thrown(v) => SerializedErrorKind::Thrown(Box::new(serialize(v)?)),
511 };
512
513 let data = e.data.as_ref().map(serialize_map_pairs).transpose()?;
514
515 let cause = e
516 .cause
517 .as_ref()
518 .map(|c| serialize_error(c.get()).map(Box::new))
519 .transpose()?;
520
521 Ok(SerializedError {
522 kind,
523 message: e.message.clone(),
524 data,
525 cause,
526 })
527}
528
529pub fn deserialize(sv: SerializedValue) -> Value {
535 match sv {
536 SerializedValue::WithMeta { value, meta } => {
537 Value::WithMeta(Box::new(deserialize(*value)), Box::new(deserialize(*meta)))
538 }
539 SerializedValue::Reduced(inner) => Value::Reduced(Box::new(deserialize(*inner))),
540
541 SerializedValue::Nil => Value::Nil,
542 SerializedValue::Bool(b) => Value::Bool(b),
543 SerializedValue::Long(n) => Value::Long(n),
544 SerializedValue::Double(d) => Value::Double(d),
545 SerializedValue::Char(c) => Value::Char(c),
546 SerializedValue::Uuid(u) => Value::Uuid(u),
547
548 SerializedValue::BigInt(n) => Value::BigInt(GcPtr::new(n)),
549 SerializedValue::BigDecimal(d) => Value::BigDecimal(GcPtr::new(d)),
550 SerializedValue::Ratio(r) => Value::Ratio(GcPtr::new(r)),
551 SerializedValue::Str(s) => Value::Str(GcPtr::new(s)),
552 SerializedValue::Pattern(src) => Value::Pattern(GcPtr::new(
553 regex::Regex::new(&src).expect("pattern was valid at serialize time"),
554 )),
555
556 SerializedValue::Symbol {
557 namespace,
558 name,
559 version,
560 } => Value::Symbol(GcPtr::new(Symbol {
561 namespace,
562 name,
563 version,
564 })),
565 SerializedValue::Keyword { namespace, name } => {
566 Value::Keyword(GcPtr::new(Keyword { namespace, name }))
567 }
568
569 SerializedValue::List(items) => Value::List(GcPtr::new(PersistentList::from_iter(
570 items.into_iter().map(deserialize),
571 ))),
572 SerializedValue::Vector(items) => Value::Vector(GcPtr::new(PersistentVector::from_iter(
573 items.into_iter().map(deserialize),
574 ))),
575 SerializedValue::ArrayMap(pairs) => Value::Map(MapValue::from_pairs(
576 pairs
577 .into_iter()
578 .map(|(k, v)| (deserialize(k), deserialize(v)))
579 .collect(),
580 )),
581 SerializedValue::HashMap(pairs) => Value::Map(MapValue::from_pairs(
582 pairs
583 .into_iter()
584 .map(|(k, v)| (deserialize(k), deserialize(v)))
585 .collect(),
586 )),
587 SerializedValue::SortedMap(pairs) => {
588 let items: Vec<(Value, Value)> = pairs
590 .into_iter()
591 .map(|(k, v)| (deserialize(k), deserialize(v)))
592 .collect();
593 let sm = SortedMap::from_pairs(items);
594 Value::Map(MapValue::Sorted(GcPtr::new(sm)))
595 }
596 SerializedValue::HashSet(items) => {
597 let mut hs = PersistentHashSet::empty();
598 for item in items.into_iter().map(deserialize) {
599 hs = hs.conj(item);
600 }
601 Value::Set(SetValue::Hash(GcPtr::new(hs)))
602 }
603 SerializedValue::SortedSet(items) => {
604 let mut ss = SortedSet::empty();
605 for item in items.into_iter().map(deserialize) {
606 ss = ss.conj(item);
607 }
608 Value::Set(SetValue::Sorted(GcPtr::new(ss)))
609 }
610 SerializedValue::Queue(items) => {
611 let mut q = PersistentQueue::empty();
612 for item in items.into_iter().map(deserialize) {
613 q = q.conj(item);
614 }
615 Value::Queue(GcPtr::new(q))
616 }
617 SerializedValue::Cons { head, tail } => {
618 use crate::types::CljxCons;
619 Value::Cons(GcPtr::new(CljxCons {
620 head: deserialize(*head),
621 tail: deserialize(*tail),
622 }))
623 }
624
625 SerializedValue::TypeInstance { type_tag, fields } => {
626 use crate::value::TypeInstance;
627 let pairs: Vec<(Value, Value)> = fields
628 .into_iter()
629 .map(|(k, v)| (deserialize(k), deserialize(v)))
630 .collect();
631 Value::TypeInstance(GcPtr::new(TypeInstance {
632 type_tag,
633 fields: MapValue::from_pairs(pairs),
634 }))
635 }
636
637 SerializedValue::Error(se) => Value::Error(GcPtr::new(deserialize_error(*se))),
638
639 SerializedValue::BooleanArray(v) => {
640 Value::BooleanArray(GcPtr::new(std::sync::Mutex::new(v)))
641 }
642 SerializedValue::ByteArray(v) => Value::ByteArray(GcPtr::new(std::sync::Mutex::new(v))),
643 SerializedValue::ShortArray(v) => Value::ShortArray(GcPtr::new(std::sync::Mutex::new(v))),
644 SerializedValue::IntArray(v) => Value::IntArray(GcPtr::new(std::sync::Mutex::new(v))),
645 SerializedValue::LongArray(v) => Value::LongArray(GcPtr::new(std::sync::Mutex::new(v))),
646 SerializedValue::FloatArray(v) => Value::FloatArray(GcPtr::new(std::sync::Mutex::new(v))),
647 SerializedValue::DoubleArray(v) => Value::DoubleArray(GcPtr::new(std::sync::Mutex::new(v))),
648 SerializedValue::CharArray(v) => Value::CharArray(GcPtr::new(std::sync::Mutex::new(v))),
649 SerializedValue::ObjectArray(items) => {
650 use crate::value::ObjectArray;
651 Value::ObjectArray(GcPtr::new(ObjectArray::new(
652 items.into_iter().map(deserialize).collect(),
653 )))
654 }
655
656 SerializedValue::SharedAtom(a) => Value::SharedAtom(a),
658 SerializedValue::ByteBlob(b) => Value::ByteBlob(b),
659 }
660}
661
662fn deserialize_error(se: SerializedError) -> crate::error::ExceptionInfo {
663 let error = match se.kind {
664 SerializedErrorKind::WrongType { expected, got } => ValueError::WrongType { expected, got },
665 SerializedErrorKind::IndexOutOfBounds { idx, count } => {
666 ValueError::IndexOutOfBounds { idx, count }
667 }
668 SerializedErrorKind::ArityError {
669 name,
670 expected,
671 got,
672 } => ValueError::ArityError {
673 name,
674 expected,
675 got,
676 },
677 SerializedErrorKind::NotCallable { value } => ValueError::NotCallable { value },
678 SerializedErrorKind::OddMap { count } => ValueError::OddMap { count },
679 SerializedErrorKind::Unsupported => ValueError::Unsupported,
680 SerializedErrorKind::Other(s) => ValueError::Other(s),
681 SerializedErrorKind::OutOfRange => ValueError::OutOfRange,
682 SerializedErrorKind::TransientAlreadyPersisted => ValueError::TransientAlreadyPersisted,
683 SerializedErrorKind::Parse => ValueError::Parse,
684 SerializedErrorKind::Thrown(sv) => ValueError::Thrown(deserialize(*sv)),
685 };
686
687 let data = se.data.map(|pairs| {
688 MapValue::from_pairs(
689 pairs
690 .into_iter()
691 .map(|(k, v)| (deserialize(k), deserialize(v)))
692 .collect(),
693 )
694 });
695
696 let cause = se.cause.map(|c| GcPtr::new(deserialize_error(*c)));
697
698 crate::error::ExceptionInfo::new(error, se.message, data, cause)
699}
700
701#[cfg(test)]
704mod tests {
705 use super::*;
706
707 fn roundtrip(v: &Value) -> Value {
708 deserialize(serialize(v).expect("serialize"))
709 }
710
711 #[test]
712 fn scalars_roundtrip() {
713 assert_eq!(roundtrip(&Value::Nil), Value::Nil);
714 assert_eq!(roundtrip(&Value::Bool(true)), Value::Bool(true));
715 assert_eq!(roundtrip(&Value::Long(42)), Value::Long(42));
716 assert_eq!(roundtrip(&Value::Char('x')), Value::Char('x'));
717 assert_eq!(roundtrip(&Value::Uuid(12345)), Value::Uuid(12345));
718 }
719
720 #[test]
721 fn string_roundtrip() {
722 let v = Value::string("hello, world");
723 assert_eq!(roundtrip(&v), v);
724 }
725
726 #[test]
727 fn keyword_roundtrip() {
728 let v = Value::keyword(Keyword::simple("foo"));
729 assert_eq!(roundtrip(&v), v);
730 let v2 = Value::keyword(Keyword::qualified("clojure.core", "map"));
731 assert_eq!(roundtrip(&v2), v2);
732 }
733
734 #[test]
735 fn symbol_roundtrip() {
736 let v = Value::symbol(Symbol::simple("my-fn"));
737 assert_eq!(roundtrip(&v), v);
738 }
739
740 #[test]
741 fn list_roundtrip() {
742 let v = Value::List(GcPtr::new(PersistentList::from_iter([
743 Value::Long(1),
744 Value::Long(2),
745 Value::Long(3),
746 ])));
747 assert_eq!(roundtrip(&v), v);
748 }
749
750 #[test]
751 fn vector_roundtrip() {
752 let v = Value::Vector(GcPtr::new(PersistentVector::from_iter([
753 Value::string("a"),
754 Value::Bool(false),
755 Value::Nil,
756 ])));
757 assert_eq!(roundtrip(&v), v);
758 }
759
760 #[test]
761 fn nested_map_roundtrip() {
762 let inner = Value::Vector(GcPtr::new(PersistentVector::from_iter([Value::Long(1)])));
763 let v = MapValue::from_pairs(vec![(Value::keyword(Keyword::simple("k")), inner)]);
764 let v = Value::Map(v);
765 assert_eq!(roundtrip(&v), v);
766 }
767
768 #[test]
769 fn resource_not_shareable() {
770 use crate::resource::ResourceHandle;
771 use std::any::Any;
772 use std::sync::Arc;
773 #[derive(Debug)]
774 struct FakeResource;
775 impl crate::resource::Resource for FakeResource {
776 fn resource_type(&self) -> &'static str {
777 "fake"
778 }
779 fn close(&self) -> crate::error::ValueResult<()> {
780 Ok(())
781 }
782 fn is_closed(&self) -> bool {
783 false
784 }
785 fn as_any(&self) -> &dyn Any {
786 self
787 }
788 }
789 let r = Value::Resource(ResourceHandle(Arc::new(FakeResource)));
790 assert!(matches!(
791 serialize(&r),
792 Err(CloneError::NotShareable {
793 type_name: "resource"
794 })
795 ));
796 }
797
798 #[test]
799 fn atom_not_shareable() {
800 use crate::types::Atom;
801 let a = Value::Atom(GcPtr::new(Atom::new(Value::Nil)));
802 assert!(matches!(
803 serialize(&a),
804 Err(CloneError::NotShareable { type_name: "atom" })
805 ));
806 }
807
808 #[test]
809 fn fn_not_shareable() {
810 use crate::types::{Arity, NativeFn};
811 let nf = Value::NativeFunction(GcPtr::new(NativeFn::new("test", Arity::Fixed(0), |_| {
812 Ok(Value::Nil)
813 })));
814 assert!(matches!(
815 serialize(&nf),
816 Err(CloneError::NotShareable { type_name: "fn" })
817 ));
818 }
819
820 #[test]
821 fn lazy_seq_realized_roundtrip() {
822 use crate::types::{LazySeq, Thunk};
823 struct DoneThunk;
824 impl std::fmt::Debug for DoneThunk {
825 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
826 write!(f, "DoneThunk")
827 }
828 }
829 impl cljrs_gc::Trace for DoneThunk {
830 fn trace(&self, _: &mut cljrs_gc::MarkVisitor) {}
831 }
832 impl Thunk for DoneThunk {
833 fn force(&self) -> Result<Value, String> {
834 Ok(Value::Long(99))
835 }
836 }
837 let ls = LazySeq::new(Box::new(DoneThunk));
838 let _ = ls.realize(); let v = Value::LazySeq(GcPtr::new(ls));
840 assert_eq!(roundtrip(&v), Value::Long(99));
841 }
842
843 #[test]
844 fn with_meta_roundtrip() {
845 let v = Value::Long(7).with_meta(Value::Map(MapValue::empty()));
846 let rt = roundtrip(&v);
847 assert_eq!(rt, Value::Long(7));
849 }
850
851 #[test]
852 fn reduced_roundtrip() {
853 let v = Value::Reduced(Box::new(Value::Long(55)));
854 assert_eq!(roundtrip(&v), Value::Reduced(Box::new(Value::Long(55))));
855 }
856
857 #[test]
858 fn byte_size_counts_string_payload() {
859 let small = serialize(&Value::string("hi")).unwrap();
860 let large = serialize(&Value::string(&"x".repeat(1000))).unwrap();
861 assert!(large.byte_size() > small.byte_size() + 900);
863 }
864
865 #[test]
866 fn byte_size_grows_with_collection() {
867 let small = serialize(&Value::Vector(GcPtr::new(PersistentVector::from_iter([
868 Value::Long(1),
869 ]))))
870 .unwrap();
871 let large = serialize(&Value::Vector(GcPtr::new(PersistentVector::from_iter(
872 (0..100).map(Value::Long),
873 ))))
874 .unwrap();
875 assert!(large.byte_size() > small.byte_size());
876 }
877
878 #[test]
879 fn byte_size_scalar_is_node_sized() {
880 let node = std::mem::size_of::<SerializedValue>();
882 assert_eq!(serialize(&Value::Long(7)).unwrap().byte_size(), node);
883 assert_eq!(serialize(&Value::Nil).unwrap().byte_size(), node);
884 }
885}