1use crate::{Arena, EpochID, TermError};
7use core::fmt;
8use smartstring::alias::String;
9use std::borrow::Cow;
10
11#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
20pub(crate) struct TinyArray {
21 pub(crate) bytes: [u8; 14],
22 pub(crate) len: u8,
23}
24
25#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
26pub(crate) struct Slice {
27 pub(crate) epoch_id: EpochID,
28 pub(crate) index: u32,
29 pub(crate) len: u32,
30}
31
32#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
39#[repr(u8)]
40pub(crate) enum Handle {
41 Int(i64),
42 Real(f64),
43 Date(i64),
44 Var(TinyArray),
45 VarRef(Slice),
46 Atom(TinyArray),
47 AtomRef(Slice),
48 Str(TinyArray),
49 StrRef(Slice),
50 Bin(TinyArray),
51 BinRef(Slice),
52 FuncRef(Slice),
53 ListRef(Slice),
54 ListCRef(Slice),
55 TupleRef(Slice),
56}
57
58#[derive(Copy, Clone, PartialEq, PartialOrd)]
73pub struct Term(pub(crate) Handle);
74
75impl AsRef<Term> for Term {
76 fn as_ref(&self) -> &Self {
77 self
78 }
79}
80
81macro_rules! impl_from_integers_for_term {
82 ($($t:ty),* $(,)?) => {$(
83 impl From<$t> for Term {
84 #[inline]
85 fn from(v: $t) -> Self { Term::int(v as i64) }
86 }
87 )*};
88}
89impl_from_integers_for_term!(i8, i16, i32, i64, u8, u16, u32);
90
91macro_rules! impl_from_floats_for_term {
92 ($($t:ty),* $(,)?) => {$(
93 impl From<$t> for Term {
94 #[inline]
95 fn from(v: $t) -> Self { Term::real(v as f64) }
96 }
97 )*};
98}
99impl_from_floats_for_term!(f32, f64);
100
101pub trait IntoTerm {
102 fn into_term(self, arena: &mut Arena) -> Term;
103}
104
105macro_rules! impl_intoterm_for_integers {
106 ($($t:ty),* $(,)?) => {$(
107 impl IntoTerm for $t {
108 #[inline]
109 fn into_term(self, _arena: &mut Arena) -> Term { Term::int(self as i64) }
110 }
111 )*};
112}
113impl_intoterm_for_integers!(i8, i16, i32, i64, u8, u16, u32);
114
115macro_rules! impl_intoterm_for_floats {
116 ($($t:ty),* $(,)?) => {$(
117 impl IntoTerm for $t {
118 #[inline]
119 fn into_term(self, _arena: &mut Arena) -> Term { Term::real(self as f64) }
120 }
121 )*};
122}
123impl_intoterm_for_floats!(f32, f64);
124
125impl<'a> IntoTerm for &'a str {
126 #[inline]
127 fn into_term(self, arena: &mut Arena) -> Term {
128 Term::str(arena, self)
129 }
130}
131
132impl<'a> IntoTerm for &'a [u8] {
133 #[inline]
134 fn into_term(self, arena: &mut Arena) -> Term {
135 Term::bin(arena, self)
136 }
137}
138
139impl<'a> IntoTerm for Cow<'a, str> {
140 #[inline]
141 fn into_term(self, arena: &mut Arena) -> Term {
142 match self {
143 Cow::Borrowed(s) => Term::str(arena, s),
144 Cow::Owned(s) => Term::str(arena, s),
145 }
146 }
147}
148
149impl<'a> IntoTerm for Cow<'a, [u8]> {
150 #[inline]
151 fn into_term(self, arena: &mut Arena) -> Term {
152 match self {
153 Cow::Borrowed(s) => Term::bin(arena, s),
154 Cow::Owned(s) => Term::bin(arena, s),
155 }
156 }
157}
158
159impl IntoTerm for String {
160 #[inline]
161 fn into_term(self, arena: &mut Arena) -> Term {
162 Term::str(arena, &self)
163 }
164}
165
166impl IntoTerm for std::string::String {
167 #[inline]
168 fn into_term(self, arena: &mut Arena) -> Term {
169 Term::str(arena, &self)
170 }
171}
172
173impl IntoTerm for Vec<u8> {
174 #[inline]
175 fn into_term(self, arena: &mut Arena) -> Term {
176 Term::bin(arena, &self)
177 }
178}
179
180impl IntoTerm for Term {
181 #[inline]
182 fn into_term(self, _arena: &mut Arena) -> Term {
183 self
184 }
185}
186
187impl IntoTerm for &Term {
188 #[inline]
189 fn into_term(self, _arena: &mut Arena) -> Term {
190 *self
191 }
192}
193
194impl<F> IntoTerm for F
195where
196 F: FnOnce(&mut Arena) -> Term,
197{
198 #[inline]
199 fn into_term(self, arena: &mut Arena) -> Term {
200 self(arena)
201 }
202}
203
204impl Term {
205 #[inline]
209 pub fn int(i: impl Into<i64>) -> Self {
210 Self(Handle::Int(i.into()))
211 }
212
213 #[inline]
216 pub fn real(f: impl Into<f64>) -> Self {
217 Self(Handle::Real(f.into()))
218 }
219
220 #[inline]
225 pub fn date(ms: impl Into<i64>) -> Self {
226 Self(Handle::Date(ms.into()))
227 }
228
229 #[inline]
234 pub fn atom(arena: &mut Arena, name: impl AsRef<str>) -> Self {
235 let name = name.as_ref();
236 let bytes = name.as_bytes();
237 if bytes.len() <= 14 {
238 let mut buf = [0u8; 14];
239 buf[..bytes.len()].copy_from_slice(bytes);
240 Self(Handle::Atom(TinyArray {
241 bytes: buf,
242 len: bytes.len() as u8,
243 }))
244 } else {
245 Self(Handle::AtomRef(arena.intern_str(name)))
246 }
247 }
248
249 #[inline]
254 pub fn var(arena: &mut Arena, name: impl AsRef<str>) -> Self {
255 let name = name.as_ref();
256 let bytes = name.as_bytes();
257 if bytes.len() <= 14 {
258 let mut buf = [0u8; 14];
259 buf[..bytes.len()].copy_from_slice(bytes);
260 Self(Handle::Var(TinyArray {
261 bytes: buf,
262 len: bytes.len() as u8,
263 }))
264 } else {
265 Self(Handle::VarRef(arena.intern_str(name)))
266 }
267 }
268
269 #[inline]
274 pub fn str(arena: &mut Arena, s: impl AsRef<str>) -> Self {
275 let s = s.as_ref();
276 let bytes = s.as_bytes();
277 if bytes.len() <= 14 {
278 let mut buf = [0u8; 14];
279 buf[..bytes.len()].copy_from_slice(bytes);
280 Self(Handle::Str(TinyArray {
281 bytes: buf,
282 len: bytes.len() as u8,
283 }))
284 } else {
285 Self(Handle::StrRef(arena.intern_str(s)))
286 }
287 }
288
289 #[inline]
293 pub fn bin(arena: &mut Arena, bytes: impl AsRef<[u8]>) -> Self {
294 let bytes = bytes.as_ref();
295 if bytes.len() <= 14 {
296 let mut buf = [0u8; 14];
297 buf[..bytes.len()].copy_from_slice(bytes);
298 Self(Handle::Bin(TinyArray {
299 bytes: buf,
300 len: bytes.len() as u8,
301 }))
302 } else {
303 Self(Handle::BinRef(arena.intern_bytes(bytes)))
304 }
305 }
306
307 #[inline]
313 pub fn func(
314 arena: &mut Arena,
315 functor: impl AsRef<str>,
316 args: impl IntoIterator<Item = impl IntoTerm>,
317 ) -> Self {
318 let functor_atom = Self::atom(arena, functor);
319 let mut args = args.into_iter();
320 let Some(first) = args.next() else {
321 return functor_atom;
322 };
323 Self(Handle::FuncRef(arena.intern_func(
324 functor_atom,
325 std::iter::once(first).chain(args),
326 )))
327 }
328
329 #[inline]
334 pub fn funcv(
335 arena: &mut Arena,
336 terms: impl IntoIterator<Item = impl IntoTerm>,
337 ) -> Result<Self, TermError> {
338 let mut terms = terms.into_iter();
339 let Some(functor_atom) = terms.next() else {
340 return Err(TermError::MissingFunctor);
341 };
342 let functor_atom = functor_atom.into_term(arena);
343 if !functor_atom.is_atom() {
344 return Err(TermError::InvalidFunctor(functor_atom));
345 }
346 let Some(first) = terms.next() else {
347 return Ok(functor_atom);
348 };
349 Ok(Self(Handle::FuncRef(arena.intern_func(
350 functor_atom,
351 std::iter::once(first).chain(terms),
352 ))))
353 }
354
355 #[inline]
358 pub fn list(arena: &mut Arena, terms: impl IntoIterator<Item = impl IntoTerm>) -> Self {
359 let mut terms = terms.into_iter();
360 let Some(first) = terms.next() else {
361 return Self::NIL;
362 };
363 Self(Handle::ListRef(
364 arena.intern_seq(std::iter::once(first).chain(terms)),
365 ))
366 }
367
368 #[inline]
371 pub fn listc(
372 arena: &mut Arena,
373 terms: impl IntoIterator<Item = impl IntoTerm>,
374 tail: impl IntoTerm,
375 ) -> Self {
376 let mut terms = terms.into_iter();
377 let Some(first) = terms.next() else {
378 return Self::NIL;
379 };
380 let tail = tail.into_term(arena);
381 if tail != Term::NIL {
382 Self(Handle::ListCRef(arena.intern_seq_plus_one(
383 std::iter::once(first).chain(terms),
384 tail,
385 )))
386 } else {
387 Self(Handle::ListRef(
388 arena.intern_seq(std::iter::once(first).chain(terms)),
389 ))
390 }
391 }
392
393 #[inline]
396 pub fn tuple(arena: &mut Arena, terms: impl IntoIterator<Item = impl IntoTerm>) -> Self {
397 let mut terms = terms.into_iter();
398 let Some(first) = terms.next() else {
399 return Self::UNIT;
400 };
401 Self(Handle::TupleRef(
402 arena.intern_seq(std::iter::once(first).chain(terms)),
403 ))
404 }
405
406 pub const UNIT: Self = {
410 let buf: [u8; 14] = [b'u', b'n', b'i', b't', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
411 Self(Handle::Atom(TinyArray { bytes: buf, len: 4 }))
412 };
413
414 pub const NIL: Self = {
418 let buf: [u8; 14] = [b'n', b'i', b'l', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
419 Self(Handle::Atom(TinyArray { bytes: buf, len: 3 }))
420 };
421
422 #[inline]
424 pub fn unpack_int(&self, arena: &Arena) -> Result<i64, TermError> {
425 arena.unpack_int(self)
426 }
427
428 #[inline]
430 pub fn unpack_real(&self, arena: &Arena) -> Result<f64, TermError> {
431 arena.unpack_real(self)
432 }
433
434 #[inline]
436 pub fn unpack_date(&self, arena: &Arena) -> Result<i64, TermError> {
437 arena.unpack_date(self)
438 }
439
440 #[inline]
442 pub fn unpack_str<'a>(&'a self, arena: &'a Arena) -> Result<&'a str, TermError> {
443 arena.unpack_str(self)
444 }
445
446 #[inline]
448 pub fn unpack_bin<'a>(&'a self, arena: &'a Arena) -> Result<&'a [u8], TermError> {
449 arena.unpack_bin(self)
450 }
451
452 #[inline]
454 pub fn unpack_atom<'a>(
455 &'a self,
456 arena: &'a Arena,
457 allowed_names: &[&str],
458 ) -> Result<&'a str, TermError> {
459 arena.unpack_atom(self, allowed_names)
460 }
461
462 #[inline]
464 pub fn unpack_var<'a>(
465 &'a self,
466 arena: &'a Arena,
467 allowed_names: &[&str],
468 ) -> Result<&'a str, TermError> {
469 arena.unpack_var(self, allowed_names)
470 }
471
472 #[inline]
476 pub fn unpack_func_any<'a>(
477 &'a self,
478 arena: &'a Arena,
479 allowed_names: &[&str],
480 ) -> Result<(&'a Term, &'a [Term]), TermError> {
481 arena.unpack_func_any(self, allowed_names)
482 }
483
484 #[inline]
488 pub fn unpack_func<'a, const ARITY: usize>(
489 &'a self,
490 arena: &'a Arena,
491 allowed_names: &[&str],
492 ) -> Result<(&'a Term, [Term; ARITY]), TermError> {
493 arena.unpack_func(self, allowed_names)
494 }
495
496 #[inline]
499 pub fn unpack_list<'a>(
500 &'a self,
501 arena: &'a Arena,
502 ) -> Result<(&'a [Term], &'a Term), TermError> {
503 arena.unpack_list(self)
504 }
505
506 #[inline]
509 pub fn unpack_tuple_any<'a>(&'a self, arena: &'a Arena) -> Result<&'a [Term], TermError> {
510 arena.unpack_tuple_any(self)
511 }
512
513 #[inline]
516 pub fn unpack_tuple<const ARITY: usize>(
517 &self,
518 arena: &Arena,
519 ) -> Result<[Term; ARITY], TermError> {
520 arena.unpack_tuple(self)
521 }
522
523 #[inline]
526 pub fn is_inline(&self) -> bool {
527 match &self.0 {
528 Handle::Int(_)
529 | Handle::Real(_)
530 | Handle::Date(_)
531 | Handle::Atom(_)
532 | Handle::Var(_)
533 | Handle::Str(_)
534 | Handle::Bin(_) => true,
535 Handle::AtomRef(_)
536 | Handle::VarRef(_)
537 | Handle::StrRef(_)
538 | Handle::BinRef(_)
539 | Handle::FuncRef(_)
540 | Handle::ListRef(_)
541 | Handle::ListCRef(_)
542 | Handle::TupleRef(_) => false,
543 }
544 }
545
546 #[inline]
548 pub fn is_func(&self) -> bool {
549 matches!(self.0, Handle::FuncRef(_))
550 }
551
552 #[inline]
554 pub fn is_list(&self) -> bool {
555 matches!(self.0, Handle::ListRef(_) | Handle::ListCRef(_)) || *self == Self::NIL
556 }
557
558 #[inline]
560 pub fn is_tuple(&self) -> bool {
561 matches!(self.0, Handle::TupleRef(_)) || *self == Self::UNIT
562 }
563
564 #[inline]
566 pub fn is_int(&self) -> bool {
567 matches!(self.0, Handle::Int(_))
568 }
569
570 #[inline]
572 pub fn is_real(&self) -> bool {
573 matches!(self.0, Handle::Real(_))
574 }
575
576 #[inline]
578 pub fn is_date(&self) -> bool {
579 matches!(self.0, Handle::Date(_))
580 }
581
582 #[inline]
584 pub fn is_atom(&self) -> bool {
585 matches!(self.0, Handle::Atom(_) | Handle::AtomRef(_))
586 }
587
588 #[inline]
590 pub fn is_var(&self) -> bool {
591 matches!(self.0, Handle::Var(_) | Handle::VarRef(_))
592 }
593
594 #[inline]
596 pub fn is_number(&self) -> bool {
597 matches!(self.0, Handle::Int(_) | Handle::Real(_) | Handle::Date(_))
598 }
599
600 #[inline]
602 pub fn is_str(&self) -> bool {
603 matches!(self.0, Handle::Str(_) | Handle::StrRef(_))
604 }
605
606 #[inline]
608 pub fn is_bin(&self) -> bool {
609 matches!(self.0, Handle::Bin(_) | Handle::BinRef(_))
610 }
611
612 #[inline]
614 pub fn arity(&self) -> usize {
615 match &self.0 {
616 Handle::Atom(_)
617 | Handle::AtomRef(_)
618 | Handle::Int(_)
619 | Handle::Real(_)
620 | Handle::Date(_)
621 | Handle::Str(_)
622 | Handle::StrRef(_)
623 | Handle::Bin(_)
624 | Handle::BinRef(_) => 0,
625 Handle::FuncRef(Slice { len: n, .. }) => (n - 1) as usize,
626 Handle::TupleRef(Slice { len: n, .. }) => *n as usize,
627 Handle::ListRef(_) | Handle::ListCRef(_) | Handle::Var(_) | Handle::VarRef(_) => 0,
628 }
629 }
630
631 #[inline]
635 pub fn name<'a>(&'a self, arena: &'a Arena) -> Result<&'a str, TermError> {
636 arena.name(self)
637 }
638
639 #[inline]
641 pub fn atom_name<'a>(&'a self, arena: &'a Arena) -> Result<&'a str, TermError> {
642 arena.unpack_atom(self, &[])
643 }
644
645 #[inline]
647 pub fn var_name<'a>(&'a self, arena: &'a Arena) -> Result<&'a str, TermError> {
648 arena.unpack_var(self, &[])
649 }
650
651 #[inline]
653 pub fn func_name<'a>(&'a self, arena: &'a Arena) -> Result<&'a str, TermError> {
654 let (functor, _) = arena.unpack_func_any(self, &[])?;
655 arena.atom_name(functor)
656 }
657
658 #[inline]
660 pub fn kind_name(&self) -> &'static str {
661 match &self.0 {
662 Handle::Int(_) => "int",
663 Handle::Real(_) => "real",
664 Handle::Date(_) => "date",
665 Handle::Var(_) | Handle::VarRef(_) => "var",
666 Handle::Atom(_) | Handle::AtomRef(_) => "atom",
667 Handle::Str(_) | Handle::StrRef(_) => "str",
668 Handle::Bin(_) | Handle::BinRef(_) => "bin",
669 Handle::FuncRef(_) => "func",
670 Handle::ListRef(_) | Handle::ListCRef(_) => "list",
671 Handle::TupleRef(_) => "tuple",
672 }
673 }
674}
675
676impl fmt::Debug for Term {
693 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
694 match &self.0 {
695 Handle::Int(i) => f.debug_tuple("Int").field(i).finish(),
696 Handle::Real(r) => f.debug_tuple("Real").field(r).finish(),
697 Handle::Date(d) => f.debug_tuple("Date").field(d).finish(),
698 Handle::Var(v) => {
699 let name =
700 core::str::from_utf8(&v.bytes[..v.len as usize]).unwrap_or("<invalid utf8>");
701 f.debug_struct("Var").field("name", &name).finish()
702 }
703 Handle::VarRef(v) => f
704 .debug_struct("VarRef")
705 .field("epoch_id", &v.epoch_id)
706 .field("index", &v.index)
707 .field("len", &v.len)
708 .finish(),
709 Handle::Atom(a) => {
710 let name =
711 core::str::from_utf8(&a.bytes[..a.len as usize]).unwrap_or("<invalid utf8>");
712 f.debug_struct("Atom").field("name", &name).finish()
713 }
714 Handle::AtomRef(v) => f
715 .debug_struct("AtomRef")
716 .field("epoch_id", &v.epoch_id)
717 .field("index", &v.index)
718 .field("len", &v.len)
719 .finish(),
720 Handle::Str(s) => {
721 let value =
722 core::str::from_utf8(&s.bytes[..s.len as usize]).unwrap_or("<invalid utf8>");
723 f.debug_struct("Str").field("value", &value).finish()
724 }
725 Handle::StrRef(v) => f
726 .debug_struct("StrRef")
727 .field("epoch_id", &v.epoch_id)
728 .field("index", &v.index)
729 .field("len", &v.len)
730 .finish(),
731 Handle::Bin(b) => {
732 let slice = &b.bytes[..b.len as usize];
733 f.debug_struct("Bin").field("bytes", &slice).finish()
734 }
735 Handle::BinRef(v) => f
736 .debug_struct("BinRef")
737 .field("epoch_id", &v.epoch_id)
738 .field("index", &v.index)
739 .field("len", &v.len)
740 .finish(),
741 Handle::FuncRef(v) => f
742 .debug_struct("Func")
743 .field("epoch_id", &v.epoch_id)
744 .field("index", &v.index)
745 .field("len", &v.len)
746 .finish(),
747 Handle::ListRef(v) => f
748 .debug_struct("List")
749 .field("epoch_id", &v.epoch_id)
750 .field("index", &v.index)
751 .field("len", &v.len)
752 .finish(),
753 Handle::ListCRef(v) => f
754 .debug_struct("ListC")
755 .field("epoch_id", &v.epoch_id)
756 .field("index", &v.index)
757 .field("len", &v.len)
758 .finish(),
759 Handle::TupleRef(v) => f
760 .debug_struct("Tuple")
761 .field("epoch_id", &v.epoch_id)
762 .field("index", &v.index)
763 .field("len", &v.len)
764 .finish(),
765 }
766 }
767}
768
769#[macro_export]
771macro_rules! list {
772 ($($arg:expr),* $(,)?; $tail:expr => $arena:expr) => {
774 $crate::list!($($arg),* ; $tail)($arena)
775 };
776 ($($arg:expr),* $(,)? => $arena:expr) => {
778 $crate::list!($($arg),*)($arena)
779 };
780 ($($arg:expr),* $(,)?; $tail:expr) => { (|__arena: &mut $crate::Arena| {
782 let __args: &[$crate::Term] = &[$($arg.into_term(__arena)),*];
783 let __tail: Term = $tail.into_term(__arena);
784 __arena.listc(__args, __tail)
785 })};
786 ($($arg:expr),* $(,)?) => { (|__arena: &mut $crate::Arena| {
788 let __args: &[$crate::Term] = &[$($arg.into_term(__arena)),*];
789 __arena.list(__args)
790 })};
791}
792
793#[macro_export]
794macro_rules! tuple {
795 ($($arg:expr),* $(,)? => $arena:expr) => {
797 $crate::tuple!($($arg),*)($arena)
798 };
799 ($($arg:expr),* $(,)?) => { (|__arena: &mut $crate::Arena| {
801 let __args: &[$crate::Term] = &[$($arg.into_term(__arena)),*];
802 __arena.tuple(__args)
803 })};
804}
805
806#[macro_export]
807macro_rules! func {
808 ($functor:expr; $($arg:expr),+ $(,)? => $arena:expr) => {
810 $crate::func!($functor; $($arg),+)($arena)
811 };
812 ($functor:expr; $($arg:expr),+ $(,)?) => { (|__arena: &mut $crate::Arena| {
814 let __args: &[$crate::Term] = &[$($arg.into_term(__arena)),+];
815 __arena.func($functor, __args)
816 })};
817}
818
819#[macro_export]
820macro_rules! atom {
821 ($functor:expr => $arena:expr) => {
823 $crate::atom!($functor)($arena)
824 };
825 ($functor:expr) => {
827 (|__arena: &mut $crate::Arena| __arena.atom($functor))
828 };
829}
830
831#[macro_export]
832macro_rules! var {
833 ($name:expr => $arena:expr) => {
835 $crate::var!($name)($arena)
836 };
837 ($name:expr) => {
839 (|__arena: &mut $crate::Arena| __arena.var($name))
840 };
841}
842
843#[macro_export]
844macro_rules! date {
845 ($value:expr) => {
846 $crate::Term::date($value)
847 };
848}
849
850#[macro_export]
851macro_rules! unit {
852 () => {
853 $crate::Term::UNIT
854 };
855}
856
857#[macro_export]
858macro_rules! nil {
859 () => {
860 $crate::Term::NIL
861 };
862}
863
864#[cfg(test)]
865mod tests {
866 use super::*;
867 use crate::View;
868 use std::fmt::Write;
869
870 #[test]
871 fn term_size_is_16_bytes() {
872 assert_eq!(core::mem::size_of::<Term>(), 16);
873 }
874
875 #[test]
876 fn option_term_size_is_16_bytes() {
877 assert_eq!(core::mem::size_of::<Option<Term>>(), 16);
878 }
879
880 #[test]
881 fn small_atom_interning() {
882 let mut arena = Arena::new();
883 let a1 = Term::atom(&mut arena, "foo");
884 let a2 = Term::atom(&mut arena, "foo");
885 assert_eq!(a1, a2);
886 if let Ok(View::Atom(name)) = a1.view(&arena) {
887 assert_eq!(name, "foo");
888 } else {
889 panic!("wrong view");
890 }
891 }
892
893 #[test]
894 fn compound_construction_and_formatting() {
895 let mut arena = Arena::new();
896 let a = Term::int(1);
897 let b = Term::real(2.0);
898 let c = Term::date(1000);
899 let d = Term::atom(&mut arena, "hello");
900 let e = Term::var(&mut arena, "Hello");
901 let f = Term::str(&mut arena, "A str\ning. Longer string.");
902 let g = list![d, e, f => &mut arena];
903 let h = tuple!(f, f => &mut arena);
904 let p = Term::func(&mut arena, "point", &[a, b, c, d, e, f, g, h]);
905 let p = func![
906 "foo";
907 Term::NIL,
908 Term::UNIT,
909 p,
910 p,
911 list![],
912 list![a, b; c],
913 => &mut arena
914 ];
915 dbg!(&p);
916 dbg!(p.view(&arena).unwrap());
917 dbg!(arena.stats());
918 assert!(p.is_func());
919 if let Ok(View::Func(_, functor, args)) = p.view(&arena) {
920 assert_eq!(functor.atom_name(&arena).unwrap(), "foo");
921 assert_eq!(p.arity(), 6);
922 assert_eq!(args.len(), 6);
923 } else {
924 panic!("unexpected view");
925 }
926
927 let s = format!("{}", p.display(&arena));
928 assert_eq!(
929 s,
930 "foo(nil, unit, point(1, 2.0, date{1970-01-01T00:00:01+00:00}, hello, Hello, \"A str\\ning. Longer string.\", [hello, Hello, \"A str\\ning. Longer string.\"], (\"A str\\ning. Longer string.\", \"A str\\ning. Longer string.\")), point(1, 2.0, date{1970-01-01T00:00:01+00:00}, hello, Hello, \"A str\\ning. Longer string.\", [hello, Hello, \"A str\\ning. Longer string.\"], (\"A str\\ning. Longer string.\", \"A str\\ning. Longer string.\")), nil, [1, 2.0 | date{1970-01-01T00:00:01+00:00}])"
931 );
932 }
933
934 #[test]
935 fn view_construction() {
936 let mut a1 = Arena::new();
937 let x = a1.atom("Hello, hello, quite long long string, world! X");
938 dbg!(a1.view(&x).unwrap());
939 dbg!(a1.stats());
940 let p = list![x, x => &mut a1];
941 dbg!(p);
942 let v = a1.view(&p).unwrap();
943 dbg!(v);
944 }
945
946 #[test]
947 #[should_panic]
948 fn arena_mismatch() {
949 let a1 = Arena::new();
950 let mut a2 = Arena::new();
951 let y = a2.str("Hello, hello, quite long long string, world! Y");
952 dbg!(a1.view(&y).unwrap());
953 }
954
955 #[test]
956 #[should_panic]
957 fn stale_term_str() {
958 let mut a = Arena::new();
959 let x = a.str("Hello, hello, quite long long string, world! Y");
960 dbg!(&a);
961 a.truncate(a.current_epoch()).unwrap();
962 dbg!(a.view(&x).unwrap());
963 }
964
965 #[test]
966 #[should_panic]
967 fn stale_term_list() {
968 let mut a = Arena::new();
969 let _x = list![1, 2, 3 => &mut a];
970 let epoch = a.begin_epoch().unwrap();
971 dbg!(&epoch);
972 let y = list![4, 5, 6 => &mut a];
973 dbg!(&a);
974 a.truncate(epoch).unwrap();
975 dbg!(&a);
976 dbg!(a.view(&y).unwrap());
977 }
978
979 #[test]
980 fn big_term() {
981 let mut a1 = Arena::new();
982 let x = a1.atom("Hello, hello, quite long long string, world! X");
983 let p = a1.func("foo", vec![x; 1_000_000]);
984 assert!(p.arity() == 1_000_000);
985 dbg!(a1.stats());
986 }
987
988 #[test]
989 fn interface() {
990 let a = &mut Arena::new();
991 let s = String::from("x");
992 let x1 = a.func(&s, &vec![Term::date(1000)]);
993 let x2 = a.func(s.as_str(), vec![Term::date(1000)]);
994 let x3 = a.func(s, &[Term::date(1000)]);
995 let _x4 = a.func("x", [Term::date(1000)]);
996 let _x5 = a.func("x", [x1, x2, x3]);
997 let _x6 = a.func("x", (5..=6).map(|x| x as f64));
998 let _x7 = a.func("x", vec![&x1, &x2, &x3]);
999 let _x8 = a.func("x", &[x1, x2, x3]);
1000 let x9 = func!(
1001 String::from("aaa");
1002 x1, 1u8, 1i8, 2.0,
1003 "x",
1004 "X",
1005 atom!("ATOM"),
1006 var!("var"),
1007 "a string",
1008 b"a binary",
1009 1,
1010 2,
1011 3,
1012 4,
1013 6,
1014 unit!(),
1015 list![1, 2, 3; tuple!()],
1016 list![1, 2, 3; nil!()],
1017 => a
1018 );
1019 dbg!(a.view(&x9).unwrap());
1020 dbg!(a.stats());
1021 }
1022
1023 #[test]
1024 fn into_test() {
1025 let mut arena = Arena::new();
1026 let t1 = arena.term(1);
1028 let t2 = arena.term(2.0);
1029 let t3 = arena.term("x");
1030 let t4 = arena.term(b"bin" as &[u8]);
1031 let point1 = arena.func("point", [t1, t2, t3, t4]);
1032 let t1 = Term::int(1);
1034 let t2 = Term::real(2.0);
1035 let t3 = Term::str(&mut arena, "x");
1036 let t4 = Term::bin(&mut arena, b"bin");
1037 let point2 = arena.func("point", [t1, t2, t3, t4]);
1038 assert_eq!(arena.view(&point1).unwrap(), arena.view(&point2).unwrap());
1039 dbg!(arena.view(&point1).unwrap());
1040
1041 let lazy = Term::func(&mut arena, "lazy", [|arena: &mut Arena| arena.atom("ok")]);
1043 dbg!(arena.view(&lazy).unwrap());
1044
1045 let list = arena.list([1, 2, 3]);
1046 dbg!(arena.view(&list).unwrap());
1047 }
1048
1049 #[test]
1050 fn arena_truncate_test() {
1051 let a = &mut Arena::new();
1052
1053 let t1 = a.str("a".repeat(1000));
1054 let _t5 = atom!("x".repeat(100) => a);
1055 let _t6 = var!("X".repeat(200) => a);
1056 let _t7 = a.bin(b"x".repeat(5000));
1057 let epoch1 = a.begin_epoch().unwrap();
1058 dbg!(a.stats());
1059 dbg!(&epoch1);
1060 let t2 = a.str("b".repeat(2000));
1061 let t3 = a.bin(b"b".repeat(3000));
1062 let _t4 = list![t1, t2, t3];
1063 let _t5 = atom!("z".repeat(4000) => a);
1064 let _t8 = var!("Z".repeat(2000) => a);
1065 let _t7 = a.bin(b"z".repeat(10_000));
1066 let epoch2 = a.begin_epoch().unwrap();
1067 dbg!(a.stats());
1068 dbg!(&epoch2);
1069 a.truncate(epoch2).unwrap();
1070 dbg!(a.stats());
1071 }
1072
1073 #[test]
1074 fn funcv() {
1075 let a = &mut Arena::new();
1076 let xs = [a.atom("foo"), a.atom("x"), a.atom("y")];
1077 let x = a.funcv(xs).unwrap();
1078 let ys = [a.atom("x"), a.atom("y")];
1079 let y = a.func("foo", ys);
1080 assert_eq!(x.arity(), y.arity());
1081 if let Ok(View::Func(_, functor, args)) = x.view(&a) {
1082 assert_eq!(functor.name(a).unwrap(), "foo");
1083 assert_eq!(args.len(), 2);
1084 }
1085 if let Ok(View::Func(_, functor, args)) = y.view(&a) {
1086 assert_eq!(functor.name(a).unwrap(), "foo");
1087 assert_eq!(args.len(), 2);
1088 }
1089 }
1090
1091 #[test]
1092 fn unpack() {
1093 let a = &mut Arena::new();
1094 let xs = [a.atom("foo"), a.atom("x"), a.atom("y")];
1095 let x = a.funcv(xs).unwrap();
1096
1097 let (foo, [x, y]) = x.unpack_func(a, &["foo", "boo"]).unwrap();
1098 dbg!((foo, x, y));
1099
1100 let z = tuple!(1 => a);
1101 assert_eq!(z.arity(), 1);
1102 }
1103
1104 #[test]
1105 fn arity_primitives_and_lists_are_zero() {
1106 let a = &mut Arena::new();
1107
1108 let t_int = Term::int(42);
1109 let t_real = Term::real(3.14);
1110 let t_atom = Term::atom(a, "ok");
1111 let t_var = Term::var(a, "X");
1112 let t_str = Term::str(a, "hello");
1113 let t_bin = Term::bin(a, &[1, 2, 3, 4]);
1114 let t_list = Term::list(a, &[Term::int(1), Term::int(2), Term::int(3)]);
1115
1116 assert_eq!(t_int.arity(), 0);
1117 assert_eq!(t_real.arity(), 0);
1118 assert_eq!(t_atom.arity(), 0);
1119 assert_eq!(t_var.arity(), 0);
1120 assert_eq!(t_str.arity(), 0);
1121 assert_eq!(t_bin.arity(), 0);
1122 assert_eq!(t_list.arity(), 0); }
1124
1125 #[test]
1126 fn arity_for_tuples_and_funcs() {
1127 let a = &mut Arena::new();
1128
1129 let t2 = Term::tuple(a, &[Term::int(1), Term::int(2)]);
1130 let t3 = Term::tuple(a, &[Term::int(1), Term::int(2), Term::int(3)]);
1131 assert_eq!(t2.arity(), 2);
1132 assert_eq!(t3.arity(), 3);
1133
1134 let f0 = Term::func(a, "nilary", &[] as &[Term]); let f2 = Term::func(a, "pair", &[Term::int(1), Term::int(2)]);
1136 let f3 = Term::func(a, "triple", &[Term::int(1), Term::int(2), Term::int(3)]);
1137
1138 assert_eq!(f0.arity(), 0);
1139 assert_eq!(f2.arity(), 2);
1140 assert_eq!(f3.arity(), 3);
1141 }
1142
1143 #[test]
1144 fn name_and_kind_name() {
1145 let a = &mut Arena::new();
1146
1147 let atom = Term::atom(a, "foo");
1148 let var = Term::var(a, "X");
1149 let fun = Term::func(a, "bar", &[Term::int(1)]);
1150 let tup = Term::tuple(a, &[Term::int(1), Term::int(2)]);
1151 let lst = Term::list(a, &[Term::int(1), Term::int(2), Term::int(3)]);
1152
1153 assert_eq!(atom.name(&a).unwrap(), "foo");
1155 assert_eq!(var.name(&a).unwrap(), "X");
1156 assert_eq!(fun.name(&a).unwrap(), "bar");
1157
1158 assert_eq!(atom.kind_name(), "atom");
1160 assert_eq!(var.kind_name(), "var");
1161 assert_eq!(fun.kind_name(), "func");
1162 assert_eq!(tup.kind_name(), "tuple");
1163 assert_eq!(lst.kind_name(), "list");
1164 assert_eq!(Term::int(7).kind_name(), "int");
1165 assert_eq!(Term::str(a, "s").kind_name(), "str");
1166 }
1167
1168 #[test]
1169 fn is_func_tuple_list() {
1170 let a = &mut Arena::new();
1171
1172 let f2 = Term::func(a, "pair", &[Term::int(1), Term::int(2)]);
1173 let tup = Term::tuple(a, &[Term::int(1), Term::int(2)]);
1174 let lst = Term::list(a, &[Term::int(1), Term::int(2), Term::int(3)]);
1175
1176 assert!(f2.is_func());
1177 assert!(tup.is_tuple());
1178 assert!(lst.is_list());
1179
1180 assert!(!f2.is_tuple());
1181 assert!(!f2.is_list());
1182 assert!(!tup.is_func());
1183 assert!(!lst.is_func());
1184 assert!(!lst.is_tuple());
1185 }
1186
1187 #[test]
1188 fn is_inline_obvious_cases() {
1189 let a = &mut Arena::new();
1190
1191 let i = Term::int(42);
1192 let f0 = Term::func(a, "nilary", &[] as &[Term]);
1193 let tup = Term::tuple(a, &[Term::int(1), Term::int(2)]);
1194 let lst = Term::list(a, &[Term::int(1), Term::int(2)]);
1195
1196 assert!(i.is_inline());
1198 assert!(f0.is_inline()); assert!(!tup.is_inline());
1200 assert!(!lst.is_inline());
1201 }
1202
1203 #[test]
1204 fn is_atom_var_str_bin_number_minimal() {
1205 let a = &mut Arena::new();
1206
1207 let at = Term::atom(a, "foo");
1208 let vr = Term::var(a, "X");
1209 let st = Term::str(a, "hi");
1210 let bi = Term::bin(a, &[1, 2, 3, 4]);
1211 let i = Term::int(7);
1212
1213 assert!(at.is_atom());
1214 assert!(vr.is_var());
1215 assert!(st.is_str());
1216 assert!(bi.is_bin());
1217
1218 assert!(!at.is_var());
1219 assert!(!vr.is_atom());
1220 assert!(!st.is_bin());
1221 assert!(!bi.is_str());
1222
1223 assert!(i.is_number());
1225 assert!(!at.is_number());
1226 assert!(!st.is_number());
1227 assert!(!bi.is_number());
1228 }
1229
1230 #[test]
1231 fn nil_and_tuple_edge_behavior() {
1232 let a = &mut Arena::new();
1233
1234 assert!(Term::NIL.is_list());
1236 assert!(!Term::NIL.is_tuple());
1237 assert!(!Term::NIL.is_func());
1238 assert_eq!(Term::NIL.arity(), 0);
1239
1240 let t = Term::tuple(a, &[Term::int(1), Term::int(2), Term::int(3)]);
1242 assert!(t.is_tuple());
1243 assert_eq!(t.arity(), 3);
1244 assert!(!t.is_list());
1245 assert!(!t.is_func());
1246 }
1247
1248 #[test]
1249 fn arity_consistency_with_predicates() {
1250 let a = &mut Arena::new();
1251
1252 let f3 = Term::func(a, "triple", &[Term::int(1), Term::int(2), Term::int(3)]);
1253 let t2 = Term::tuple(a, &[Term::int(1), Term::int(2)]);
1254 let l2 = Term::list(a, &[Term::int(1), Term::int(2)]);
1255 let v = Term::var(a, "X");
1256
1257 assert!(f3.is_func());
1258 assert_eq!(f3.arity(), 3);
1259
1260 assert!(t2.is_tuple());
1261 assert_eq!(t2.arity(), 2);
1262
1263 assert!(l2.is_list());
1264 assert_eq!(l2.arity(), 0); assert!(v.is_var());
1267 assert_eq!(v.arity(), 0); }
1269
1270 #[test]
1271 fn name_and_kind_name_roundtrip() {
1272 let a = &mut Arena::new();
1273
1274 let atom = Term::atom(a, "foo");
1275 let var = Term::var(a, "X");
1276 let fun = Term::func(a, "bar", &[Term::int(1)]);
1277 let tup = Term::tuple(a, &[Term::int(1), Term::int(2)]);
1278 let lst = Term::list(a, &[Term::int(1), Term::int(2), Term::int(3)]);
1279
1280 assert_eq!(atom.name(&a).unwrap(), "foo");
1282 assert_eq!(var.name(&a).unwrap(), "X");
1283 assert_eq!(fun.name(&a).unwrap(), "bar");
1284
1285 assert_eq!(atom.kind_name(), "atom");
1287 assert_eq!(var.kind_name(), "var");
1288 assert_eq!(fun.kind_name(), "func");
1289 assert_eq!(tup.kind_name(), "tuple");
1290 assert_eq!(lst.kind_name(), "list");
1291 assert_eq!(Term::int(7).kind_name(), "int");
1292 assert_eq!(Term::str(a, "s").kind_name(), "str");
1293 }
1294
1295 #[test]
1296 fn unpack_primitives_ok() {
1297 let a = &mut Arena::new();
1298
1299 let t_int = Term::int(42);
1300 let t_real = Term::real(3.5);
1301 let t_date = Term::date(2);
1302 let t_str = Term::str(a, "hello");
1303 let t_bin = Term::bin(a, &[1u8, 2, 3, 4]);
1304
1305 assert_eq!(t_int.unpack_int(a).unwrap(), 42);
1306 assert!((t_real.unpack_real(a).unwrap() - 3.5).abs() < f64::EPSILON);
1307 assert_eq!(t_date.unpack_date(a).unwrap(), 2);
1308
1309 assert_eq!(t_str.unpack_str(a).unwrap(), "hello");
1310 assert_eq!(t_bin.unpack_bin(a).unwrap(), &[1, 2, 3, 4]);
1311 }
1312
1313 #[test]
1314 fn unpack_primitives_wrong_type_errs() {
1315 let a = &mut Arena::new();
1316
1317 let not_int = Term::str(a, "nope");
1318 let not_real = Term::int(1);
1319 let not_date = Term::str(a, "2024-01-02");
1320
1321 assert!(not_int.unpack_int(a).is_err());
1322 assert!(not_real.unpack_real(a).is_err());
1323 assert!(not_date.unpack_date(a).is_err());
1324
1325 let not_str = Term::int(5);
1326 let not_bin = Term::str(a, "bytes");
1327 assert!(not_str.unpack_str(a).is_err());
1328 assert!(not_bin.unpack_bin(a).is_err());
1329 }
1330
1331 #[test]
1332 fn unpack_atom_and_var_with_allowed_names() {
1333 let a = &mut Arena::new();
1334
1335 let at_ok = Term::atom(a, "foo");
1336 let at_no = Term::atom(a, "bar");
1337 let vr_ok = Term::var(a, "X");
1338 let vr_no = Term::var(a, "Y");
1339
1340 let allowed_atoms = ["foo", "baz"];
1342 let allowed_vars = ["X", "Z"];
1343
1344 assert_eq!(at_ok.unpack_atom(a, &allowed_atoms).unwrap(), "foo");
1345 assert!(at_no.unpack_atom(a, &allowed_atoms).is_err());
1346
1347 assert_eq!(vr_ok.unpack_var(a, &allowed_vars).unwrap(), "X");
1348 assert!(vr_no.unpack_var(a, &allowed_vars).is_err());
1349
1350 assert_eq!(at_no.name(a).unwrap(), "bar");
1352 assert_eq!(vr_no.var_name(a).unwrap(), "Y");
1353 }
1354
1355 #[test]
1356 fn unpack_func_any_and_arity_specific() {
1357 let a = &mut Arena::new();
1358
1359 let f0 = Term::func(a, "nilary", &[] as &[Term]);
1360 let f2 = Term::func(a, "pair", &[Term::int(1), Term::int(2)]);
1361 let f3 = Term::func(a, "triple", &[Term::int(1), Term::int(2), Term::int(3)]);
1362
1363 {
1365 let (name, args) = f2.unpack_func_any(a, &["pair", "other"]).unwrap();
1366 assert_eq!(name.name(a).unwrap(), "pair");
1367 assert_eq!(args.len(), 2);
1368 assert_eq!(args[0].unpack_int(a).unwrap(), 1);
1369 assert_eq!(args[1].unpack_int(a).unwrap(), 2);
1370
1371 let (name0, args0) = f0.unpack_func_any(a, &[]).unwrap();
1373 assert_eq!(name0.name(a).unwrap(), "nilary");
1374 assert!(args0.is_empty());
1375
1376 assert!(f3.unpack_func_any(a, &["not_triple"]).is_err());
1378 }
1379
1380 {
1382 let (name2, [x, y]) = f2.unpack_func(a, &["pair"]).unwrap();
1383 assert_eq!(name2.name(a).unwrap(), "pair");
1384 assert_eq!(x.unpack_int(a).unwrap(), 1);
1385 assert_eq!(y.unpack_int(a).unwrap(), 2);
1386
1387 assert!(f3.unpack_func::<2>(a, &["triple"]).is_err());
1389
1390 assert!(f2.unpack_func::<2>(a, &["other"]).is_err());
1392 }
1393 }
1394
1395 #[test]
1396 fn unpack_list_proper_and_tail() {
1397 let a = &mut Arena::new();
1398
1399 let l0 = Term::list(a, &[] as &[Term]);
1400 let (elems0, tail0) = l0.unpack_list(a).unwrap();
1401 assert!(elems0.is_empty());
1402 assert_eq!(*tail0, Term::NIL);
1403
1404 let l3 = Term::list(a, &[Term::int(1), Term::int(2), Term::int(3)]);
1405 let (elems3, tail3) = l3.unpack_list(a).unwrap();
1406 assert_eq!(elems3.len(), 3);
1407 assert_eq!(elems3[0].unpack_int(a).unwrap(), 1);
1408 assert_eq!(elems3[1].unpack_int(a).unwrap(), 2);
1409 assert_eq!(elems3[2].unpack_int(a).unwrap(), 3);
1410 assert_eq!(tail3, &Term::NIL);
1411
1412 let not_list = Term::int(9);
1414 assert!(not_list.unpack_list(a).is_err());
1415 }
1416
1417 #[test]
1418 fn unpack_tuple_any_and_fixed() {
1419 let a = &mut Arena::new();
1420
1421 let t0 = Term::tuple(a, &[] as &[Term]);
1422 let t3 = Term::tuple(a, &[Term::int(1), Term::int(2), Term::int(3)]);
1423
1424 let elems0 = t0.unpack_tuple_any(a).unwrap();
1426 assert!(elems0.is_empty());
1427
1428 let elems3 = t3.unpack_tuple_any(a).unwrap();
1429 assert_eq!(elems3.len(), 3);
1430 assert_eq!(elems3[0].unpack_int(a).unwrap(), 1);
1431 assert_eq!(elems3[1].unpack_int(a).unwrap(), 2);
1432 assert_eq!(elems3[2].unpack_int(a).unwrap(), 3);
1433
1434 let arr3 = t3.unpack_tuple::<3>(a).unwrap();
1436 assert_eq!(arr3[0].unpack_int(a).unwrap(), 1);
1437 assert_eq!(arr3[1].unpack_int(a).unwrap(), 2);
1438 assert_eq!(arr3[2].unpack_int(a).unwrap(), 3);
1439
1440 assert!(t3.unpack_tuple::<2>(a).is_err());
1442
1443 assert!(Term::int(1).unpack_tuple_any(a).is_err());
1445 assert!(Term::int(1).unpack_tuple::<0>(a).is_err());
1446 }
1447
1448 #[test]
1449 fn unpack_atom_var_wrong_type_errs() {
1450 let a = &mut Arena::new();
1451
1452 let not_atom = Term::int(1);
1453 let not_var = Term::str(a, "X");
1454 assert!(not_atom.atom_name(a).is_err());
1455 assert!(not_var.var_name(a).is_err());
1456 }
1457
1458 #[test]
1459 fn unpack_func_wrong_type_errs() {
1460 let a = &mut Arena::new();
1461
1462 let tup = Term::tuple(a, &[Term::int(1), Term::int(2)]);
1464 assert!(tup.func_name(a).is_err());
1465 assert!(tup.unpack_func::<2>(a, &[]).is_err());
1466
1467 let at = Term::atom(a, "f");
1469 assert!(!at.unpack_func_any(a, &[]).is_err());
1470 assert!(!at.unpack_func::<0>(a, &[]).is_err());
1471 }
1472
1473 #[test]
1474 fn fmt_nil_to_string() {
1475 let arena = Arena::new();
1476 let t = Term::NIL;
1477 assert_eq!(t.display(&arena).to_string(), "nil");
1478 }
1479
1480 #[test]
1481 fn fmt_unit_format_macro() {
1482 let arena = Arena::new();
1483 let t = Term::UNIT;
1484 assert_eq!(format!("{}", t.display(&arena)), "unit");
1485 }
1486
1487 #[test]
1488 fn fmt_int_positive() {
1489 let arena = Arena::new();
1490 let t = Term::int(42);
1491 assert_eq!(format!("{}", t.display(&arena)), "42");
1492 }
1493
1494 #[test]
1495 fn fmt_int_negative_to_string() {
1496 let arena = Arena::new();
1497 let t = Term::int(-9001);
1498 assert_eq!(t.display(&arena).to_string(), "-9001");
1499 }
1500
1501 #[test]
1502 fn fmt_str_quotes() {
1503 let mut arena = Arena::new();
1504 let t = Term::str(&mut arena, "hello");
1505 assert_eq!(format!("{}", t.display(&arena)), r#""hello""#);
1506 }
1507
1508 #[test]
1509 fn fmt_str_with_escape_chars() {
1510 let mut arena = Arena::new();
1511 let t = Term::str(&mut arena, "a\nb\tc");
1512 assert_eq!(t.display(&arena).to_string(), "\"a\\nb\\tc\"");
1513 }
1514
1515 #[test]
1516 fn fmt_date_epoch_zero() {
1517 let arena = Arena::new();
1518 let t = Term::date(0);
1520 assert_eq!(
1521 format!("{}", t.display(&arena)),
1522 "date{1970-01-01T00:00:00+00:00}"
1523 );
1524 }
1525
1526 #[test]
1527 fn fmt_date_epoch_ms_trunc_to_seconds() {
1528 let arena = Arena::new();
1529 let t = Term::date(1_234);
1530 assert_eq!(
1531 t.display(&arena).to_string(),
1532 "date{1970-01-01T00:00:01.234+00:00}"
1533 );
1534 }
1535
1536 #[test]
1537 fn fmt_date_specific_moment() {
1538 let arena = Arena::new();
1539 let t = Term::date(1_727_525_530_123i64);
1540 assert_eq!(
1541 format!("{}", t.display(&arena)),
1542 "date{2024-09-28T12:12:10.123+00:00}"
1543 );
1544 }
1545
1546 #[test]
1547 fn fmt_date_specific_moment_in_the_past() {
1548 let arena = Arena::new();
1549 let t = Term::date(-5_382_698_399_999i64);
1550 assert_eq!(
1551 format!("{}", t.display(&arena)),
1552 "date{1799-06-06T06:00:00.001+00:00}"
1553 );
1554 }
1555
1556 #[test]
1557 fn fmt_write_into_string_buffer() {
1558 let arena = Arena::new();
1559 let t = Term::int(7);
1560 let mut buf = String::new();
1561 write!(&mut buf, "val={}", t.display(&arena)).expect("formatting failed");
1563 assert_eq!(buf, "val=7");
1564 }
1565}