1#![allow(clippy::inherent_to_string)] use arena_btreemap::BTreeMap;
40use serde::{Deserialize, Serialize};
41use serde_json::{Value as SerdeJsonValue, json};
42use std::convert::TryFrom;
43
44#[cfg(not(target_arch = "wasm32"))]
55pub type Children = BTreeMap<String, NodeData, crate::arena::SyncBumpArena>;
56
57#[cfg(target_arch = "wasm32")]
59pub type Children = BTreeMap<String, NodeData>;
60
61#[derive(Clone, Serialize, Deserialize, Debug, PartialEq)]
74pub struct NodeData {
75 pub value: Value,
77 pub updated_at: f64,
79}
80
81impl Default for NodeData {
82 fn default() -> Self {
83 Self {
84 value: Value::Null,
85 updated_at: 0.0,
86 }
87 }
88}
89
90#[derive(Clone, Serialize, Deserialize, Debug, PartialEq)]
113pub enum Value {
114 Null,
116 Bit(bool),
118 Number(f64),
120 Text(String),
122 Link(String),
124}
125
126impl Value {
127 pub fn size(&self) -> usize {
134 match self {
135 Value::Text(s) => s.len(),
136 _ => std::mem::size_of_val(self),
137 }
138 }
139
140 pub fn to_string(&self) -> String {
155 match self {
156 Value::Null => "null".to_string(),
157 Value::Bit(bool) => {
158 if *bool {
159 "true".to_string()
160 } else {
161 "false".to_string()
162 }
163 }
164 Value::Number(n) => n.to_string(),
165 Value::Text(t) => t.clone(),
166 Value::Link(l) => l.clone(),
167 }
168 }
169
170 pub fn is_null(&self) -> bool {
172 matches!(self, Value::Null)
173 }
174
175 pub fn is_bit(&self) -> bool {
177 matches!(self, Value::Bit(_))
178 }
179
180 pub fn is_number(&self) -> bool {
182 matches!(self, Value::Number(_))
183 }
184
185 pub fn is_text(&self) -> bool {
187 matches!(self, Value::Text(_))
188 }
189
190 pub fn is_link(&self) -> bool {
192 matches!(self, Value::Link(_))
193 }
194
195 pub fn as_bit(&self) -> Option<bool> {
197 match self {
198 Value::Bit(b) => Some(*b),
199 _ => None,
200 }
201 }
202
203 pub fn as_number(&self) -> Option<f64> {
205 match self {
206 Value::Number(n) => Some(*n),
207 _ => None,
208 }
209 }
210
211 pub fn as_text(&self) -> Option<&str> {
213 match self {
214 Value::Text(t) => Some(t.as_str()),
215 _ => None,
216 }
217 }
218
219 pub fn as_link(&self) -> Option<&str> {
221 match self {
222 Value::Link(l) => Some(l.as_str()),
223 _ => None,
224 }
225 }
226}
227
228impl Value {
229 pub fn from_json_ref(v: &SerdeJsonValue) -> Result<Value, &'static str> {
239 match v {
240 SerdeJsonValue::Null => Ok(Value::Null),
241 SerdeJsonValue::Bool(b) => Ok(Value::Bit(*b)),
242 SerdeJsonValue::String(s) => Ok(Value::Text(s.clone())),
243 SerdeJsonValue::Number(n) => match n.as_f64() {
244 Some(n) => Ok(Value::Number(n)),
245 _ => Err("not convertible to f64"),
246 },
247 SerdeJsonValue::Object(obj) => {
248 if let Some(soul) = obj.get("#").and_then(|v| v.as_str()) {
249 Ok(Value::Link(soul.to_string()))
250 } else {
251 Err("cannot convert json object into Value")
252 }
253 }
254 SerdeJsonValue::Array(_) => Err("cannot convert array into Value"),
255 }
256 }
257}
258impl TryFrom<SerdeJsonValue> for Value {
277 type Error = &'static str;
278
279 fn try_from(v: SerdeJsonValue) -> Result<Value, Self::Error> {
280 match v {
281 SerdeJsonValue::Null => Ok(Value::Null),
282 SerdeJsonValue::Bool(b) => Ok(Value::Bit(b)),
283 SerdeJsonValue::String(s) => Ok(Value::Text(s)),
284 SerdeJsonValue::Number(n) => match n.as_f64() {
285 Some(n) => Ok(Value::Number(n)),
286 _ => Err("not convertible to f64"),
287 },
288 SerdeJsonValue::Object(obj) => {
289 if let Some(soul) = obj.get("#").and_then(|v| v.as_str()) {
291 Ok(Value::Link(soul.to_string()))
292 } else {
293 Err("cannot convert json object into Value")
294 }
295 }
296 SerdeJsonValue::Array(_) => Err("cannot convert array into Value"),
297 }
298 }
299}
300
301impl TryFrom<&SerdeJsonValue> for Value {
313 type Error = &'static str;
314
315 fn try_from(v: &SerdeJsonValue) -> Result<Value, Self::Error> {
316 match v {
317 SerdeJsonValue::Null => Ok(Value::Null),
318 SerdeJsonValue::Bool(b) => Ok(Value::Bit(*b)),
319 SerdeJsonValue::String(s) => Ok(Value::Text(s.clone())),
320 SerdeJsonValue::Number(n) => match n.as_f64() {
321 Some(n) => Ok(Value::Number(n)),
322 _ => Err("not convertible to f64"),
323 },
324 SerdeJsonValue::Object(obj) => {
325 if let Some(soul) = obj.get("#").and_then(|v| v.as_str()) {
326 Ok(Value::Link(soul.to_string()))
327 } else {
328 Err("cannot convert json object into Value")
329 }
330 }
331 SerdeJsonValue::Array(_) => Err("cannot convert array into Value"),
332 }
333 }
334}
335
336impl From<Value> for SerdeJsonValue {
342 fn from(v: Value) -> SerdeJsonValue {
343 match v {
344 Value::Null => SerdeJsonValue::Null,
345 Value::Text(t) => SerdeJsonValue::String(t),
346 Value::Bit(b) => SerdeJsonValue::Bool(b),
347 Value::Number(n) => json!(n),
348 Value::Link(l) => json!({ "#": l }),
349 }
350 }
351}
352
353impl From<usize> for Value {
354 fn from(n: usize) -> Value {
355 Value::Number(n as f64)
356 }
357}
358
359impl From<f32> for Value {
360 fn from(n: f32) -> Value {
361 Value::Number(n as f64)
362 }
363}
364
365impl From<u64> for Value {
366 fn from(n: u64) -> Value {
367 Value::Number(n as f64)
368 }
369}
370
371impl From<&str> for Value {
372 fn from(s: &str) -> Value {
373 Value::Text(s.to_string())
374 }
375}
376
377impl From<String> for Value {
378 fn from(s: String) -> Value {
379 Value::Text(s)
380 }
381}
382
383#[cfg(test)]
384mod tests {
385 use super::*;
386
387 #[test]
390 fn test_nodedata_default() {
391 let nd = NodeData::default();
392 assert!(nd.value.is_null());
393 assert_eq!(nd.updated_at, 0.0);
394 }
395
396 #[test]
397 fn test_nodedata_partial_eq() {
398 let a = NodeData {
399 value: Value::Text("x".into()),
400 updated_at: 1.0,
401 };
402 let b = NodeData {
403 value: Value::Text("x".into()),
404 updated_at: 1.0,
405 };
406 let c = NodeData {
407 value: Value::Text("y".into()),
408 updated_at: 1.0,
409 };
410 assert_eq!(a, b);
411 assert_ne!(a, c);
412 }
413
414 #[test]
417 fn test_value_size_text() {
418 assert_eq!(Value::Text("hello".into()).size(), 5);
419 assert_eq!(Value::Text("".into()).size(), 0);
420 assert_eq!(Value::Text("héllo".into()).size(), 6); }
422
423 #[test]
424 fn test_value_size_non_text() {
425 assert!(Value::Null.size() > 0);
427 assert!(Value::Bit(true).size() > 0);
428 assert!(Value::Number(42.0).size() > 0);
429 assert!(Value::Link("soul".into()).size() > 0);
430 }
431
432 #[test]
435 fn test_value_to_string_null() {
436 assert_eq!(Value::Null.to_string(), "null");
437 }
438
439 #[test]
440 fn test_value_to_string_bit() {
441 assert_eq!(Value::Bit(true).to_string(), "true");
442 assert_eq!(Value::Bit(false).to_string(), "false");
443 }
444
445 #[test]
446 fn test_value_to_string_number() {
447 assert_eq!(Value::Number(42.0).to_string(), "42");
448 assert_eq!(Value::Number(3.15).to_string(), "3.15");
449 }
450
451 #[test]
452 fn test_value_to_string_text() {
453 assert_eq!(Value::Text("hello".into()).to_string(), "hello");
454 }
455
456 #[test]
457 fn test_value_to_string_link() {
458 assert_eq!(Value::Link("node/abc".into()).to_string(), "node/abc");
459 }
460
461 #[test]
464 fn test_value_is_null() {
465 assert!(Value::Null.is_null());
466 assert!(!Value::Bit(false).is_null());
467 }
468
469 #[test]
470 fn test_value_is_bit() {
471 assert!(Value::Bit(true).is_bit());
472 assert!(!Value::Null.is_bit());
473 }
474
475 #[test]
476 fn test_value_is_number() {
477 assert!(Value::Number(1.0).is_number());
478 assert!(!Value::Null.is_number());
479 }
480
481 #[test]
482 fn test_value_is_text() {
483 assert!(Value::Text("x".into()).is_text());
484 assert!(!Value::Null.is_text());
485 }
486
487 #[test]
488 fn test_value_is_link() {
489 assert!(Value::Link("soul".into()).is_link());
490 assert!(!Value::Null.is_link());
491 }
492
493 #[test]
496 fn test_value_as_bit() {
497 assert_eq!(Value::Bit(true).as_bit(), Some(true));
498 assert_eq!(Value::Bit(false).as_bit(), Some(false));
499 assert_eq!(Value::Null.as_bit(), None);
500 }
501
502 #[test]
503 fn test_value_as_number() {
504 assert_eq!(Value::Number(42.0).as_number(), Some(42.0));
505 assert_eq!(Value::Null.as_number(), None);
506 }
507
508 #[test]
509 fn test_value_as_text() {
510 assert_eq!(Value::Text("hello".into()).as_text(), Some("hello"));
511 assert_eq!(Value::Null.as_text(), None);
512 }
513
514 #[test]
515 fn test_value_as_link() {
516 assert_eq!(Value::Link("soul".into()).as_link(), Some("soul"));
517 assert_eq!(Value::Null.as_link(), None);
518 }
519
520 #[test]
523 fn test_try_from_json_null() {
524 let v = Value::try_from(SerdeJsonValue::Null).unwrap();
525 assert!(v.is_null());
526 }
527
528 #[test]
529 fn test_try_from_json_bool() {
530 let v = Value::try_from(SerdeJsonValue::Bool(true)).unwrap();
531 assert_eq!(v.as_bit(), Some(true));
532 }
533
534 #[test]
535 fn test_try_from_json_string() {
536 let v = Value::try_from(SerdeJsonValue::String("hello".into())).unwrap();
537 assert_eq!(v.as_text(), Some("hello"));
538 }
539
540 #[test]
541 fn test_try_from_json_number() {
542 let v = Value::try_from(serde_json::json!(42.0)).unwrap();
543 assert_eq!(v.as_number(), Some(42.0));
544 }
545
546 #[test]
547 fn test_try_from_json_link() {
548 let json = serde_json::json!({ "#": "node/abc" });
549 let v = Value::try_from(json).unwrap();
550 assert_eq!(v.as_link(), Some("node/abc"));
551 }
552
553 #[test]
554 fn test_try_from_json_array_fails() {
555 let json = serde_json::json!([1, 2, 3]);
556 assert!(Value::try_from(json).is_err());
557 }
558
559 #[test]
560 fn test_try_from_json_object_without_soul_fails() {
561 let json = serde_json::json!({ "foo": "bar" });
562 assert!(Value::try_from(json).is_err());
563 }
564
565 #[test]
568 fn test_try_from_json_ref_null() {
569 let json = SerdeJsonValue::Null;
570 let v = Value::try_from(&json).unwrap();
571 assert!(v.is_null());
572 }
573
574 #[test]
575 fn test_try_from_json_ref_bool() {
576 let json = SerdeJsonValue::Bool(true);
577 let v = Value::try_from(&json).unwrap();
578 assert_eq!(v.as_bit(), Some(true));
579 }
580
581 #[test]
582 fn test_try_from_json_ref_string() {
583 let json = SerdeJsonValue::String("hello".into());
584 let v = Value::try_from(&json).unwrap();
585 assert_eq!(v.as_text(), Some("hello"));
586 }
587
588 #[test]
589 fn test_try_from_json_ref_number() {
590 let json = serde_json::json!(42.0);
591 let v = Value::try_from(&json).unwrap();
592 assert_eq!(v.as_number(), Some(42.0));
593 }
594
595 #[test]
596 fn test_try_from_json_ref_link() {
597 let json = serde_json::json!({ "#": "node/abc" });
598 let v = Value::try_from(&json).unwrap();
599 assert_eq!(v.as_link(), Some("node/abc"));
600 }
601
602 #[test]
603 fn test_try_from_json_ref_array_fails() {
604 let json = serde_json::json!([1, 2, 3]);
605 assert!(Value::try_from(&json).is_err());
606 }
607
608 #[test]
609 fn test_try_from_json_ref_object_without_soul_fails() {
610 let json = serde_json::json!({ "foo": "bar" });
611 assert!(Value::try_from(&json).is_err());
612 }
613
614 #[test]
615 fn test_try_from_json_ref_matches_owned() {
616 let cases = vec![
618 SerdeJsonValue::Null,
619 SerdeJsonValue::Bool(true),
620 SerdeJsonValue::Bool(false),
621 SerdeJsonValue::String("test".into()),
622 serde_json::json!(42.0),
623 serde_json::json!({ "#": "soul/123" }),
624 ];
625 for json in cases {
626 let owned = Value::try_from(json.clone()).unwrap();
627 let borrowed = Value::try_from(&json).unwrap();
628 assert_eq!(owned, borrowed, "mismatch for json: {}", json);
629 }
630 }
631
632 #[test]
635 fn test_roundtrip_null() {
636 let v = Value::Null;
637 let json: SerdeJsonValue = v.clone().into();
638 let v2 = Value::try_from(json).unwrap();
639 assert_eq!(v, v2);
640 }
641
642 #[test]
643 fn test_roundtrip_bit() {
644 let v = Value::Bit(true);
645 let json: SerdeJsonValue = v.clone().into();
646 let v2 = Value::try_from(json).unwrap();
647 assert_eq!(v, v2);
648 }
649
650 #[test]
651 fn test_roundtrip_text() {
652 let v = Value::Text("hello world".into());
653 let json: SerdeJsonValue = v.clone().into();
654 let v2 = Value::try_from(json).unwrap();
655 assert_eq!(v, v2);
656 }
657
658 #[test]
659 fn test_roundtrip_number() {
660 let v = Value::Number(42.0);
661 let json: SerdeJsonValue = v.clone().into();
662 let v2 = Value::try_from(json).unwrap();
663 assert_eq!(v, v2);
664 }
665
666 #[test]
667 fn test_roundtrip_link() {
668 let v = Value::Link("node/abc".into());
669 let json: SerdeJsonValue = v.clone().into();
670 let v2 = Value::try_from(json).unwrap();
671 assert_eq!(v, v2);
672 }
673
674 #[test]
677 fn test_from_str() {
678 let v: Value = "hello".into();
679 assert_eq!(v.as_text(), Some("hello"));
680 }
681
682 #[test]
683 fn test_from_string() {
684 let v: Value = String::from("world").into();
685 assert_eq!(v.as_text(), Some("world"));
686 }
687
688 #[test]
691 fn test_from_usize() {
692 let v: Value = 42usize.into();
693 assert_eq!(v.as_number(), Some(42.0));
694 }
695
696 #[test]
697 fn test_from_u64() {
698 let v: Value = 99u64.into();
699 assert_eq!(v.as_number(), Some(99.0));
700 }
701
702 #[test]
703 fn test_from_f32() {
704 let v: Value = 3.15f32.into();
705 assert!((v.as_number().unwrap() - 3.15).abs() < 0.001);
706 }
707
708 #[test]
711 fn test_children_btreemap() {
712 let mut children: Children = BTreeMap::default();
713 children.insert(
714 "key1".to_string(),
715 NodeData {
716 value: Value::Text("v1".into()),
717 updated_at: 1.0,
718 },
719 );
720 children.insert(
721 "key2".to_string(),
722 NodeData {
723 value: Value::Text("v2".into()),
724 updated_at: 2.0,
725 },
726 );
727 let keys: Vec<&String> = children.keys().collect();
729 assert_eq!(keys, vec!["key1", "key2"]);
730 }
731}