1#[derive(Clone, Debug, PartialEq)]
6pub enum Value {
7 Null,
9 Bool(bool),
11 I64(i64),
13 F64(f64),
15 String(Box<str>),
17 Array(Vec<Value>),
19 Object(Object),
21}
22
23#[derive(Clone, Debug, Default, PartialEq)]
25pub struct Object {
26 pub entries: Vec<(StringKey, Value)>,
28}
29
30#[derive(Clone, Debug, PartialEq, Eq, Hash)]
32pub struct StringKey(Box<str>);
33
34impl StringKey {
35 #[must_use]
37 pub fn new(s: &str) -> Self {
38 Self(s.into())
39 }
40}
41
42impl AsRef<str> for StringKey {
43 fn as_ref(&self) -> &str {
44 &self.0
45 }
46}
47
48impl Object {
49 #[must_use]
51 pub const fn new() -> Self {
52 Self {
53 entries: Vec::new(),
54 }
55 }
56
57 pub fn insert(&mut self, key: &str, value: Value) {
61 match self.entries.iter_mut().find(|(k, _)| k.as_ref() == key) {
62 Some((_, v)) => *v = value,
63 None => self.entries.push((StringKey(key.into()), value)),
64 }
65 }
66
67 pub fn iter(&self) -> impl Iterator<Item = (&StringKey, &Value)> {
69 self.entries.iter().map(|(k, v)| (k, v))
70 }
71
72 #[must_use]
76 pub fn get(&self, key: &str) -> Option<&Value> {
77 self.entries
78 .iter()
79 .find(|(k, _)| k.as_ref() == key)
80 .map(|(_, v)| v)
81 }
82}
83
84pub trait ToValue {
100 fn to_value(self) -> Value;
102}
103
104impl ToValue for i8 {
105 fn to_value(self) -> Value {
106 Value::I64(i64::from(self))
107 }
108}
109
110impl ToValue for i16 {
111 fn to_value(self) -> Value {
112 Value::I64(i64::from(self))
113 }
114}
115
116impl ToValue for i32 {
117 fn to_value(self) -> Value {
118 Value::I64(i64::from(self))
119 }
120}
121
122impl ToValue for i64 {
123 fn to_value(self) -> Value {
124 Value::I64(self)
125 }
126}
127
128impl ToValue for u8 {
129 fn to_value(self) -> Value {
130 Value::I64(i64::from(self))
131 }
132}
133
134impl ToValue for u16 {
135 fn to_value(self) -> Value {
136 Value::I64(i64::from(self))
137 }
138}
139
140impl ToValue for u32 {
141 fn to_value(self) -> Value {
142 Value::I64(i64::from(self))
143 }
144}
145
146impl ToValue for f32 {
147 fn to_value(self) -> Value {
148 Value::F64(f64::from(self))
149 }
150}
151
152impl ToValue for f64 {
153 fn to_value(self) -> Value {
154 Value::F64(self)
155 }
156}
157
158impl ToValue for String {
159 fn to_value(self) -> Value {
160 Value::String(self.into())
161 }
162}
163
164impl ToValue for bool {
165 fn to_value(self) -> Value {
166 Value::Bool(self)
167 }
168}
169
170impl Value {
171 #[must_use]
173 pub fn as_str(&self) -> Option<&str> {
174 match self {
175 Self::String(s) => Some(s),
176 _ => None,
177 }
178 }
179
180 #[must_use]
182 pub fn as_bool(&self) -> Option<bool> {
183 match self {
184 Self::Bool(b) => Some(*b),
185 _ => None,
186 }
187 }
188
189 #[must_use]
191 pub fn as_i64(&self) -> Option<i64> {
192 match self {
193 Self::I64(i) => Some(*i),
194 _ => None,
195 }
196 }
197
198 #[must_use]
200 pub fn as_f64(&self) -> Option<f64> {
201 match self {
202 Self::F64(f) => Some(*f),
203 _ => None,
204 }
205 }
206}
207
208impl From<&str> for Value {
209 fn from(s: &str) -> Self {
210 Self::String(s.into())
211 }
212}
213
214impl From<String> for Value {
215 fn from(s: String) -> Self {
216 Self::String(s.into())
217 }
218}
219
220impl From<bool> for Value {
221 fn from(b: bool) -> Self {
222 Self::Bool(b)
223 }
224}
225
226impl From<i64> for Value {
227 fn from(i: i64) -> Self {
228 Self::I64(i)
229 }
230}
231
232impl From<f64> for Value {
233 fn from(f: f64) -> Self {
234 Self::F64(f)
235 }
236}
237
238#[cfg(test)]
239mod tests {
240 use super::{Object, ToValue, Value};
241
242 #[test]
243 fn d5_get_finds_present_key() {
244 let mut o = Object::new();
245 o.insert("name", Value::String("Ada".into()));
246 assert_eq!(o.get("name"), Some(&Value::String("Ada".into())));
247 }
248
249 #[test]
250 fn d5_get_misses_absent_key() {
251 let mut o = Object::new();
252 o.insert("name", Value::String("Ada".into()));
253 assert_eq!(o.get("age"), None);
254 }
255
256 #[test]
257 fn d5_get_on_empty_object() {
258 let o = Object::new();
259 assert_eq!(o.get("anything"), None);
260 }
261
262 #[test]
263 fn d4_tovalue_mappings_are_pinned() {
264 assert_eq!((-1i8).to_value(), Value::I64(-1));
265 assert_eq!((-2i16).to_value(), Value::I64(-2));
266 assert_eq!((-3i32).to_value(), Value::I64(-3));
267 assert_eq!((-4i64).to_value(), Value::I64(-4));
268 assert_eq!(1u8.to_value(), Value::I64(1));
269 assert_eq!(2u16.to_value(), Value::I64(2));
270 assert_eq!(3u32.to_value(), Value::I64(3));
271 assert_eq!(0.5f32.to_value(), Value::F64(0.5));
272 assert_eq!(1.5f64.to_value(), Value::F64(1.5));
273 assert_eq!(String::from("hi").to_value(), Value::String("hi".into()));
274 assert_eq!(true.to_value(), Value::Bool(true));
275 assert_eq!(false.to_value(), Value::Bool(false));
276 }
277
278 #[test]
279 fn dv1_duplicate_insert_last_write_wins_in_place() {
280 let mut o = Object::new();
281 o.insert("a", Value::I64(1));
282 o.insert("b", Value::I64(2));
283 o.insert("a", Value::I64(3));
284 assert_eq!(o.get("a"), Some(&Value::I64(3)), "last write wins");
285 assert_eq!(o.entries.len(), 2, "no duplicate entry appended");
286 let keys: Vec<&str> = o.iter().map(|(k, _)| k.as_ref()).collect();
287 assert_eq!(keys, vec!["a", "b"], "original position kept");
288 }
289
290 #[test]
291 fn dv1_ordered_entries_preserved() {
292 let mut o = Object::new();
293 o.insert("b", Value::I64(1));
294 o.insert("a", Value::I64(2));
295 o.insert("c", Value::I64(3));
296 let keys: Vec<&str> = o.iter().map(|(k, _)| k.as_ref()).collect();
297 assert_eq!(keys, vec!["b", "a", "c"]);
298 }
299
300 #[test]
301 fn dv1_number_fidelity_i64_max_and_f64() {
302 let v = Value::I64(i64::MAX);
303 assert_eq!(v.as_i64(), Some(i64::MAX));
304 let f = Value::F64(0.1);
305 assert_eq!(f.as_f64(), Some(0.1));
306 assert_eq!(f.as_i64(), None, "strict: no silent F64-to-I64 cast");
307 }
308
309 #[test]
310 fn accessor_strictness_no_silent_casts() {
311 assert_eq!(Value::String("x".into()).as_str(), Some("x"));
312 assert_eq!(Value::Bool(true).as_bool(), Some(true));
313 assert_eq!(Value::Null.as_str(), None);
314 assert_eq!(Value::F64(1.0).as_i64(), None);
315 assert_eq!(Value::I64(1).as_f64(), None, "strict: no silent I64-to-F64");
316 }
317
318 #[test]
319 fn from_impls_build_expected_variants() {
320 assert_eq!(Value::from("hi"), Value::String("hi".into()));
321 assert_eq!(Value::from(String::from("hi")), Value::String("hi".into()));
322 assert_eq!(Value::from(true), Value::Bool(true));
323 assert_eq!(Value::from(7i64), Value::I64(7));
324 assert_eq!(Value::from(0.5f64), Value::F64(0.5));
325 }
326}
327
328#[cfg(feature = "serde")]
330pub mod json;