1pub mod json;
33#[cfg(feature = "lua")]
34pub mod lua;
35pub mod message;
36
37pub use message::{MessageError, MessageField};
38
39#[cfg(feature = "derive")]
43pub use kui_derive::Message;
44
45#[derive(Debug, Default, PartialEq)]
48pub enum Value {
49 #[default]
50 Null,
51 Bool(bool),
52 Int(i64),
53 Float(f64),
54 Str(String),
55 List(Vec<Value>),
56 Map(Vec<(String, Value)>),
57}
58
59impl Clone for Value {
60 fn clone(&self) -> Self {
61 match self {
62 Value::Null => Value::Null,
63 Value::Bool(b) => Value::Bool(*b),
64 Value::Int(i) => Value::Int(*i),
65 Value::Float(f) => Value::Float(*f),
66 Value::Str(s) => Value::Str(s.clone()),
67 Value::List(items) => Value::List(items.clone()),
68 Value::Map(entries) => Value::Map(entries.clone()),
69 }
70 }
71
72 fn clone_from(&mut self, source: &Self) {
78 match (self, source) {
79 (Value::Str(a), Value::Str(b)) => a.clone_from(b),
80 (Value::List(a), Value::List(b)) => a.clone_from(b),
81 (Value::Map(a), Value::Map(b)) => {
82 a.truncate(b.len());
85 for ((ka, va), (kb, vb)) in a.iter_mut().zip(b) {
86 ka.clone_from(kb);
87 va.clone_from(vb);
88 }
89 let have = a.len();
90 a.extend(b[have..].iter().cloned());
91 }
92 (this, source) => *this = source.clone(),
93 }
94 }
95}
96
97impl Value {
98 #[inline]
101 pub fn owns_buffers(&self) -> bool {
102 matches!(self, Value::Str(_) | Value::List(_) | Value::Map(_))
103 }
104
105 pub fn str(s: impl Into<String>) -> Value {
107 Value::Str(s.into())
108 }
109
110 pub fn map(entries: impl IntoIterator<Item = (&'static str, Value)>) -> Value {
112 Value::Map(
113 entries
114 .into_iter()
115 .map(|(k, v)| (k.to_string(), v))
116 .collect(),
117 )
118 }
119
120 pub fn get(&self, key: &str) -> Option<&Value> {
123 match self {
124 Value::Map(entries) => entries.iter().find(|(k, _)| k == key).map(|(_, v)| v),
125 _ => None,
126 }
127 }
128
129 pub fn as_str(&self) -> Option<&str> {
130 match self {
131 Value::Str(s) => Some(s),
132 _ => None,
133 }
134 }
135
136 pub fn as_int(&self) -> Option<i64> {
138 match self {
139 Value::Int(i) => Some(*i),
140 Value::Float(f) => Some(*f as i64),
141 _ => None,
142 }
143 }
144
145 pub fn as_list(&self) -> Option<&[Value]> {
146 match self {
147 Value::List(items) => Some(items),
148 _ => None,
149 }
150 }
151
152 pub fn as_float(&self) -> Option<f64> {
154 match self {
155 Value::Float(f) => Some(*f),
156 Value::Int(i) => Some(*i as f64),
157 _ => None,
158 }
159 }
160
161 pub fn type_name(&self) -> &'static str {
163 match self {
164 Value::Null => "nil",
165 Value::Bool(_) => "boolean",
166 Value::Int(_) | Value::Float(_) => "number",
167 Value::Str(_) => "string",
168 Value::List(_) => "list",
169 Value::Map(_) => "map",
170 }
171 }
172
173 pub fn as_bool(&self) -> Option<bool> {
174 match self {
175 Value::Bool(b) => Some(*b),
176 _ => None,
177 }
178 }
179
180 #[inline]
182 pub fn get_str(&self, key: &str) -> Option<&str> {
183 self.get(key).and_then(Value::as_str)
184 }
185
186 #[inline]
188 pub fn get_int(&self, key: &str) -> Option<i64> {
189 self.get(key).and_then(Value::as_int)
190 }
191
192 #[inline]
194 pub fn get_float(&self, key: &str) -> Option<f64> {
195 self.get(key).and_then(Value::as_float)
196 }
197
198 #[inline]
201 pub fn get_f32(&self, key: &str) -> Option<f32> {
202 self.get_float(key).map(|v| v as f32)
203 }
204
205 #[inline]
207 pub fn get_bool(&self, key: &str) -> Option<bool> {
208 self.get(key).and_then(Value::as_bool)
209 }
210}
211
212impl Value {
213 pub fn float(v: f32) -> Value {
215 Value::Float(v as f64)
216 }
217
218 pub fn opt<T>(v: Option<T>, f: impl FnOnce(T) -> Value) -> Value {
221 v.map_or(Value::Null, f)
222 }
223
224 pub fn opt_str(s: &Option<String>) -> Value {
225 Value::opt(s.as_ref(), |s| Value::Str(s.clone()))
226 }
227
228 pub fn opt_float(v: Option<f32>) -> Value {
229 Value::opt(v, Value::float)
230 }
231
232 pub fn opt_usize(v: Option<usize>) -> Value {
233 Value::opt(v, |v| Value::Int(v as i64))
234 }
235
236 pub fn opt_bool(v: Option<bool>) -> Value {
237 Value::opt(v, Value::Bool)
238 }
239
240 pub fn list(items: impl IntoIterator<Item = Value>) -> Value {
241 Value::List(items.into_iter().collect())
242 }
243
244 pub fn floats(items: &[f32]) -> Value {
245 Value::list(items.iter().map(|v| Value::float(*v)))
246 }
247}
248
249impl TryFrom<&Value> for String {
253 type Error = ();
254
255 fn try_from(v: &Value) -> Result<Self, ()> {
256 v.as_str().map(str::to_string).ok_or(())
257 }
258}
259
260impl From<&Value> for Value {
263 fn from(v: &Value) -> Self {
264 v.clone()
265 }
266}
267
268impl From<&str> for Value {
269 fn from(s: &str) -> Self {
270 Value::Str(s.to_string())
271 }
272}
273
274impl From<String> for Value {
275 fn from(s: String) -> Self {
276 Value::Str(s)
277 }
278}
279
280impl From<i64> for Value {
281 fn from(v: i64) -> Self {
282 Value::Int(v)
283 }
284}
285
286impl From<i32> for Value {
287 fn from(v: i32) -> Self {
288 Value::Int(v.into())
289 }
290}
291
292impl From<u32> for Value {
293 fn from(v: u32) -> Self {
294 Value::Int(v.into())
295 }
296}
297
298impl From<usize> for Value {
301 fn from(v: usize) -> Self {
302 Value::Int(i64::try_from(v).unwrap_or(i64::MAX))
303 }
304}
305
306impl From<f32> for Value {
307 fn from(v: f32) -> Self {
308 Value::float(v)
309 }
310}
311
312impl From<f64> for Value {
313 fn from(v: f64) -> Self {
314 Value::Float(v)
315 }
316}
317
318impl From<bool> for Value {
319 fn from(v: bool) -> Self {
320 Value::Bool(v)
321 }
322}
323
324#[cfg(test)]
325mod tests {
326 use super::*;
327
328 #[test]
329 fn map_get_finds_entries() {
330 let v = Value::map([("kind", Value::str("inc")), ("by", Value::Int(2))]);
331 assert_eq!(v.get("kind").and_then(Value::as_str), Some("inc"));
332 assert_eq!(v.get("by").and_then(Value::as_int), Some(2));
333 assert_eq!(v.get("missing"), None);
334 }
335
336 #[test]
337 fn clone_from_is_a_clone_whatever_it_overwrites() {
338 let shapes = [
339 Value::Null,
340 Value::Bool(true),
341 Value::Int(-3),
342 Value::Float(2.5),
343 Value::str("row"),
344 Value::str(""),
345 Value::List(vec![Value::Int(1), Value::str("two")]),
346 Value::List(Vec::new()),
347 Value::map([("kind", Value::str("row")), ("line", Value::Int(7))]),
348 Value::map([
349 ("kind", Value::str("a longer kind")),
350 ("x", Value::Float(1.0)),
351 ]),
352 Value::map([("only", Value::Null)]),
353 Value::map([
354 ("kind", Value::str("row")),
355 ("line", Value::Int(7)),
356 (
357 "nested",
358 Value::map([("deep", Value::List(vec![Value::Bool(false)]))]),
359 ),
360 ]),
361 ];
362 for into in &shapes {
363 for from in &shapes {
364 let mut v = into.clone();
365 v.clone_from(from);
366 assert_eq!(&v, from, "{from:?} written over {into:?}");
367 }
368 }
369 }
370
371 #[test]
372 fn clone_from_keeps_the_buffers_of_a_value_like_it() {
373 let mut v = Value::map([("kind", Value::str("row")), ("line", Value::Int(1))]);
374 let Value::Map(entries) = &v else {
375 unreachable!()
376 };
377 let (keys, text) = (entries.as_ptr(), entries[0].0.as_ptr());
378 v.clone_from(&Value::map([
379 ("kind", Value::str("cut")),
380 ("line", Value::Int(2)),
381 ]));
382 let Value::Map(entries) = &v else {
383 unreachable!()
384 };
385 assert_eq!((entries.as_ptr(), entries[0].0.as_ptr()), (keys, text));
386 assert_eq!(v.get_str("kind"), Some("cut"));
387 }
388
389 #[test]
390 fn numeric_coercions() {
391 assert_eq!(Value::Int(3).as_float(), Some(3.0));
392 assert_eq!(Value::Float(3.7).as_int(), Some(3));
393 assert_eq!(Value::str("x").as_int(), None);
394 }
395}