1#![allow(clippy::inherent_to_string)] use serde::{Deserialize, Serialize};
40use serde_json::{Value as SerdeJsonValue, json};
41use std::collections::BTreeMap;
42use std::convert::TryFrom;
43
44pub type Children = BTreeMap<String, NodeData>;
51
52#[derive(Clone, Serialize, Deserialize, Debug, PartialEq)]
65pub struct NodeData {
66 pub value: Value,
68 pub updated_at: f64,
70}
71
72impl Default for NodeData {
73 fn default() -> Self {
74 Self {
75 value: Value::Null,
76 updated_at: 0.0,
77 }
78 }
79}
80
81#[derive(Clone, Serialize, Deserialize, Debug, PartialEq)]
104pub enum Value {
105 Null,
107 Bit(bool),
109 Number(f64),
111 Text(String),
113 Link(String),
115}
116
117impl Value {
118 pub fn size(&self) -> usize {
125 match self {
126 Value::Text(s) => s.len(),
127 _ => std::mem::size_of_val(self),
128 }
129 }
130
131 pub fn to_string(&self) -> String {
146 match self {
147 Value::Null => "null".to_string(),
148 Value::Bit(bool) => {
149 if *bool {
150 "true".to_string()
151 } else {
152 "false".to_string()
153 }
154 }
155 Value::Number(n) => n.to_string(),
156 Value::Text(t) => t.clone(),
157 Value::Link(l) => l.clone(),
158 }
159 }
160
161 pub fn is_null(&self) -> bool {
163 matches!(self, Value::Null)
164 }
165
166 pub fn is_bit(&self) -> bool {
168 matches!(self, Value::Bit(_))
169 }
170
171 pub fn is_number(&self) -> bool {
173 matches!(self, Value::Number(_))
174 }
175
176 pub fn is_text(&self) -> bool {
178 matches!(self, Value::Text(_))
179 }
180
181 pub fn is_link(&self) -> bool {
183 matches!(self, Value::Link(_))
184 }
185
186 pub fn as_bit(&self) -> Option<bool> {
188 match self {
189 Value::Bit(b) => Some(*b),
190 _ => None,
191 }
192 }
193
194 pub fn as_number(&self) -> Option<f64> {
196 match self {
197 Value::Number(n) => Some(*n),
198 _ => None,
199 }
200 }
201
202 pub fn as_text(&self) -> Option<&str> {
204 match self {
205 Value::Text(t) => Some(t.as_str()),
206 _ => None,
207 }
208 }
209
210 pub fn as_link(&self) -> Option<&str> {
212 match self {
213 Value::Link(l) => Some(l.as_str()),
214 _ => None,
215 }
216 }
217}
218
219impl TryFrom<SerdeJsonValue> for Value {
239 type Error = &'static str;
240
241 fn try_from(v: SerdeJsonValue) -> Result<Value, Self::Error> {
242 match v {
243 SerdeJsonValue::Null => Ok(Value::Null),
244 SerdeJsonValue::Bool(b) => Ok(Value::Bit(b)),
245 SerdeJsonValue::String(s) => Ok(Value::Text(s)),
246 SerdeJsonValue::Number(n) => match n.as_f64() {
247 Some(n) => Ok(Value::Number(n)),
248 _ => Err("not convertible to f64"),
249 },
250 SerdeJsonValue::Object(obj) => {
251 if let Some(soul) = obj.get("#").and_then(|v| v.as_str()) {
253 Ok(Value::Link(soul.to_string()))
254 } else {
255 Err("cannot convert json object into Value")
256 }
257 }
258 SerdeJsonValue::Array(_) => Err("cannot convert array into Value"),
259 }
260 }
261}
262
263impl From<Value> for SerdeJsonValue {
269 fn from(v: Value) -> SerdeJsonValue {
270 match v {
271 Value::Null => SerdeJsonValue::Null,
272 Value::Text(t) => SerdeJsonValue::String(t),
273 Value::Bit(b) => SerdeJsonValue::Bool(b),
274 Value::Number(n) => json!(n),
275 Value::Link(l) => json!({ "#": l }),
276 }
277 }
278}
279
280impl From<usize> for Value {
281 fn from(n: usize) -> Value {
282 Value::Number(n as f64)
283 }
284}
285
286impl From<f32> for Value {
287 fn from(n: f32) -> Value {
288 Value::Number(n as f64)
289 }
290}
291
292impl From<u64> for Value {
293 fn from(n: u64) -> Value {
294 Value::Number(n as f64)
295 }
296}
297
298impl From<&str> for Value {
299 fn from(s: &str) -> Value {
300 Value::Text(s.to_string())
301 }
302}
303
304impl From<String> for Value {
305 fn from(s: String) -> Value {
306 Value::Text(s)
307 }
308}
309
310#[cfg(test)]
311mod tests {
312 use super::*;
313
314 #[test]
317 fn test_nodedata_default() {
318 let nd = NodeData::default();
319 assert!(nd.value.is_null());
320 assert_eq!(nd.updated_at, 0.0);
321 }
322
323 #[test]
324 fn test_nodedata_partial_eq() {
325 let a = NodeData {
326 value: Value::Text("x".into()),
327 updated_at: 1.0,
328 };
329 let b = NodeData {
330 value: Value::Text("x".into()),
331 updated_at: 1.0,
332 };
333 let c = NodeData {
334 value: Value::Text("y".into()),
335 updated_at: 1.0,
336 };
337 assert_eq!(a, b);
338 assert_ne!(a, c);
339 }
340
341 #[test]
344 fn test_value_size_text() {
345 assert_eq!(Value::Text("hello".into()).size(), 5);
346 assert_eq!(Value::Text("".into()).size(), 0);
347 assert_eq!(Value::Text("héllo".into()).size(), 6); }
349
350 #[test]
351 fn test_value_size_non_text() {
352 assert!(Value::Null.size() > 0);
354 assert!(Value::Bit(true).size() > 0);
355 assert!(Value::Number(42.0).size() > 0);
356 assert!(Value::Link("soul".into()).size() > 0);
357 }
358
359 #[test]
362 fn test_value_to_string_null() {
363 assert_eq!(Value::Null.to_string(), "null");
364 }
365
366 #[test]
367 fn test_value_to_string_bit() {
368 assert_eq!(Value::Bit(true).to_string(), "true");
369 assert_eq!(Value::Bit(false).to_string(), "false");
370 }
371
372 #[test]
373 fn test_value_to_string_number() {
374 assert_eq!(Value::Number(42.0).to_string(), "42");
375 assert_eq!(Value::Number(3.15).to_string(), "3.15");
376 }
377
378 #[test]
379 fn test_value_to_string_text() {
380 assert_eq!(Value::Text("hello".into()).to_string(), "hello");
381 }
382
383 #[test]
384 fn test_value_to_string_link() {
385 assert_eq!(Value::Link("node/abc".into()).to_string(), "node/abc");
386 }
387
388 #[test]
391 fn test_value_is_null() {
392 assert!(Value::Null.is_null());
393 assert!(!Value::Bit(false).is_null());
394 }
395
396 #[test]
397 fn test_value_is_bit() {
398 assert!(Value::Bit(true).is_bit());
399 assert!(!Value::Null.is_bit());
400 }
401
402 #[test]
403 fn test_value_is_number() {
404 assert!(Value::Number(1.0).is_number());
405 assert!(!Value::Null.is_number());
406 }
407
408 #[test]
409 fn test_value_is_text() {
410 assert!(Value::Text("x".into()).is_text());
411 assert!(!Value::Null.is_text());
412 }
413
414 #[test]
415 fn test_value_is_link() {
416 assert!(Value::Link("soul".into()).is_link());
417 assert!(!Value::Null.is_link());
418 }
419
420 #[test]
423 fn test_value_as_bit() {
424 assert_eq!(Value::Bit(true).as_bit(), Some(true));
425 assert_eq!(Value::Bit(false).as_bit(), Some(false));
426 assert_eq!(Value::Null.as_bit(), None);
427 }
428
429 #[test]
430 fn test_value_as_number() {
431 assert_eq!(Value::Number(42.0).as_number(), Some(42.0));
432 assert_eq!(Value::Null.as_number(), None);
433 }
434
435 #[test]
436 fn test_value_as_text() {
437 assert_eq!(Value::Text("hello".into()).as_text(), Some("hello"));
438 assert_eq!(Value::Null.as_text(), None);
439 }
440
441 #[test]
442 fn test_value_as_link() {
443 assert_eq!(Value::Link("soul".into()).as_link(), Some("soul"));
444 assert_eq!(Value::Null.as_link(), None);
445 }
446
447 #[test]
450 fn test_try_from_json_null() {
451 let v = Value::try_from(SerdeJsonValue::Null).unwrap();
452 assert!(v.is_null());
453 }
454
455 #[test]
456 fn test_try_from_json_bool() {
457 let v = Value::try_from(SerdeJsonValue::Bool(true)).unwrap();
458 assert_eq!(v.as_bit(), Some(true));
459 }
460
461 #[test]
462 fn test_try_from_json_string() {
463 let v = Value::try_from(SerdeJsonValue::String("hello".into())).unwrap();
464 assert_eq!(v.as_text(), Some("hello"));
465 }
466
467 #[test]
468 fn test_try_from_json_number() {
469 let v = Value::try_from(serde_json::json!(42.0)).unwrap();
470 assert_eq!(v.as_number(), Some(42.0));
471 }
472
473 #[test]
474 fn test_try_from_json_link() {
475 let json = serde_json::json!({ "#": "node/abc" });
476 let v = Value::try_from(json).unwrap();
477 assert_eq!(v.as_link(), Some("node/abc"));
478 }
479
480 #[test]
481 fn test_try_from_json_array_fails() {
482 let json = serde_json::json!([1, 2, 3]);
483 assert!(Value::try_from(json).is_err());
484 }
485
486 #[test]
487 fn test_try_from_json_object_without_soul_fails() {
488 let json = serde_json::json!({ "foo": "bar" });
489 assert!(Value::try_from(json).is_err());
490 }
491
492 #[test]
495 fn test_roundtrip_null() {
496 let v = Value::Null;
497 let json: SerdeJsonValue = v.clone().into();
498 let v2 = Value::try_from(json).unwrap();
499 assert_eq!(v, v2);
500 }
501
502 #[test]
503 fn test_roundtrip_bit() {
504 let v = Value::Bit(true);
505 let json: SerdeJsonValue = v.clone().into();
506 let v2 = Value::try_from(json).unwrap();
507 assert_eq!(v, v2);
508 }
509
510 #[test]
511 fn test_roundtrip_text() {
512 let v = Value::Text("hello world".into());
513 let json: SerdeJsonValue = v.clone().into();
514 let v2 = Value::try_from(json).unwrap();
515 assert_eq!(v, v2);
516 }
517
518 #[test]
519 fn test_roundtrip_number() {
520 let v = Value::Number(42.0);
521 let json: SerdeJsonValue = v.clone().into();
522 let v2 = Value::try_from(json).unwrap();
523 assert_eq!(v, v2);
524 }
525
526 #[test]
527 fn test_roundtrip_link() {
528 let v = Value::Link("node/abc".into());
529 let json: SerdeJsonValue = v.clone().into();
530 let v2 = Value::try_from(json).unwrap();
531 assert_eq!(v, v2);
532 }
533
534 #[test]
537 fn test_from_str() {
538 let v: Value = "hello".into();
539 assert_eq!(v.as_text(), Some("hello"));
540 }
541
542 #[test]
543 fn test_from_string() {
544 let v: Value = String::from("world").into();
545 assert_eq!(v.as_text(), Some("world"));
546 }
547
548 #[test]
551 fn test_from_usize() {
552 let v: Value = 42usize.into();
553 assert_eq!(v.as_number(), Some(42.0));
554 }
555
556 #[test]
557 fn test_from_u64() {
558 let v: Value = 99u64.into();
559 assert_eq!(v.as_number(), Some(99.0));
560 }
561
562 #[test]
563 fn test_from_f32() {
564 let v: Value = 3.15f32.into();
565 assert!((v.as_number().unwrap() - 3.15).abs() < 0.001);
566 }
567
568 #[test]
571 fn test_children_btreemap() {
572 let mut children: Children = BTreeMap::new();
573 children.insert(
574 "key1".to_string(),
575 NodeData {
576 value: Value::Text("v1".into()),
577 updated_at: 1.0,
578 },
579 );
580 children.insert(
581 "key2".to_string(),
582 NodeData {
583 value: Value::Text("v2".into()),
584 updated_at: 2.0,
585 },
586 );
587 let keys: Vec<&String> = children.keys().collect();
589 assert_eq!(keys, vec!["key1", "key2"]);
590 }
591}