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 Value {
220 pub fn from_json_ref(v: &SerdeJsonValue) -> Result<Value, &'static str> {
230 match v {
231 SerdeJsonValue::Null => Ok(Value::Null),
232 SerdeJsonValue::Bool(b) => Ok(Value::Bit(*b)),
233 SerdeJsonValue::String(s) => Ok(Value::Text(s.clone())),
234 SerdeJsonValue::Number(n) => match n.as_f64() {
235 Some(n) => Ok(Value::Number(n)),
236 _ => Err("not convertible to f64"),
237 },
238 SerdeJsonValue::Object(obj) => {
239 if let Some(soul) = obj.get("#").and_then(|v| v.as_str()) {
240 Ok(Value::Link(soul.to_string()))
241 } else {
242 Err("cannot convert json object into Value")
243 }
244 }
245 SerdeJsonValue::Array(_) => Err("cannot convert array into Value"),
246 }
247 }
248}
249impl TryFrom<SerdeJsonValue> for Value {
268 type Error = &'static str;
269
270 fn try_from(v: SerdeJsonValue) -> Result<Value, Self::Error> {
271 match v {
272 SerdeJsonValue::Null => Ok(Value::Null),
273 SerdeJsonValue::Bool(b) => Ok(Value::Bit(b)),
274 SerdeJsonValue::String(s) => Ok(Value::Text(s)),
275 SerdeJsonValue::Number(n) => match n.as_f64() {
276 Some(n) => Ok(Value::Number(n)),
277 _ => Err("not convertible to f64"),
278 },
279 SerdeJsonValue::Object(obj) => {
280 if let Some(soul) = obj.get("#").and_then(|v| v.as_str()) {
282 Ok(Value::Link(soul.to_string()))
283 } else {
284 Err("cannot convert json object into Value")
285 }
286 }
287 SerdeJsonValue::Array(_) => Err("cannot convert array into Value"),
288 }
289 }
290}
291
292impl TryFrom<&SerdeJsonValue> for Value {
304 type Error = &'static str;
305
306 fn try_from(v: &SerdeJsonValue) -> Result<Value, Self::Error> {
307 match v {
308 SerdeJsonValue::Null => Ok(Value::Null),
309 SerdeJsonValue::Bool(b) => Ok(Value::Bit(*b)),
310 SerdeJsonValue::String(s) => Ok(Value::Text(s.clone())),
311 SerdeJsonValue::Number(n) => match n.as_f64() {
312 Some(n) => Ok(Value::Number(n)),
313 _ => Err("not convertible to f64"),
314 },
315 SerdeJsonValue::Object(obj) => {
316 if let Some(soul) = obj.get("#").and_then(|v| v.as_str()) {
317 Ok(Value::Link(soul.to_string()))
318 } else {
319 Err("cannot convert json object into Value")
320 }
321 }
322 SerdeJsonValue::Array(_) => Err("cannot convert array into Value"),
323 }
324 }
325}
326
327impl From<Value> for SerdeJsonValue {
333 fn from(v: Value) -> SerdeJsonValue {
334 match v {
335 Value::Null => SerdeJsonValue::Null,
336 Value::Text(t) => SerdeJsonValue::String(t),
337 Value::Bit(b) => SerdeJsonValue::Bool(b),
338 Value::Number(n) => json!(n),
339 Value::Link(l) => json!({ "#": l }),
340 }
341 }
342}
343
344impl From<usize> for Value {
345 fn from(n: usize) -> Value {
346 Value::Number(n as f64)
347 }
348}
349
350impl From<f32> for Value {
351 fn from(n: f32) -> Value {
352 Value::Number(n as f64)
353 }
354}
355
356impl From<u64> for Value {
357 fn from(n: u64) -> Value {
358 Value::Number(n as f64)
359 }
360}
361
362impl From<&str> for Value {
363 fn from(s: &str) -> Value {
364 Value::Text(s.to_string())
365 }
366}
367
368impl From<String> for Value {
369 fn from(s: String) -> Value {
370 Value::Text(s)
371 }
372}
373
374#[cfg(test)]
375mod tests {
376 use super::*;
377
378 #[test]
381 fn test_nodedata_default() {
382 let nd = NodeData::default();
383 assert!(nd.value.is_null());
384 assert_eq!(nd.updated_at, 0.0);
385 }
386
387 #[test]
388 fn test_nodedata_partial_eq() {
389 let a = NodeData {
390 value: Value::Text("x".into()),
391 updated_at: 1.0,
392 };
393 let b = NodeData {
394 value: Value::Text("x".into()),
395 updated_at: 1.0,
396 };
397 let c = NodeData {
398 value: Value::Text("y".into()),
399 updated_at: 1.0,
400 };
401 assert_eq!(a, b);
402 assert_ne!(a, c);
403 }
404
405 #[test]
408 fn test_value_size_text() {
409 assert_eq!(Value::Text("hello".into()).size(), 5);
410 assert_eq!(Value::Text("".into()).size(), 0);
411 assert_eq!(Value::Text("héllo".into()).size(), 6); }
413
414 #[test]
415 fn test_value_size_non_text() {
416 assert!(Value::Null.size() > 0);
418 assert!(Value::Bit(true).size() > 0);
419 assert!(Value::Number(42.0).size() > 0);
420 assert!(Value::Link("soul".into()).size() > 0);
421 }
422
423 #[test]
426 fn test_value_to_string_null() {
427 assert_eq!(Value::Null.to_string(), "null");
428 }
429
430 #[test]
431 fn test_value_to_string_bit() {
432 assert_eq!(Value::Bit(true).to_string(), "true");
433 assert_eq!(Value::Bit(false).to_string(), "false");
434 }
435
436 #[test]
437 fn test_value_to_string_number() {
438 assert_eq!(Value::Number(42.0).to_string(), "42");
439 assert_eq!(Value::Number(3.15).to_string(), "3.15");
440 }
441
442 #[test]
443 fn test_value_to_string_text() {
444 assert_eq!(Value::Text("hello".into()).to_string(), "hello");
445 }
446
447 #[test]
448 fn test_value_to_string_link() {
449 assert_eq!(Value::Link("node/abc".into()).to_string(), "node/abc");
450 }
451
452 #[test]
455 fn test_value_is_null() {
456 assert!(Value::Null.is_null());
457 assert!(!Value::Bit(false).is_null());
458 }
459
460 #[test]
461 fn test_value_is_bit() {
462 assert!(Value::Bit(true).is_bit());
463 assert!(!Value::Null.is_bit());
464 }
465
466 #[test]
467 fn test_value_is_number() {
468 assert!(Value::Number(1.0).is_number());
469 assert!(!Value::Null.is_number());
470 }
471
472 #[test]
473 fn test_value_is_text() {
474 assert!(Value::Text("x".into()).is_text());
475 assert!(!Value::Null.is_text());
476 }
477
478 #[test]
479 fn test_value_is_link() {
480 assert!(Value::Link("soul".into()).is_link());
481 assert!(!Value::Null.is_link());
482 }
483
484 #[test]
487 fn test_value_as_bit() {
488 assert_eq!(Value::Bit(true).as_bit(), Some(true));
489 assert_eq!(Value::Bit(false).as_bit(), Some(false));
490 assert_eq!(Value::Null.as_bit(), None);
491 }
492
493 #[test]
494 fn test_value_as_number() {
495 assert_eq!(Value::Number(42.0).as_number(), Some(42.0));
496 assert_eq!(Value::Null.as_number(), None);
497 }
498
499 #[test]
500 fn test_value_as_text() {
501 assert_eq!(Value::Text("hello".into()).as_text(), Some("hello"));
502 assert_eq!(Value::Null.as_text(), None);
503 }
504
505 #[test]
506 fn test_value_as_link() {
507 assert_eq!(Value::Link("soul".into()).as_link(), Some("soul"));
508 assert_eq!(Value::Null.as_link(), None);
509 }
510
511 #[test]
514 fn test_try_from_json_null() {
515 let v = Value::try_from(SerdeJsonValue::Null).unwrap();
516 assert!(v.is_null());
517 }
518
519 #[test]
520 fn test_try_from_json_bool() {
521 let v = Value::try_from(SerdeJsonValue::Bool(true)).unwrap();
522 assert_eq!(v.as_bit(), Some(true));
523 }
524
525 #[test]
526 fn test_try_from_json_string() {
527 let v = Value::try_from(SerdeJsonValue::String("hello".into())).unwrap();
528 assert_eq!(v.as_text(), Some("hello"));
529 }
530
531 #[test]
532 fn test_try_from_json_number() {
533 let v = Value::try_from(serde_json::json!(42.0)).unwrap();
534 assert_eq!(v.as_number(), Some(42.0));
535 }
536
537 #[test]
538 fn test_try_from_json_link() {
539 let json = serde_json::json!({ "#": "node/abc" });
540 let v = Value::try_from(json).unwrap();
541 assert_eq!(v.as_link(), Some("node/abc"));
542 }
543
544 #[test]
545 fn test_try_from_json_array_fails() {
546 let json = serde_json::json!([1, 2, 3]);
547 assert!(Value::try_from(json).is_err());
548 }
549
550 #[test]
551 fn test_try_from_json_object_without_soul_fails() {
552 let json = serde_json::json!({ "foo": "bar" });
553 assert!(Value::try_from(json).is_err());
554 }
555
556 #[test]
559 fn test_try_from_json_ref_null() {
560 let json = SerdeJsonValue::Null;
561 let v = Value::try_from(&json).unwrap();
562 assert!(v.is_null());
563 }
564
565 #[test]
566 fn test_try_from_json_ref_bool() {
567 let json = SerdeJsonValue::Bool(true);
568 let v = Value::try_from(&json).unwrap();
569 assert_eq!(v.as_bit(), Some(true));
570 }
571
572 #[test]
573 fn test_try_from_json_ref_string() {
574 let json = SerdeJsonValue::String("hello".into());
575 let v = Value::try_from(&json).unwrap();
576 assert_eq!(v.as_text(), Some("hello"));
577 }
578
579 #[test]
580 fn test_try_from_json_ref_number() {
581 let json = serde_json::json!(42.0);
582 let v = Value::try_from(&json).unwrap();
583 assert_eq!(v.as_number(), Some(42.0));
584 }
585
586 #[test]
587 fn test_try_from_json_ref_link() {
588 let json = serde_json::json!({ "#": "node/abc" });
589 let v = Value::try_from(&json).unwrap();
590 assert_eq!(v.as_link(), Some("node/abc"));
591 }
592
593 #[test]
594 fn test_try_from_json_ref_array_fails() {
595 let json = serde_json::json!([1, 2, 3]);
596 assert!(Value::try_from(&json).is_err());
597 }
598
599 #[test]
600 fn test_try_from_json_ref_object_without_soul_fails() {
601 let json = serde_json::json!({ "foo": "bar" });
602 assert!(Value::try_from(&json).is_err());
603 }
604
605 #[test]
606 fn test_try_from_json_ref_matches_owned() {
607 let cases = vec![
609 SerdeJsonValue::Null,
610 SerdeJsonValue::Bool(true),
611 SerdeJsonValue::Bool(false),
612 SerdeJsonValue::String("test".into()),
613 serde_json::json!(42.0),
614 serde_json::json!({ "#": "soul/123" }),
615 ];
616 for json in cases {
617 let owned = Value::try_from(json.clone()).unwrap();
618 let borrowed = Value::try_from(&json).unwrap();
619 assert_eq!(owned, borrowed, "mismatch for json: {}", json);
620 }
621 }
622
623 #[test]
626 fn test_roundtrip_null() {
627 let v = Value::Null;
628 let json: SerdeJsonValue = v.clone().into();
629 let v2 = Value::try_from(json).unwrap();
630 assert_eq!(v, v2);
631 }
632
633 #[test]
634 fn test_roundtrip_bit() {
635 let v = Value::Bit(true);
636 let json: SerdeJsonValue = v.clone().into();
637 let v2 = Value::try_from(json).unwrap();
638 assert_eq!(v, v2);
639 }
640
641 #[test]
642 fn test_roundtrip_text() {
643 let v = Value::Text("hello world".into());
644 let json: SerdeJsonValue = v.clone().into();
645 let v2 = Value::try_from(json).unwrap();
646 assert_eq!(v, v2);
647 }
648
649 #[test]
650 fn test_roundtrip_number() {
651 let v = Value::Number(42.0);
652 let json: SerdeJsonValue = v.clone().into();
653 let v2 = Value::try_from(json).unwrap();
654 assert_eq!(v, v2);
655 }
656
657 #[test]
658 fn test_roundtrip_link() {
659 let v = Value::Link("node/abc".into());
660 let json: SerdeJsonValue = v.clone().into();
661 let v2 = Value::try_from(json).unwrap();
662 assert_eq!(v, v2);
663 }
664
665 #[test]
668 fn test_from_str() {
669 let v: Value = "hello".into();
670 assert_eq!(v.as_text(), Some("hello"));
671 }
672
673 #[test]
674 fn test_from_string() {
675 let v: Value = String::from("world").into();
676 assert_eq!(v.as_text(), Some("world"));
677 }
678
679 #[test]
682 fn test_from_usize() {
683 let v: Value = 42usize.into();
684 assert_eq!(v.as_number(), Some(42.0));
685 }
686
687 #[test]
688 fn test_from_u64() {
689 let v: Value = 99u64.into();
690 assert_eq!(v.as_number(), Some(99.0));
691 }
692
693 #[test]
694 fn test_from_f32() {
695 let v: Value = 3.15f32.into();
696 assert!((v.as_number().unwrap() - 3.15).abs() < 0.001);
697 }
698
699 #[test]
702 fn test_children_btreemap() {
703 let mut children: Children = BTreeMap::new();
704 children.insert(
705 "key1".to_string(),
706 NodeData {
707 value: Value::Text("v1".into()),
708 updated_at: 1.0,
709 },
710 );
711 children.insert(
712 "key2".to_string(),
713 NodeData {
714 value: Value::Text("v2".into()),
715 updated_at: 2.0,
716 },
717 );
718 let keys: Vec<&String> = children.keys().collect();
720 assert_eq!(keys, vec!["key1", "key2"]);
721 }
722}