Skip to main content

max / makenotwork

2.1 KB · 72 lines History Blame Raw
1 //! The leaf value type flowing through substitution and filters.
2
3 use std::fmt;
4
5 /// A leaf value: the result of resolving a `{{ path }}` before and after
6 /// filters run. Callers populate a [`crate::Substituter`] with these and read
7 /// them back out with `get`.
8 #[derive(Debug, Clone, PartialEq)]
9 pub enum Value {
10 Int(i64),
11 Float(f64),
12 String(String),
13 }
14
15 impl Value {
16 /// Coerce to `f64` for numeric filters. `Int` is widened; `String` returns `None`.
17 pub fn as_f64(&self) -> Option<f64> {
18 match self {
19 Self::Int(n) => Some(*n as f64),
20 Self::Float(x) => Some(*x),
21 Self::String(_) => None,
22 }
23 }
24 }
25
26 impl fmt::Display for Value {
27 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
28 match self {
29 Self::Int(n) => write!(f, "{n}"),
30 Self::Float(x) => f.write_str(&format_float(*x)),
31 Self::String(s) => f.write_str(s),
32 }
33 }
34 }
35
36 /// Format a float with up to 4 decimal places, trimming trailing zeros.
37 ///
38 /// `22.0 -> "22"`, `0.59 -> "0.59"`, `0.0367 -> "0.0367"`, `61960.0 -> "61960"`.
39 fn format_float(x: f64) -> String {
40 let s = format!("{x:.4}");
41 let trimmed = s.trim_end_matches('0').trim_end_matches('.');
42 trimmed.to_string()
43 }
44
45 #[cfg(test)]
46 mod tests {
47 use super::*;
48
49 #[test]
50 fn float_formatting_strips_trailing_zeros() {
51 assert_eq!(format_float(22.0), "22");
52 assert_eq!(format_float(0.59), "0.59");
53 assert_eq!(format_float(61960.0), "61960");
54 assert_eq!(format_float(0.5), "0.5");
55 assert_eq!(format_float(1.085), "1.085");
56 }
57
58 #[test]
59 fn as_f64_widens_int_and_rejects_string() {
60 assert_eq!(Value::Int(5).as_f64(), Some(5.0));
61 assert_eq!(Value::Float(1.5).as_f64(), Some(1.5));
62 assert_eq!(Value::String("x".into()).as_f64(), None);
63 }
64
65 #[test]
66 fn display_matches_format() {
67 assert_eq!(Value::Int(580).to_string(), "580");
68 assert_eq!(Value::Float(0.5).to_string(), "0.5");
69 assert_eq!(Value::String("lifetime".into()).to_string(), "lifetime");
70 }
71 }
72