1use crate::{
2 cons::{Cons, Tag},
3 number::Number,
4 value_inner,
5};
6use core::fmt::{self, Display, Formatter};
7
8#[derive(Copy, Clone, Debug)]
10pub struct Value(u64);
11
12#[derive(Copy, Clone, Debug, PartialEq)]
14#[cfg_attr(not(feature = "float"), derive(Eq))]
15pub enum TypedValue {
16 Cons(Cons),
17 Number(Number),
18}
19
20impl Value {
21 #[inline]
23 pub const fn to_cons(self) -> Option<Cons> {
24 if let TypedValue::Cons(cons) = self.to_typed() {
25 Some(cons)
26 } else {
27 None
28 }
29 }
30
31 #[inline]
33 pub const fn to_number(self) -> Option<Number> {
34 if let TypedValue::Number(number) = self.to_typed() {
35 Some(number)
36 } else {
37 None
38 }
39 }
40
41 #[inline]
43 pub const fn to_typed(self) -> TypedValue {
44 if self.is_cons() {
45 TypedValue::Cons(self.assume_cons())
46 } else {
47 TypedValue::Number(self.assume_number())
48 }
49 }
50
51 #[inline]
53 pub const fn assume_cons(self) -> Cons {
54 debug_assert!(self.is_cons());
55
56 Cons::from_raw(self.0)
57 }
58
59 #[inline]
61 pub const fn assume_number(self) -> Number {
62 debug_assert!(self.is_number());
63
64 Number::from_raw(self.0)
65 }
66
67 #[inline]
69 pub const fn is_cons(&self) -> bool {
70 value_inner::is_cons(self.0)
71 }
72
73 #[inline]
75 pub const fn is_number(&self) -> bool {
76 !self.is_cons()
77 }
78
79 #[inline]
81 pub const fn tag(self) -> Tag {
82 if let Some(cons) = self.to_cons() {
83 cons.tag()
84 } else {
85 0
86 }
87 }
88
89 #[inline]
91 pub const fn set_tag(self, tag: Tag) -> Self {
92 if let Some(cons) = self.to_cons() {
93 Self::from_cons(cons.set_tag(tag))
94 } else {
95 self
96 }
97 }
98
99 const fn from_cons(cons: Cons) -> Self {
100 Self(cons.to_raw())
101 }
102}
103
104impl Default for Value {
105 #[inline]
106 fn default() -> Self {
107 Number::default().into()
108 }
109}
110
111impl PartialEq for Value {
112 #[inline]
113 fn eq(&self, other: &Self) -> bool {
114 self.is_cons() && self.to_cons() == other.to_cons()
115 || self.is_number() && self.to_number() == other.to_number()
116 }
117}
118
119impl Eq for Value {}
120
121impl From<Cons> for Value {
122 #[inline]
123 fn from(cons: Cons) -> Self {
124 Self(cons.to_raw())
125 }
126}
127
128impl From<Number> for Value {
129 #[inline]
130 fn from(number: Number) -> Self {
131 Self(number.to_raw())
132 }
133}
134
135impl Display for Value {
136 fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
137 match self.to_typed() {
138 TypedValue::Cons(cons) => write!(formatter, "{cons}"),
139 TypedValue::Number(number) => write!(formatter, "{number}"),
140 }
141 }
142}
143
144#[cfg(test)]
145mod tests {
146 use super::*;
147 use crate::{Type, cons::NEVER};
148
149 #[test]
150 fn convert_cons() {
151 let cons = Cons::new(42);
152
153 assert_eq!(Value::from(cons).to_cons().unwrap(), cons);
154 }
155
156 #[test]
157 fn convert_tagged_cons() {
158 const TAG: Tag = 0b111;
159
160 let cons = Cons::new(42).set_tag(TAG);
161 let converted = Value::from(cons).to_cons().unwrap();
162
163 assert_eq!(converted, cons);
164 assert_eq!(converted.tag(), TAG);
165 }
166
167 #[test]
168 fn convert_number() {
169 let number = Number::from_i64(42);
170
171 assert_eq!(Value::from(number).to_number().unwrap(), number);
172 }
173
174 #[test]
175 fn convert_moved() {
176 assert_eq!(Value::from(NEVER).to_cons().unwrap(), NEVER);
177 }
178
179 #[test]
180 fn get_tag_from_number() {
181 let tag = Value::from(Number::from_i64(42)).tag();
182
183 assert_eq!(tag, Default::default());
184 assert_eq!(tag, Type::default() as Tag);
185 }
186
187 #[test]
188 fn set_tag_to_number() {
189 let value = Value::from(Number::from_i64(42)).set_tag(0b111);
190
191 assert_eq!(value.tag(), Default::default());
192 assert_eq!(value.to_number(), Some(Number::from_i64(42)));
193 }
194}