1#[derive(Clone, Debug, Default, PartialEq)]
6pub enum Value {
7 #[default]
8 Null,
9 Bool(bool),
10 Int(i64),
11 Float(f64),
12 Str(String),
13 List(Vec<Value>),
14 Map(Vec<(String, Value)>),
15}
16
17impl Value {
18 pub fn str(s: impl Into<String>) -> Value {
19 Value::Str(s.into())
20 }
21
22 pub fn map(entries: impl IntoIterator<Item = (&'static str, Value)>) -> Value {
23 Value::Map(
24 entries
25 .into_iter()
26 .map(|(k, v)| (k.to_string(), v))
27 .collect(),
28 )
29 }
30
31 pub fn get(&self, key: &str) -> Option<&Value> {
32 match self {
33 Value::Map(entries) => entries.iter().find(|(k, _)| k == key).map(|(_, v)| v),
34 _ => None,
35 }
36 }
37
38 pub fn as_str(&self) -> Option<&str> {
39 match self {
40 Value::Str(s) => Some(s),
41 _ => None,
42 }
43 }
44
45 pub fn as_int(&self) -> Option<i64> {
46 match self {
47 Value::Int(i) => Some(*i),
48 Value::Float(f) => Some(*f as i64),
49 _ => None,
50 }
51 }
52
53 pub fn as_list(&self) -> Option<&[Value]> {
54 match self {
55 Value::List(items) => Some(items),
56 _ => None,
57 }
58 }
59
60 pub fn as_float(&self) -> Option<f64> {
61 match self {
62 Value::Float(f) => Some(*f),
63 Value::Int(i) => Some(*i as f64),
64 _ => None,
65 }
66 }
67
68 pub fn type_name(&self) -> &'static str {
70 match self {
71 Value::Null => "nil",
72 Value::Bool(_) => "boolean",
73 Value::Int(_) | Value::Float(_) => "number",
74 Value::Str(_) => "string",
75 Value::List(_) => "list",
76 Value::Map(_) => "map",
77 }
78 }
79
80 pub fn as_bool(&self) -> Option<bool> {
81 match self {
82 Value::Bool(b) => Some(*b),
83 _ => None,
84 }
85 }
86
87 #[inline]
89 pub fn get_str(&self, key: &str) -> Option<&str> {
90 self.get(key).and_then(Value::as_str)
91 }
92
93 #[inline]
95 pub fn get_int(&self, key: &str) -> Option<i64> {
96 self.get(key).and_then(Value::as_int)
97 }
98
99 #[inline]
101 pub fn get_float(&self, key: &str) -> Option<f64> {
102 self.get(key).and_then(Value::as_float)
103 }
104
105 #[inline]
108 pub fn get_f32(&self, key: &str) -> Option<f32> {
109 self.get_float(key).map(|v| v as f32)
110 }
111
112 #[inline]
114 pub fn get_bool(&self, key: &str) -> Option<bool> {
115 self.get(key).and_then(Value::as_bool)
116 }
117}
118
119#[derive(Clone, Copy)]
127pub struct Handles {
128 pub key: fn(crate::key::Key) -> Value,
129 pub id: fn(u64) -> Value,
130}
131
132impl Handles {
133 pub const INT: Handles = Handles {
136 key: |k| Value::Int(k.0 as i64),
137 id: |id| Value::Int(id as i64),
138 };
139 pub const HEX: Handles = Handles {
141 key: |k| Value::Str(format!("{:016x}", k.0)),
142 id: |id| Value::Str(format!("{id:016x}")),
143 };
144
145 pub fn opt_key(&self, k: Option<crate::key::Key>) -> Value {
146 k.map_or(Value::Null, self.key)
147 }
148}
149
150impl Value {
151 pub fn float(v: f32) -> Value {
153 Value::Float(v as f64)
154 }
155
156 pub fn opt<T>(v: Option<T>, f: impl FnOnce(T) -> Value) -> Value {
159 v.map_or(Value::Null, f)
160 }
161
162 pub fn opt_str(s: &Option<String>) -> Value {
163 Value::opt(s.as_ref(), |s| Value::Str(s.clone()))
164 }
165
166 pub fn opt_float(v: Option<f32>) -> Value {
167 Value::opt(v, Value::float)
168 }
169
170 pub fn opt_usize(v: Option<usize>) -> Value {
171 Value::opt(v, |v| Value::Int(v as i64))
172 }
173
174 pub fn opt_bool(v: Option<bool>) -> Value {
175 Value::opt(v, Value::Bool)
176 }
177
178 pub fn list(items: impl IntoIterator<Item = Value>) -> Value {
179 Value::List(items.into_iter().collect())
180 }
181
182 pub fn floats(items: &[f32]) -> Value {
183 Value::list(items.iter().map(|v| Value::float(*v)))
184 }
185}
186
187impl From<&str> for Value {
188 fn from(s: &str) -> Self {
189 Value::Str(s.to_string())
190 }
191}
192
193impl From<String> for Value {
194 fn from(s: String) -> Self {
195 Value::Str(s)
196 }
197}
198
199impl From<i64> for Value {
200 fn from(v: i64) -> Self {
201 Value::Int(v)
202 }
203}
204
205impl From<i32> for Value {
206 fn from(v: i32) -> Self {
207 Value::Int(v.into())
208 }
209}
210
211impl From<u32> for Value {
212 fn from(v: u32) -> Self {
213 Value::Int(v.into())
214 }
215}
216
217impl From<usize> for Value {
220 fn from(v: usize) -> Self {
221 Value::Int(i64::try_from(v).unwrap_or(i64::MAX))
222 }
223}
224
225impl From<f32> for Value {
226 fn from(v: f32) -> Self {
227 Value::float(v)
228 }
229}
230
231impl From<f64> for Value {
232 fn from(v: f64) -> Self {
233 Value::Float(v)
234 }
235}
236
237impl From<bool> for Value {
238 fn from(v: bool) -> Self {
239 Value::Bool(v)
240 }
241}
242
243#[cfg(test)]
244mod tests {
245 use super::*;
246
247 #[test]
248 fn map_get_finds_entries() {
249 let v = Value::map([("kind", Value::str("inc")), ("by", Value::Int(2))]);
250 assert_eq!(v.get("kind").and_then(Value::as_str), Some("inc"));
251 assert_eq!(v.get("by").and_then(Value::as_int), Some(2));
252 assert_eq!(v.get("missing"), None);
253 }
254
255 #[test]
256 fn numeric_coercions() {
257 assert_eq!(Value::Int(3).as_float(), Some(3.0));
258 assert_eq!(Value::Float(3.7).as_int(), Some(3));
259 assert_eq!(Value::str("x").as_int(), None);
260 }
261}