Skip to main content

mical_cli_config/
json.rs

1use crate::{Config, Value};
2use smallvec::ToSmallVec;
3
4fn normalize_integer(s: &str) -> i64 {
5    let s = if let Some(rest) = s.strip_prefix('+') { rest } else { s };
6    if let Some(hex) = s.strip_prefix("0x").or_else(|| s.strip_prefix("0X")) {
7        i64::from_str_radix(hex, 16).unwrap_or_else(|_| {
8            // TODO: handle integers that don't fit in i64
9            panic!("unsupported: integer '{s}' does not fit in i64")
10        })
11    } else if let Some(hex) = s.strip_prefix("-0x").or_else(|| s.strip_prefix("-0X")) {
12        let abs = i64::from_str_radix(hex, 16).unwrap_or_else(|_| {
13            // TODO: handle integers that don't fit in i64
14            panic!("unsupported: integer '{s}' does not fit in i64")
15        });
16        abs.checked_neg().unwrap_or_else(|| {
17            // TODO: handle integers that don't fit in i64
18            panic!("unsupported: integer '{s}' does not fit in i64")
19        })
20    } else {
21        s.parse::<i64>().unwrap_or_else(|_| {
22            // TODO: handle integers that don't fit in i64
23            panic!("unsupported: integer '{s}' does not fit in i64")
24        })
25    }
26}
27
28impl<'s> Value<'s> {
29    pub fn to_json(&self) -> serde_json::Value {
30        match self {
31            Value::Bool(b) => serde_json::Value::Bool(*b),
32            Value::Integer(s) => {
33                let n = normalize_integer(s);
34                serde_json::Value::Number(n.into())
35            }
36            Value::String(s) => serde_json::Value::String((*s).to_owned()),
37        }
38    }
39}
40
41impl Config {
42    pub fn to_json(&self) -> serde_json::Value {
43        let mut map = serde_json::Map::new();
44        for &(group_start, count) in &self.group_order {
45            let (group_start, count) = (group_start as usize, count as usize);
46            let mut idxs: smallvec::SmallVec<[u32; 2]> =
47                { self.sorted_indices[group_start..(group_start + count)].to_smallvec() };
48            idxs.sort_unstable();
49            let key = &self.arena[self.entries[idxs[0] as usize].0];
50            if count == 1 {
51                let val = self.entries[idxs[0] as usize].1.to_value(&self.arena);
52                map.insert(key.to_owned(), val.to_json());
53            } else {
54                let arr: Vec<serde_json::Value> = idxs
55                    .iter()
56                    .map(|&i| {
57                        let val = self.entries[i as usize].1.to_value(&self.arena);
58                        val.to_json()
59                    })
60                    .collect();
61                map.insert(key.to_owned(), serde_json::Value::Array(arr));
62            }
63        }
64        serde_json::Value::Object(map)
65    }
66}
67
68#[cfg(test)]
69mod tests {
70    use super::normalize_integer;
71
72    #[test]
73    fn decimal() {
74        assert_eq!(normalize_integer("42"), 42);
75        assert_eq!(normalize_integer("0"), 0);
76    }
77
78    #[test]
79    fn signed() {
80        assert_eq!(normalize_integer("+7"), 7);
81        assert_eq!(normalize_integer("-10"), -10);
82        assert_eq!(normalize_integer("+0"), 0);
83    }
84
85    #[test]
86    fn hex() {
87        assert_eq!(normalize_integer("0xFF"), 255);
88        assert_eq!(normalize_integer("0XFF"), 255);
89        assert_eq!(normalize_integer("0x0"), 0);
90    }
91
92    #[test]
93    fn signed_hex() {
94        assert_eq!(normalize_integer("-0xFF"), -255);
95        assert_eq!(normalize_integer("-0XA"), -10);
96    }
97
98    #[test]
99    #[should_panic(expected = "unsupported")]
100    fn overflow_panics() {
101        normalize_integer("99999999999999999999");
102    }
103}