1use std::fmt;
5use std::sync::Arc;
6
7#[derive(Clone, Debug, PartialEq)]
39pub enum Value {
40 F64(f64),
42 Int(i64),
45 U64(u64),
47 Usize(usize),
49 Bool(bool),
52 Arr(Arc<Vec<f64>>),
54}
55
56impl Value {
57 pub fn type_name(&self) -> &'static str {
60 match self {
61 Value::F64(_) => "f64",
62 Value::Int(_) => "int",
63 Value::U64(_) => "u64",
64 Value::Usize(_) => "usize",
65 Value::Bool(_) => "bool",
66 Value::Arr(_) => "array",
67 }
68 }
69
70 pub fn as_f64(&self) -> Option<f64> {
73 match self {
74 Value::F64(x) => Some(*x),
75 Value::Int(i) => Some(*i as f64),
76 Value::U64(u) => Some(*u as f64),
77 Value::Usize(u) => Some(*u as f64),
78 Value::Bool(b) => Some(if *b { 1.0 } else { 0.0 }),
79 Value::Arr(_) => None,
80 }
81 }
82
83 pub fn as_bool(&self) -> Option<bool> {
86 match self {
87 Value::Bool(b) => Some(*b),
88 Value::F64(x) if x.is_nan() => None,
89 Value::F64(x) => Some(*x != 0.0),
90 Value::Int(i) => Some(*i != 0),
91 Value::U64(u) => Some(*u != 0),
92 Value::Usize(u) => Some(*u != 0),
93 Value::Arr(_) => None,
94 }
95 }
96
97 pub fn as_i64(&self) -> Option<i64> {
100 match self {
101 Value::Int(i) => Some(*i),
102 Value::U64(u) => i64::try_from(*u).ok(),
103 Value::Usize(u) => i64::try_from(*u).ok(),
104 Value::Bool(b) => Some(i64::from(*b)),
105 Value::F64(x) if x.fract() == 0.0 && *x >= -9.223_372_036_854_776e18 => {
107 if *x < 9.223_372_036_854_776e18 {
108 Some(*x as i64)
109 } else {
110 None
111 }
112 }
113 Value::F64(_) | Value::Arr(_) => None,
114 }
115 }
116
117 pub fn as_u64(&self) -> Option<u64> {
120 match self {
121 Value::U64(u) => Some(*u),
122 Value::Usize(u) => u64::try_from(*u).ok(),
123 Value::F64(x) if x.fract() == 0.0 && *x >= 0.0 => {
125 if *x < 1.844_674_407_370_955_2e19 {
126 Some(*x as u64)
127 } else {
128 None
129 }
130 }
131 other => other.as_i64().and_then(|i| u64::try_from(i).ok()),
132 }
133 }
134
135 pub fn as_usize(&self) -> Option<usize> {
138 match self {
139 Value::Usize(u) => Some(*u),
140 other => other.as_u64().and_then(|u| usize::try_from(u).ok()),
141 }
142 }
143
144 pub fn as_array(&self) -> Option<&[f64]> {
146 match self {
147 Value::Arr(a) => Some(a.as_slice()),
148 _ => None,
149 }
150 }
151
152 pub(crate) fn to_f64_lossy(&self) -> f64 {
158 self.as_f64().unwrap_or(f64::NAN)
159 }
160
161 pub(crate) fn num(&self) -> Result<Num, String> {
164 match self {
165 Value::F64(x) => Ok(Num::F(*x)),
166 Value::Int(i) => Ok(Num::I(*i)),
167 Value::Bool(b) => Ok(Num::I(i64::from(*b))),
168 Value::U64(u) => Ok(i64::try_from(*u).map_or(Num::F(*u as f64), Num::I)),
169 Value::Usize(u) => Ok(i64::try_from(*u).map_or(Num::F(*u as f64), Num::I)),
170 Value::Arr(_) => Err("expected a number, found an array".to_string()),
171 }
172 }
173}
174
175#[derive(Clone, Copy, Debug)]
177pub(crate) enum Num {
178 I(i64),
179 F(f64),
180}
181
182impl Num {
183 pub(crate) fn to_f64(self) -> f64 {
184 match self {
185 Num::I(i) => i as f64,
186 Num::F(x) => x,
187 }
188 }
189}
190
191impl fmt::Display for Value {
196 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
197 match self {
198 Value::F64(x) => write!(f, "{x}"),
199 Value::Int(i) => write!(f, "{i}"),
200 Value::U64(u) => write!(f, "{u}"),
201 Value::Usize(u) => write!(f, "{u}"),
202 Value::Bool(b) => write!(f, "{b}"),
203 Value::Arr(a) => {
204 f.write_str("[")?;
205 for (i, x) in a.iter().enumerate() {
206 if i > 0 {
207 f.write_str(", ")?;
208 }
209 write!(f, "{x}")?;
210 }
211 f.write_str("]")
212 }
213 }
214 }
215}
216
217impl From<f64> for Value {
218 fn from(x: f64) -> Self {
219 Value::F64(x)
220 }
221}
222
223impl From<i64> for Value {
224 fn from(i: i64) -> Self {
225 Value::Int(i)
226 }
227}
228
229impl From<i32> for Value {
230 fn from(i: i32) -> Self {
231 Value::Int(i64::from(i))
232 }
233}
234
235impl From<u64> for Value {
236 fn from(u: u64) -> Self {
237 Value::U64(u)
238 }
239}
240
241impl From<usize> for Value {
242 fn from(u: usize) -> Self {
243 Value::Usize(u)
244 }
245}
246
247impl From<bool> for Value {
248 fn from(b: bool) -> Self {
249 Value::Bool(b)
250 }
251}
252
253impl From<Vec<f64>> for Value {
254 fn from(v: Vec<f64>) -> Self {
255 Value::Arr(Arc::new(v))
256 }
257}
258
259impl From<&[f64]> for Value {
260 fn from(v: &[f64]) -> Self {
261 Value::Arr(Arc::new(v.to_vec()))
262 }
263}
264
265#[cfg(test)]
266mod tests {
267 use super::*;
268
269 #[test]
270 fn integral_conversions_respect_ranges() {
271 assert_eq!(Value::F64(-0.0).as_i64(), Some(0));
272 assert_eq!(Value::F64(9.223_372_036_854_776e18).as_i64(), None);
273 assert_eq!(
274 Value::F64(-9.223_372_036_854_776e18).as_i64(),
275 Some(i64::MIN)
276 );
277 assert_eq!(Value::F64(f64::NAN).as_i64(), None);
278 assert_eq!(Value::F64(f64::INFINITY).as_u64(), None);
279 assert_eq!(Value::F64(1.844_674_407_370_955_2e19).as_u64(), None);
280 assert_eq!(Value::U64(u64::MAX).as_u64(), Some(u64::MAX));
281 assert_eq!(Value::U64(u64::MAX).as_i64(), None);
282 assert_eq!(Value::Bool(true).as_u64(), Some(1));
283 assert_eq!(Value::Int(-3).as_usize(), None);
284 assert_eq!(Value::from(vec![1.0]).as_i64(), None);
285 }
286
287 #[test]
288 fn truthiness_and_display() {
289 assert_eq!(Value::F64(f64::NAN).as_bool(), None);
290 assert_eq!(Value::Int(2).as_bool(), Some(true));
291 assert_eq!(Value::from(vec![1.0]).as_bool(), None);
292 assert_eq!(Value::F64(3.0).to_string(), "3");
293 assert_eq!(Value::F64(0.5).to_string(), "0.5");
294 assert_eq!(Value::Bool(true).to_string(), "true");
295 assert_eq!(Value::from(vec![1.0, 2.5]).to_string(), "[1, 2.5]");
296 assert_eq!(
297 Value::from(vec![1.0]).to_f64_lossy().to_bits(),
298 f64::NAN.to_bits()
299 );
300 }
301}