1use std::collections::BTreeMap;
2
3use crate::input::Kind;
4
5#[derive(Debug, Clone, PartialEq)]
6pub enum Value {
7 Null,
8 Bool(bool),
9 Int(Int),
10 Float(f64),
11 String(String),
12 Array(Vec<Value>),
13 Object(BTreeMap<String, Value>),
14 IntTooWide,
17}
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
22pub enum Int {
23 Signed(i64),
24 Unsigned(u64),
25}
26
27impl Int {
28 pub fn as_i128(self) -> i128 {
29 match self {
30 Int::Signed(v) => i128::from(v),
31 Int::Unsigned(v) => i128::from(v),
32 }
33 }
34
35 pub fn is_negative(self) -> bool {
36 matches!(self, Int::Signed(v) if v < 0)
37 }
38}
39
40impl From<i64> for Int {
41 fn from(v: i64) -> Self {
42 Int::Signed(v)
43 }
44}
45
46impl From<u64> for Int {
47 fn from(v: u64) -> Self {
48 match i64::try_from(v) {
49 Ok(v) => Int::Signed(v),
50 Err(_) => Int::Unsigned(v),
51 }
52 }
53}
54
55#[derive(Debug, Clone, Copy, PartialEq)]
58pub enum Slot<T> {
59 Absent,
60 Null,
61 Present(T),
62}
63
64impl<'a> Slot<&'a Value> {
65 pub fn read(object: &'a BTreeMap<String, Value>, key: &str) -> Self {
66 match object.get(key) {
67 None => Slot::Absent,
68 Some(Value::Null) => Slot::Null,
69 Some(v) => Slot::Present(v),
70 }
71 }
72}
73
74impl crate::input::Input for Value {
75 type Child<'a> = &'a Value;
76
77 fn kind(&self) -> Kind {
78 match self {
79 Value::Null => Kind::Null,
80 Value::Bool(_) => Kind::Bool,
81 Value::Int(_) => Kind::Int,
82 Value::Float(_) => Kind::Float,
83 Value::String(_) => Kind::String,
84 Value::Array(_) => Kind::Array,
85 Value::Object(_) => Kind::Object,
86 Value::IntTooWide => Kind::IntegerTooWide,
87 }
88 }
89
90 fn as_bool(&self) -> Option<bool> {
91 match self {
92 Value::Bool(b) => Some(*b),
93 _ => None,
94 }
95 }
96
97 fn as_int(&self) -> Option<Int> {
98 match self {
99 Value::Int(n) => Some(*n),
100 _ => None,
101 }
102 }
103
104 fn as_f64(&self) -> Option<f64> {
105 match self {
106 Value::Float(f) => Some(*f),
107 Value::Int(n) => Some(n.as_i128() as f64),
109 _ => None,
110 }
111 }
112
113 fn as_str(&self) -> Option<std::borrow::Cow<'_, str>> {
114 match self {
115 Value::String(s) => Some(std::borrow::Cow::Borrowed(s)),
116 _ => None,
117 }
118 }
119
120 fn len(&self) -> usize {
121 match self {
122 Value::Array(items) => items.len(),
123 Value::Object(map) => map.len(),
124 _ => 0,
125 }
126 }
127
128 fn item(&self, index: usize) -> Option<&Value> {
129 match self {
130 Value::Array(items) => items.get(index),
131 _ => None,
132 }
133 }
134
135 fn slot(&self, key: &str) -> Slot<&Value> {
136 match self {
137 Value::Object(map) => Slot::read(map, key),
138 _ => Slot::Absent,
139 }
140 }
141
142 fn each_key(&self, f: &mut dyn FnMut(&str)) {
143 if let Value::Object(map) = self {
144 for k in map.keys() {
145 f(k);
146 }
147 }
148 }
149}
150
151#[cfg(test)]
152mod tests {
153 use super::*;
154
155 #[test]
156 fn u64_above_i64_max_keeps_its_value() {
157 let v = Int::from(u64::MAX);
158 assert_eq!(v, Int::Unsigned(u64::MAX));
159 assert_eq!(v.as_i128(), i128::from(u64::MAX));
160 }
161
162 #[test]
163 fn the_max_safe_integer_boundary_is_exact() {
164 let v = Int::from(9_007_199_254_740_993_u64);
165 assert_eq!(v.as_i128(), 9_007_199_254_740_993_i128);
166 }
167
168 #[test]
169 fn absent_and_null_do_not_collapse() {
170 let mut o = BTreeMap::new();
171 o.insert("null_key".to_string(), Value::Null);
172
173 assert_eq!(Slot::read(&o, "null_key"), Slot::Null);
174 assert_eq!(Slot::read(&o, "missing_key"), Slot::Absent);
175 }
176}