1use crate::{Error, value::Value, value_inner};
2use core::fmt::{self, Display, Formatter};
3
4pub type Tag = u16;
6
7pub(crate) const NEVER: Cons = Cons::new(u64::MAX);
15
16const TAG_SIZE: usize = Tag::BITS as usize;
17const TAG_MASK: u64 = Tag::MAX as u64;
18
19#[derive(Clone, Copy, Debug)]
21pub struct Cons(u64);
22
23impl Cons {
24 #[inline]
26 pub const fn new(index: u64) -> Self {
27 Self::r#box(index << TAG_SIZE)
28 }
29
30 #[inline]
32 pub const fn index(self) -> usize {
33 (self.unbox() >> TAG_SIZE) as _
34 }
35
36 #[inline]
38 pub const fn tag(self) -> Tag {
39 (self.unbox() & TAG_MASK) as _
40 }
41
42 #[inline]
44 pub const fn set_tag(self, tag: Tag) -> Self {
45 Self::r#box(self.unbox() & !TAG_MASK | (tag as u64 & TAG_MASK))
46 }
47
48 #[inline]
49 const fn r#box(value: u64) -> Self {
50 Self(value_inner::box_cons(value))
51 }
52
53 #[inline]
54 const fn unbox(self) -> u64 {
55 value_inner::unbox_cons(self.0)
56 }
57
58 #[inline]
59 pub(crate) const fn index_eq(&self, other: Self) -> bool {
60 self.index() == other.index()
61 }
62
63 #[inline]
64 pub(crate) const fn from_raw(raw: u64) -> Self {
65 Self(raw)
66 }
67
68 #[inline]
69 pub(crate) const fn to_raw(self) -> u64 {
70 self.0
71 }
72}
73
74impl PartialEq for Cons {
75 #[inline]
76 fn eq(&self, other: &Self) -> bool {
77 self.index_eq(*other)
78 }
79}
80
81impl Eq for Cons {}
82
83impl TryFrom<Value> for Cons {
84 type Error = Error;
85
86 #[inline]
87 fn try_from(value: Value) -> Result<Self, Self::Error> {
88 value.to_cons().ok_or(Error::ConsExpected)
89 }
90}
91
92impl Display for Cons {
93 fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
94 if *self == NEVER {
95 return write!(formatter, "!");
96 }
97
98 write!(formatter, "c{:x}", self.index())?;
99
100 if self.tag() > 0 {
101 write!(formatter, ":{}", self.tag())?;
102 }
103
104 Ok(())
105 }
106}
107
108#[cfg(test)]
109mod tests {
110 use super::*;
111
112 #[test]
113 fn tag() {
114 let cons = Cons::new(42);
115
116 assert_eq!(cons.index(), 42);
117 assert_eq!(cons.tag(), 0);
118
119 let cons = cons.set_tag(1);
120
121 assert_eq!(cons.index(), 42);
122 assert_eq!(cons.tag(), 1);
123
124 let cons = cons.set_tag(3);
125
126 assert_eq!(cons.index(), 42);
127 assert_eq!(cons.tag(), 3);
128 }
129
130 #[test]
131 fn reset_tag() {
132 assert_eq!(Cons::new(42).set_tag(2).set_tag(1).tag(), 1);
133 }
134
135 #[test]
136 fn set_too_large_tag() {
137 let cons = Cons::new(0).set_tag(Tag::MAX);
138
139 assert_eq!(cons.index(), 0);
140 assert_eq!(cons.tag(), TAG_MASK as Tag);
141 }
142}