1#[derive(Clone, Debug, Default, PartialEq)]
28pub enum Value {
29 #[default]
30 Null,
31 Bool(bool),
32 Int(i64),
33 Float(f64),
34 Str(String),
35 List(Vec<Value>),
36 Map(Vec<(String, Value)>),
37}
38
39impl Value {
40 pub fn str(s: impl Into<String>) -> Value {
42 Value::Str(s.into())
43 }
44
45 pub fn map(entries: impl IntoIterator<Item = (&'static str, Value)>) -> Value {
47 Value::Map(
48 entries
49 .into_iter()
50 .map(|(k, v)| (k.to_string(), v))
51 .collect(),
52 )
53 }
54
55 pub fn get(&self, key: &str) -> Option<&Value> {
58 match self {
59 Value::Map(entries) => entries.iter().find(|(k, _)| k == key).map(|(_, v)| v),
60 _ => None,
61 }
62 }
63
64 pub fn as_str(&self) -> Option<&str> {
65 match self {
66 Value::Str(s) => Some(s),
67 _ => None,
68 }
69 }
70
71 pub fn as_int(&self) -> Option<i64> {
73 match self {
74 Value::Int(i) => Some(*i),
75 Value::Float(f) => Some(*f as i64),
76 _ => None,
77 }
78 }
79
80 pub fn as_list(&self) -> Option<&[Value]> {
81 match self {
82 Value::List(items) => Some(items),
83 _ => None,
84 }
85 }
86
87 pub fn as_float(&self) -> Option<f64> {
89 match self {
90 Value::Float(f) => Some(*f),
91 Value::Int(i) => Some(*i as f64),
92 _ => None,
93 }
94 }
95
96 pub fn type_name(&self) -> &'static str {
98 match self {
99 Value::Null => "nil",
100 Value::Bool(_) => "boolean",
101 Value::Int(_) | Value::Float(_) => "number",
102 Value::Str(_) => "string",
103 Value::List(_) => "list",
104 Value::Map(_) => "map",
105 }
106 }
107
108 pub fn as_bool(&self) -> Option<bool> {
109 match self {
110 Value::Bool(b) => Some(*b),
111 _ => None,
112 }
113 }
114
115 #[inline]
117 pub fn get_str(&self, key: &str) -> Option<&str> {
118 self.get(key).and_then(Value::as_str)
119 }
120
121 #[inline]
123 pub fn get_int(&self, key: &str) -> Option<i64> {
124 self.get(key).and_then(Value::as_int)
125 }
126
127 #[inline]
129 pub fn get_float(&self, key: &str) -> Option<f64> {
130 self.get(key).and_then(Value::as_float)
131 }
132
133 #[inline]
136 pub fn get_f32(&self, key: &str) -> Option<f32> {
137 self.get_float(key).map(|v| v as f32)
138 }
139
140 #[inline]
142 pub fn get_bool(&self, key: &str) -> Option<bool> {
143 self.get(key).and_then(Value::as_bool)
144 }
145}
146
147#[derive(Clone, Copy)]
152pub struct Handles {
153 pub key: fn(crate::key::Key) -> Value,
154 pub id: fn(u64) -> Value,
155}
156
157impl Handles {
158 pub const INT: Handles = Handles {
161 key: |k| Value::Int(k.0 as i64),
162 id: |id| Value::Int(id as i64),
163 };
164 pub const HEX: Handles = Handles {
166 key: |k| Value::Str(format!("{:016x}", k.0)),
167 id: |id| Value::Str(format!("{id:016x}")),
168 };
169
170 pub fn opt_key(&self, k: Option<crate::key::Key>) -> Value {
171 k.map_or(Value::Null, self.key)
172 }
173}
174
175impl Value {
176 pub fn float(v: f32) -> Value {
178 Value::Float(v as f64)
179 }
180
181 pub fn opt<T>(v: Option<T>, f: impl FnOnce(T) -> Value) -> Value {
184 v.map_or(Value::Null, f)
185 }
186
187 pub fn opt_str(s: &Option<String>) -> Value {
188 Value::opt(s.as_ref(), |s| Value::Str(s.clone()))
189 }
190
191 pub fn opt_float(v: Option<f32>) -> Value {
192 Value::opt(v, Value::float)
193 }
194
195 pub fn opt_usize(v: Option<usize>) -> Value {
196 Value::opt(v, |v| Value::Int(v as i64))
197 }
198
199 pub fn opt_bool(v: Option<bool>) -> Value {
200 Value::opt(v, Value::Bool)
201 }
202
203 pub fn list(items: impl IntoIterator<Item = Value>) -> Value {
204 Value::List(items.into_iter().collect())
205 }
206
207 pub fn floats(items: &[f32]) -> Value {
208 Value::list(items.iter().map(|v| Value::float(*v)))
209 }
210}
211
212impl From<&str> for Value {
213 fn from(s: &str) -> Self {
214 Value::Str(s.to_string())
215 }
216}
217
218impl From<String> for Value {
219 fn from(s: String) -> Self {
220 Value::Str(s)
221 }
222}
223
224impl From<i64> for Value {
225 fn from(v: i64) -> Self {
226 Value::Int(v)
227 }
228}
229
230impl From<i32> for Value {
231 fn from(v: i32) -> Self {
232 Value::Int(v.into())
233 }
234}
235
236impl From<u32> for Value {
237 fn from(v: u32) -> Self {
238 Value::Int(v.into())
239 }
240}
241
242impl From<usize> for Value {
245 fn from(v: usize) -> Self {
246 Value::Int(i64::try_from(v).unwrap_or(i64::MAX))
247 }
248}
249
250impl From<f32> for Value {
251 fn from(v: f32) -> Self {
252 Value::float(v)
253 }
254}
255
256impl From<f64> for Value {
257 fn from(v: f64) -> Self {
258 Value::Float(v)
259 }
260}
261
262impl From<bool> for Value {
263 fn from(v: bool) -> Self {
264 Value::Bool(v)
265 }
266}
267
268#[cfg(test)]
269mod tests {
270 use super::*;
271
272 #[test]
273 fn map_get_finds_entries() {
274 let v = Value::map([("kind", Value::str("inc")), ("by", Value::Int(2))]);
275 assert_eq!(v.get("kind").and_then(Value::as_str), Some("inc"));
276 assert_eq!(v.get("by").and_then(Value::as_int), Some(2));
277 assert_eq!(v.get("missing"), None);
278 }
279
280 #[test]
281 fn numeric_coercions() {
282 assert_eq!(Value::Int(3).as_float(), Some(3.0));
283 assert_eq!(Value::Float(3.7).as_int(), Some(3));
284 assert_eq!(Value::str("x").as_int(), None);
285 }
286}