1use indexmap::IndexMap;
35use std::fmt;
36
37use crate::error::DocumentError;
38
39pub const MAX_DEPTH: usize = 200;
41
42pub const MAX_NODES: usize = 1_000_000;
51
52#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
54pub struct NodeId(usize);
55
56#[derive(Debug, Clone, PartialEq)]
81pub enum Scalar {
82 Null,
84 Bool(bool),
86 Int(num_bigint::BigInt),
88 Float(f64),
90 Str(String),
92 Date(String),
94 Time(String),
96 Datetime(String),
98}
99
100impl fmt::Display for Scalar {
101 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
102 match self {
103 Scalar::Null => write!(f, "null"),
104 Scalar::Bool(b) => write!(f, "{b}"),
105 Scalar::Int(i) => write!(f, "{i}"),
106 Scalar::Float(x) => write!(f, "{x}"),
107 Scalar::Str(s) => write!(f, "{s:?}"),
108 Scalar::Date(s) | Scalar::Time(s) | Scalar::Datetime(s) => write!(f, "{s}"),
109 }
110 }
111}
112
113#[derive(Debug, Clone, PartialEq)]
119pub enum Value {
120 Null,
122 Bool(bool),
124 Int(num_bigint::BigInt),
126 Float(f64),
128 Str(String),
130 Date(String),
134 Time(String),
136 Datetime(String),
138 Array(Vec<Value>),
140 Object(IndexMap<String, Value>),
142}
143
144impl Value {
145 #[cfg(test)]
151 fn as_object(&self) -> Option<&IndexMap<String, Value>> {
152 match self {
153 Value::Object(m) => Some(m),
154 _ => None,
155 }
156 }
157}
158
159impl From<Scalar> for Value {
160 fn from(s: Scalar) -> Self {
161 match s {
162 Scalar::Null => Value::Null,
163 Scalar::Bool(b) => Value::Bool(b),
164 Scalar::Int(i) => Value::Int(i),
165 Scalar::Float(x) => Value::Float(x),
166 Scalar::Str(s) => Value::Str(s),
167 Scalar::Date(s) => Value::Date(s),
168 Scalar::Time(s) => Value::Time(s),
169 Scalar::Datetime(s) => Value::Datetime(s),
170 }
171 }
172}
173
174#[derive(Debug, Clone)]
175enum NodeData {
176 Leaf(Scalar),
177 Internal(Vec<(String, NodeId)>),
178}
179
180#[derive(Debug, Clone)]
181struct Entry {
182 data: NodeData,
183 depth: usize,
192}
193
194pub(crate) fn check_write_depth(depth: usize, path: &str) -> Result<(), DocumentError> {
199 if depth > MAX_DEPTH {
200 return Err(DocumentError::new(
201 path,
202 format!("nesting exceeds the maximum depth ({MAX_DEPTH})"),
203 ));
204 }
205 Ok(())
206}
207
208fn join(path: &str, key: &str) -> String {
209 let is_identifier = !key.is_empty()
210 && key
211 .chars()
212 .next()
213 .is_some_and(|c| c.is_alphabetic() || c == '_')
214 && key.chars().all(|c| c.is_alphanumeric() || c == '_');
215 if is_identifier {
216 format!("{path}.{key}")
217 } else {
218 format!("{path}[\"{key}\"]")
219 }
220}
221
222struct ChildSpec<'a> {
227 path: String,
228 depth: usize,
229 value: &'a Value,
230}
231
232fn child_specs<'a>(
233 v: &'a Value,
234 path: &str,
235 depth: usize,
236) -> Result<Vec<ChildSpec<'a>>, DocumentError> {
237 match v {
238 Value::Array(items) => {
239 let mut out = Vec::with_capacity(items.len());
240 for (i, item) in items.iter().enumerate() {
241 let ip = format!("{path}[{i}]");
242 if matches!(item, Value::Array(_)) {
243 return Err(DocumentError::new(
244 ip,
245 "an array of arrays has no labeled-edge form",
246 ));
247 }
248 out.push(ChildSpec {
249 path: ip,
250 depth: depth + 1,
251 value: item,
252 });
253 }
254 Ok(out)
255 }
256 other => Ok(vec![ChildSpec {
257 path: path.to_string(),
258 depth,
259 value: other,
260 }]),
261 }
262}
263
264fn build_node(
267 arena: &mut Vec<Entry>,
268 value: &Value,
269 path: &str,
270 depth: usize,
271) -> Result<NodeId, DocumentError> {
272 check_write_depth(depth, path)?;
273 match value {
274 Value::Object(map) => {
275 let mut edges = Vec::new();
276 for (k, v) in map {
277 let kp = join(path, k);
278 for spec in child_specs(v, &kp, depth + 1)? {
279 let cid = build_node(arena, spec.value, &spec.path, spec.depth)?;
280 edges.push((k.clone(), cid));
281 }
282 }
283 push(arena, NodeData::Internal(edges), depth, path)
284 }
285 Value::Array(_) => Err(DocumentError::new(
286 path,
287 "a bare array has no labeled-edge form (arrays appear only as a repeated field)",
288 )),
289 Value::Null => push(arena, NodeData::Leaf(Scalar::Null), depth, path),
295 Value::Bool(b) => push(arena, NodeData::Leaf(Scalar::Bool(*b)), depth, path),
296 Value::Int(i) => push(arena, NodeData::Leaf(Scalar::Int(i.clone())), depth, path),
297 Value::Float(x) => push(arena, NodeData::Leaf(Scalar::Float(*x)), depth, path),
298 Value::Date(s) => push(arena, NodeData::Leaf(Scalar::Date(s.clone())), depth, path),
299 Value::Time(s) => push(arena, NodeData::Leaf(Scalar::Time(s.clone())), depth, path),
300 Value::Datetime(s) => push(
301 arena,
302 NodeData::Leaf(Scalar::Datetime(s.clone())),
303 depth,
304 path,
305 ),
306 Value::Str(s) => push(arena, NodeData::Leaf(Scalar::Str(s.clone())), depth, path),
307 }
308}
309
310fn push(
313 arena: &mut Vec<Entry>,
314 data: NodeData,
315 depth: usize,
316 path: &str,
317) -> Result<NodeId, DocumentError> {
318 if arena.len() >= MAX_NODES {
319 return Err(DocumentError::new(
320 path,
321 format!("document exceeds the maximum node count ({MAX_NODES})"),
322 ));
323 }
324 let id = NodeId(arena.len());
325 arena.push(Entry { data, depth });
326 Ok(id)
327}
328
329#[derive(Debug, Clone)]
331pub struct Doc {
332 arena: Vec<Entry>,
333 root: NodeId,
334}
335
336impl Doc {
337 pub fn of(value: &Value) -> Result<Doc, DocumentError> {
339 let mut arena = Vec::new();
340 let root = build_node(&mut arena, value, "$", 0)?;
341 Ok(Doc { arena, root })
342 }
343
344 pub fn root(&self) -> Cursor<'_> {
346 Cursor {
347 doc: self,
348 id: self.root,
349 path: "$".to_string(),
350 }
351 }
352
353 fn entry(&self, id: NodeId) -> &Entry {
354 &self.arena[id.0]
355 }
356
357 pub fn add(
361 &mut self,
362 at: NodeId,
363 path: &str,
364 label: &str,
365 value: &Value,
366 ) -> Result<NodeId, DocumentError> {
367 self.require_internal(at, path, "add")?;
368 let attach_depth = self.entry(at).depth;
369 let child_path = join(path, label);
370 let cid = build_node(&mut self.arena, value, &child_path, attach_depth + 1)?;
371 let edges = self.internal_edges_mut(at, path, "add")?;
372 edges.push((label.to_string(), cid));
373 Ok(cid)
374 }
375
376 pub fn set(
379 &mut self,
380 at: NodeId,
381 path: &str,
382 label: &str,
383 value: &Value,
384 ) -> Result<NodeId, DocumentError> {
385 self.require_internal(at, path, "set")?;
386 let attach_depth = self.entry(at).depth;
387 let child_path = join(path, label);
388 let cid = build_node(&mut self.arena, value, &child_path, attach_depth + 1)?;
389 let edges = self.internal_edges_mut(at, path, "set")?;
390 let mut first: Option<usize> = None;
391 let mut kept: Vec<(String, NodeId)> = Vec::with_capacity(edges.len());
392 for (lbl, child) in edges.drain(..) {
393 if lbl == label {
394 if first.is_none() {
395 first = Some(kept.len());
396 kept.push((label.to_string(), cid));
397 }
398 } else {
400 kept.push((lbl, child));
401 }
402 }
403 if first.is_none() {
404 kept.push((label.to_string(), cid));
405 }
406 *edges = kept;
407 Ok(cid)
408 }
409
410 pub fn remove(&mut self, at: NodeId, path: &str, label: &str) -> Result<(), DocumentError> {
412 self.require_internal(at, path, "remove")?;
413 let edges = self.internal_edges_mut(at, path, "remove")?;
414 edges.retain(|(lbl, _)| lbl != label);
415 Ok(())
416 }
417
418 fn require_internal(&self, id: NodeId, path: &str, op: &str) -> Result<(), DocumentError> {
419 match self.entry(id).data {
420 NodeData::Internal(_) => Ok(()),
421 NodeData::Leaf(_) => Err(DocumentError::new(path, format!("cannot {op} on a leaf"))),
422 }
423 }
424
425 fn internal_edges_mut(
436 &mut self,
437 at: NodeId,
438 path: &str,
439 op: &str,
440 ) -> Result<&mut Vec<(String, NodeId)>, DocumentError> {
441 match &mut self.arena[at.0].data {
442 NodeData::Internal(edges) => Ok(edges),
443 NodeData::Leaf(_) => Err(DocumentError::new(path, format!("cannot {op} on a leaf"))),
444 }
445 }
446
447 pub fn to_grouped(&self) -> Value {
450 self.grouped_at(self.root)
451 }
452
453 pub(crate) fn has_interleaving_loss(&self) -> bool {
464 self.node_has_interleaving_loss(self.root)
465 }
466
467 fn node_has_interleaving_loss(&self, id: NodeId) -> bool {
468 match &self.entry(id).data {
469 NodeData::Leaf(_) => false,
470 NodeData::Internal(edges) => {
471 let mut closed: std::collections::HashSet<&str> = std::collections::HashSet::new();
472 let mut prev_label: Option<&str> = None;
473 for (label, _) in edges {
474 if let Some(p) = prev_label
475 && p != label.as_str()
476 {
477 closed.insert(p);
478 }
479 if closed.contains(label.as_str()) {
480 return true;
481 }
482 prev_label = Some(label.as_str());
483 }
484 edges
485 .iter()
486 .any(|(_, child)| self.node_has_interleaving_loss(*child))
487 }
488 }
489 }
490
491 fn grouped_at(&self, id: NodeId) -> Value {
495 match &self.entry(id).data {
496 NodeData::Leaf(s) => Value::from(s.clone()),
497 NodeData::Internal(edges) => {
498 let mut counts: IndexMap<&str, usize> = IndexMap::new();
499 for (label, _) in edges {
500 *counts.entry(label.as_str()).or_insert(0) += 1;
501 }
502 let mut out: IndexMap<String, Value> = IndexMap::new();
503 for (label, child) in edges {
504 let g = self.grouped_at(*child);
505 if counts[label.as_str()] > 1 {
506 match out.get_mut(label.as_str()) {
507 Some(Value::Array(arr)) => arr.push(g),
508 _ => {
509 out.insert(label.clone(), Value::Array(vec![g]));
510 }
511 }
512 } else {
513 out.insert(label.clone(), g);
514 }
515 }
516 Value::Object(out)
517 }
518 }
519 }
520
521 pub fn to_data(&self) -> Value {
523 self.data_at(self.root)
524 }
525
526 fn data_at(&self, id: NodeId) -> Value {
527 match &self.entry(id).data {
528 NodeData::Leaf(s) => Value::from(s.clone()),
529 NodeData::Internal(edges) => {
530 let mut map = IndexMap::new();
531 for (label, child) in edges {
538 map.insert(label.clone(), self.data_at(*child));
539 }
540 Value::Object(map)
541 }
542 }
543 }
544
545 pub fn eq_doc(&self, other: &Doc) -> bool {
548 self.node_eq(self.root, other, other.root)
549 }
550
551 fn node_eq(&self, a: NodeId, other: &Doc, b: NodeId) -> bool {
552 match (&self.entry(a).data, &other.entry(b).data) {
553 (NodeData::Leaf(x), NodeData::Leaf(y)) => x == y,
554 (NodeData::Internal(xs), NodeData::Internal(ys)) => {
555 xs.len() == ys.len()
556 && xs
557 .iter()
558 .zip(ys.iter())
559 .all(|((la, ca), (lb, cb))| la == lb && self.node_eq(*ca, other, *cb))
560 }
561 _ => false,
562 }
563 }
564}
565
566#[derive(Debug, Clone)]
573pub struct Cursor<'a> {
574 doc: &'a Doc,
575 id: NodeId,
576 pub path: String,
578}
579
580impl<'a> Cursor<'a> {
581 pub fn id(&self) -> NodeId {
583 self.id
584 }
585
586 pub fn is_leaf(&self) -> bool {
588 matches!(self.doc.entry(self.id).data, NodeData::Leaf(_))
589 }
590
591 pub fn value(&self) -> Result<&'a Scalar, DocumentError> {
593 match &self.doc.entry(self.id).data {
594 NodeData::Leaf(s) => Ok(s),
595 NodeData::Internal(_) => Err(DocumentError::new(&self.path, "not a leaf; use edges()")),
596 }
597 }
598
599 pub fn edges(&self) -> Result<Vec<(String, Cursor<'a>)>, DocumentError> {
601 match &self.doc.entry(self.id).data {
602 NodeData::Internal(edges) => {
603 let mut counts: IndexMap<&str, usize> = IndexMap::new();
604 let mut out = Vec::with_capacity(edges.len());
605 for (label, child) in edges {
606 let i = *counts.entry(label.as_str()).or_insert(0);
607 counts.insert(label.as_str(), i + 1);
608 let cp = crate::report::child_path(&self.path, label, i);
609 out.push((
610 label.clone(),
611 Cursor {
612 doc: self.doc,
613 id: *child,
614 path: cp,
615 },
616 ));
617 }
618 Ok(out)
619 }
620 NodeData::Leaf(_) => Err(DocumentError::new(&self.path, "a leaf has no edges")),
621 }
622 }
623
624 pub(crate) fn internal_edges(&self) -> Result<&'a [(String, NodeId)], DocumentError> {
632 match &self.doc.entry(self.id).data {
633 NodeData::Internal(edges) => Ok(edges),
634 NodeData::Leaf(_) => Err(DocumentError::new(&self.path, "a leaf has no edges")),
635 }
636 }
637
638 pub(crate) fn raw_edges(&self) -> Result<Vec<(&'a str, usize, NodeId)>, DocumentError> {
639 match &self.doc.entry(self.id).data {
640 NodeData::Internal(edges) => {
641 let mut counts: IndexMap<&str, usize> = IndexMap::new();
642 let mut out = Vec::with_capacity(edges.len());
643 for (label, child) in edges {
644 let i = *counts.entry(label.as_str()).or_insert(0);
645 counts.insert(label.as_str(), i + 1);
646 out.push((label.as_str(), i, *child));
647 }
648 Ok(out)
649 }
650 NodeData::Leaf(_) => Err(DocumentError::new(&self.path, "a leaf has no edges")),
651 }
652 }
653
654 pub(crate) fn seek(&self, id: NodeId) -> Cursor<'a> {
659 Cursor {
660 doc: self.doc,
661 id,
662 path: String::new(),
663 }
664 }
665
666 pub fn labels(&self) -> Vec<String> {
668 let mut seen = std::collections::HashSet::new();
669 let mut out = Vec::new();
670 if let NodeData::Internal(edges) = &self.doc.entry(self.id).data {
671 for (label, _) in edges {
672 if seen.insert(label.clone()) {
673 out.push(label.clone());
674 }
675 }
676 }
677 out
678 }
679
680 pub fn get(&self, label: &str) -> Vec<Cursor<'a>> {
682 self.edges()
683 .into_iter()
684 .flatten()
685 .filter(|(lbl, _)| lbl == label)
686 .map(|(_, c)| c)
687 .collect()
688 }
689
690 pub fn get_one(&self, label: &str) -> Result<Cursor<'a>, DocumentError> {
692 let mut cs = self.get(label);
693 if cs.len() != 1 {
694 return Err(DocumentError::new(
695 &self.path,
696 format!("expected exactly one {label:?}, found {}", cs.len()),
697 ));
698 }
699 Ok(cs.remove(0))
700 }
701
702 pub fn count(&self, label: &str) -> usize {
704 if let NodeData::Internal(edges) = &self.doc.entry(self.id).data {
705 edges.iter().filter(|(lbl, _)| lbl == label).count()
706 } else {
707 0
708 }
709 }
710
711 pub fn child(&self, label: &str) -> Result<Cursor<'a>, DocumentError> {
713 self.get_one(label)
714 }
715
716 pub fn to_raw(&self) -> RawNode {
723 self.doc.raw_at(self.id)
724 }
725}
726
727#[derive(Debug, Clone, PartialEq)]
739pub enum RawNode {
740 Leaf(Scalar),
742 Edges(Vec<(String, RawNode)>),
744}
745
746impl Doc {
747 pub fn from_raw(root: RawNode) -> Result<Doc, DocumentError> {
751 let mut arena = Vec::new();
752 let root_id = push_raw(&mut arena, root, 0)?;
753 Ok(Doc {
754 arena,
755 root: root_id,
756 })
757 }
758
759 pub fn to_raw(&self) -> RawNode {
762 self.raw_at(self.root)
763 }
764
765 fn raw_at(&self, id: NodeId) -> RawNode {
766 let entry = self.entry(id);
767 match &entry.data {
768 NodeData::Leaf(s) => RawNode::Leaf(s.clone()),
769 NodeData::Internal(edges) => RawNode::Edges(
770 edges
771 .iter()
772 .map(|(label, child)| (label.clone(), self.raw_at(*child)))
773 .collect(),
774 ),
775 }
776 }
777}
778
779impl Doc {
786 pub fn from_format(name: &str, text: &str) -> Result<Doc, crate::error::OmnistError> {
790 let fmt = crate::registry::get_format(name)?;
791 (fmt.read)(text)
792 }
793
794 pub fn to_format(&self, name: &str) -> Result<String, crate::error::OmnistError> {
797 let fmt = crate::registry::get_format(name)?;
798 (fmt.write)(self)
799 }
800
801 pub fn check_format(
808 &self,
809 name: &str,
810 ) -> Result<crate::report::WriteReport, crate::error::OmnistError> {
811 let fmt = crate::registry::get_format(name)?;
812 match &fmt.check {
813 Some(check) => Ok(check(self)),
814 None => Err(DocumentError::new(
815 "$",
816 format!("format {name:?} has no check() -- cannot simulate a write"),
817 )
818 .into()),
819 }
820 }
821}
822
823fn push_raw(arena: &mut Vec<Entry>, node: RawNode, depth: usize) -> Result<NodeId, DocumentError> {
824 check_write_depth(depth, "$")?;
829 match node {
830 RawNode::Leaf(s) => push(arena, NodeData::Leaf(s), depth, "$"),
831 RawNode::Edges(edges) => {
832 let mut out = Vec::with_capacity(edges.len());
833 for (label, child) in edges {
834 let cid = push_raw(arena, child, depth + 1)?;
835 out.push((label, cid));
836 }
837 push(arena, NodeData::Internal(out), depth, "$")
838 }
839 }
840}
841
842#[cfg(test)]
843mod tests {
844 use super::*;
845
846 fn obj(pairs: &[(&str, Value)]) -> Value {
847 let mut m = IndexMap::new();
848 for (k, v) in pairs {
849 m.insert((*k).to_string(), v.clone());
850 }
851 Value::Object(m)
852 }
853
854 fn nest(levels: usize) -> Value {
858 let mut v = Value::Int((0).into());
859 for _ in 0..levels {
860 v = obj(&[("a", v)]);
861 }
862 v
863 }
864
865 #[test]
868 fn constructs_a_scalar_leaf() {
869 let doc = Doc::of(&Value::Int((42).into())).unwrap();
870 let root = doc.root();
871 assert!(root.is_leaf());
872 assert_eq!(root.value().unwrap(), &Scalar::Int((42).into()));
873 }
874
875 #[test]
876 fn constructs_an_object_as_ordered_edges() {
877 let v = obj(&[("b", Value::Int((1).into())), ("a", Value::Int((2).into()))]);
878 let doc = Doc::of(&v).unwrap();
879 let root = doc.root();
880 assert!(!root.is_leaf());
881 let edges = root.edges().unwrap();
882 let labels: Vec<&str> = edges.iter().map(|(l, _)| l.as_str()).collect();
883 assert_eq!(labels, vec!["b", "a"]);
885 }
886
887 #[test]
888 fn a_list_value_expands_into_repeated_edges() {
889 let v = obj(&[(
890 "member",
891 Value::Array(vec![
892 Value::Int((1).into()),
893 Value::Int((2).into()),
894 Value::Int((3).into()),
895 ]),
896 )]);
897 let doc = Doc::of(&v).unwrap();
898 let root = doc.root();
899 assert_eq!(root.count("member"), 3);
900 let members = root.get("member");
901 let vals: Vec<&Scalar> = members.iter().map(|c| c.value().unwrap()).collect();
902 assert_eq!(
903 vals,
904 vec![
905 &Scalar::Int((1).into()),
906 &Scalar::Int((2).into()),
907 &Scalar::Int((3).into())
908 ]
909 );
910 }
911
912 #[test]
913 fn a_bare_top_level_array_is_rejected() {
914 let err = Doc::of(&Value::Array(vec![Value::Int((1).into())])).unwrap_err();
915 assert!(err.message.contains("bare array"));
916 assert_eq!(err.path, "$");
917 }
918
919 #[test]
920 fn an_array_of_arrays_is_rejected() {
921 let v = obj(&[(
922 "a",
923 Value::Array(vec![Value::Array(vec![Value::Int((1).into())])]),
924 )]);
925 let err = Doc::of(&v).unwrap_err();
926 assert!(err.message.contains("array of arrays"));
927 assert_eq!(err.path, "$.a[0]");
928 }
929
930 #[test]
933 fn depth_guard_accepts_exactly_max_depth() {
934 let v = nest(MAX_DEPTH);
937 assert!(Doc::of(&v).is_ok());
938 }
939
940 #[test]
941 fn depth_guard_rejects_one_past_max_depth() {
942 let v = nest(MAX_DEPTH + 1);
943 let err = Doc::of(&v).unwrap_err();
944 assert!(err.message.contains("maximum depth"));
945 }
946
947 fn wide(n: usize) -> Value {
957 obj(&[("a", Value::Array(vec![Value::Int((0).into()); n]))])
958 }
959
960 #[test]
961 fn node_guard_accepts_exactly_max_nodes() {
962 let v = wide(MAX_NODES - 1);
963 assert!(Doc::of(&v).is_ok());
964 }
965
966 #[test]
967 fn node_guard_rejects_one_past_max_nodes() {
968 let v = wide(MAX_NODES);
969 let err = Doc::of(&v).unwrap_err();
970 assert!(err.message.contains("maximum node count"));
971 }
972
973 #[test]
974 fn an_array_value_consumes_an_extra_depth_level() {
975 let direct = obj(&[("a", Value::Int((1).into()))]);
982 let via_array = obj(&[("a", Value::Array(vec![Value::Int((1).into())]))]);
983 let doc_direct = Doc::of(&direct).unwrap();
984 let doc_array = Doc::of(&via_array).unwrap();
985 let leaf_direct = doc_direct.root().child("a").unwrap();
986 let leaf_array = doc_array.root().child("a").unwrap();
987 assert_eq!(doc_direct.entry(leaf_direct.id()).depth, 1);
988 assert_eq!(doc_array.entry(leaf_array.id()).depth, 2);
989 }
990
991 #[test]
994 fn every_tree_mutating_entry_point_enforces_the_depth_guard() {
995 assert!(Doc::of(&nest(MAX_DEPTH + 1)).is_err());
997
998 let mut doc = Doc::of(&obj(&[("seed", Value::Int((0).into()))])).unwrap();
1001 let root_id = doc.root().id();
1002 let root_path = doc.root().path.clone();
1003 assert!(
1004 doc.add(root_id, &root_path, "b", &nest(MAX_DEPTH + 1))
1005 .is_err()
1006 );
1007
1008 let mut doc2 = Doc::of(&obj(&[("seed", Value::Int((0).into()))])).unwrap();
1010 let root_id2 = doc2.root().id();
1011 let root_path2 = doc2.root().path.clone();
1012 assert!(
1013 doc2.set(root_id2, &root_path2, "b", &nest(MAX_DEPTH + 1))
1014 .is_err()
1015 );
1016 }
1017
1018 #[test]
1022 fn add_at_a_deep_cursor_accounts_for_the_cursors_own_depth() {
1023 let mut doc = Doc::of(&nest(MAX_DEPTH)).unwrap();
1033 let mut cursor = doc.root();
1034 for _ in 0..190 {
1035 cursor = cursor.child("a").unwrap();
1036 }
1037 assert_eq!(doc.entry(cursor.id()).depth, 190);
1038 let id = cursor.id();
1039 let path = cursor.path.clone();
1040
1041 let too_deep = nest(15);
1042 assert!(doc.add(id, &path, "b", &too_deep).is_err());
1043
1044 let shallow = nest(5);
1048 assert!(doc.set(id, &path, "b", &shallow).is_ok());
1049 }
1050
1051 #[test]
1054 fn labels_and_get_preserve_first_seen_and_insertion_order() {
1055 let v = obj(&[
1059 (
1060 "z",
1061 Value::Array(vec![Value::Int((1).into()), Value::Int((3).into())]),
1062 ),
1063 ("a", Value::Int((2).into())),
1064 ("m", Value::Int((4).into())),
1065 ]);
1066 let doc = Doc::of(&v).unwrap();
1067 let root = doc.root();
1068 assert_eq!(root.labels(), vec!["z", "a", "m"]);
1069 let z_vals: Vec<&Scalar> = root.get("z").iter().map(|c| c.value().unwrap()).collect();
1070 assert_eq!(
1071 z_vals,
1072 vec![&Scalar::Int((1).into()), &Scalar::Int((3).into())]
1073 );
1074 }
1075
1076 #[test]
1077 fn labels_and_count_on_a_leaf_are_empty() {
1078 let doc = Doc::of(&Value::Int((1).into())).unwrap();
1079 let root = doc.root();
1080 assert!(root.labels().is_empty());
1081 assert_eq!(root.count("anything"), 0);
1082 }
1083
1084 #[test]
1085 fn to_grouped_preserves_first_seen_key_order() {
1086 let v = obj(&[
1087 (
1088 "z",
1089 Value::Array(vec![Value::Int((1).into()), Value::Int((3).into())]),
1090 ),
1091 ("a", Value::Int((2).into())),
1092 ]);
1093 let doc = Doc::of(&v).unwrap();
1094 let grouped = doc.to_grouped();
1095 let expected = obj(&[
1096 (
1097 "z",
1098 Value::Array(vec![Value::Int((1).into()), Value::Int((3).into())]),
1099 ),
1100 ("a", Value::Int((2).into())),
1101 ]);
1102 assert_eq!(grouped, expected);
1103 let keys: Vec<&str> = grouped
1106 .as_object()
1107 .unwrap()
1108 .keys()
1109 .map(|s| s.as_str())
1110 .collect();
1111 assert_eq!(keys, vec!["z", "a"]);
1112 }
1113
1114 #[test]
1115 fn value_as_object_is_none_for_a_non_object() {
1116 assert!(Value::Int((1).into()).as_object().is_none());
1117 }
1118
1119 #[test]
1122 fn add_appends_and_get_one_requires_exactly_one() {
1123 let mut doc = Doc::of(&obj(&[])).unwrap();
1124 let root_id = doc.root().id();
1125 let root_path = doc.root().path.clone();
1126 doc.add(root_id, &root_path, "x", &Value::Int((1).into()))
1127 .unwrap();
1128 doc.add(root_id, &root_path, "x", &Value::Int((2).into()))
1129 .unwrap();
1130 let root = doc.root();
1131 assert_eq!(root.count("x"), 2);
1132 assert!(root.get_one("x").is_err());
1133 }
1134
1135 #[test]
1136 fn set_replaces_all_occurrences_at_first_position() {
1137 let mut doc = Doc::of(&obj(&[
1138 ("x", Value::Int((1).into())),
1139 ("y", Value::Int((9).into())),
1140 ("x", Value::Int((2).into())),
1141 ]))
1142 .unwrap();
1143 let root_id = doc.root().id();
1144 let root_path = doc.root().path.clone();
1145 doc.set(root_id, &root_path, "x", &Value::Int((100).into()))
1146 .unwrap();
1147 let root = doc.root();
1148 let labels: Vec<String> = root.edges().unwrap().into_iter().map(|(l, _)| l).collect();
1149 assert_eq!(labels, vec!["x", "y"]);
1150 assert_eq!(
1151 root.get_one("x").unwrap().value().unwrap(),
1152 &Scalar::Int((100).into())
1153 );
1154 }
1155
1156 #[test]
1157 fn remove_drops_every_edge_with_that_label() {
1158 let mut doc = Doc::of(&obj(&[
1159 ("x", Value::Int((1).into())),
1160 ("x", Value::Int((2).into())),
1161 ]))
1162 .unwrap();
1163 let root_id = doc.root().id();
1164 let root_path = doc.root().path.clone();
1165 doc.remove(root_id, &root_path, "x").unwrap();
1166 assert_eq!(doc.root().count("x"), 0);
1167 }
1168
1169 #[test]
1170 fn internal_edges_mut_rejects_a_leaf_directly() {
1171 let mut doc = Doc::of(&Value::Int((1).into())).unwrap();
1176 let root_id = doc.root().id();
1177 let err = doc.internal_edges_mut(root_id, "$", "poke").unwrap_err();
1178 assert_eq!(err.path, "$");
1179 assert!(err.message.contains("cannot poke on a leaf"));
1180 }
1181
1182 #[test]
1183 fn mutation_on_a_leaf_is_rejected() {
1184 let mut doc = Doc::of(&Value::Int((1).into())).unwrap();
1185 let root_id = doc.root().id();
1186 let root_path = doc.root().path.clone();
1187 assert!(
1188 doc.add(root_id, &root_path, "x", &Value::Int((1).into()))
1189 .is_err()
1190 );
1191 assert!(
1192 doc.set(root_id, &root_path, "x", &Value::Int((1).into()))
1193 .is_err()
1194 );
1195 assert!(doc.remove(root_id, &root_path, "x").is_err());
1196 }
1197
1198 #[test]
1199 fn value_on_an_internal_node_is_rejected() {
1200 let doc = Doc::of(&obj(&[("x", Value::Int((1).into()))])).unwrap();
1201 assert!(doc.root().value().is_err());
1202 }
1203
1204 #[test]
1205 fn edges_on_a_leaf_is_rejected() {
1206 let doc = Doc::of(&Value::Int((1).into())).unwrap();
1207 assert!(doc.root().edges().is_err());
1208 }
1209
1210 #[test]
1211 fn raw_edges_on_a_leaf_is_rejected() {
1212 let doc = Doc::of(&Value::Int((1).into())).unwrap();
1215 assert!(doc.root().raw_edges().is_err());
1216 }
1217
1218 #[test]
1221 fn to_data_round_trips_structure() {
1222 let v = obj(&[
1223 ("a", Value::Int((1).into())),
1224 ("b", Value::Str("hi".to_string())),
1225 ]);
1226 let doc = Doc::of(&v).unwrap();
1227 assert_eq!(doc.to_data(), v);
1228 }
1229
1230 #[test]
1231 fn to_data_round_trips_every_scalar_variant() {
1232 let v = obj(&[
1236 ("n", Value::Null),
1237 ("b", Value::Bool(true)),
1238 ("i", Value::Int((7).into())),
1239 ("f", Value::Float(1.5)),
1240 ("s", Value::Str("hi".to_string())),
1241 ]);
1242 let doc = Doc::of(&v).unwrap();
1243 assert_eq!(doc.to_data(), v);
1244 assert_eq!(doc.to_grouped(), v);
1245 }
1246
1247 #[test]
1248 fn join_quotes_a_non_identifier_key() {
1249 let v = obj(&[(
1252 "1bad",
1253 Value::Array(vec![Value::Array(vec![Value::Int((1).into())])]),
1254 )]);
1255 let err = Doc::of(&v).unwrap_err();
1256 assert_eq!(err.path, "$[\"1bad\"][0]");
1257 }
1258
1259 #[test]
1260 fn eq_doc_compares_structurally() {
1261 let a = Doc::of(&obj(&[("a", Value::Int((1).into()))])).unwrap();
1262 let b = Doc::of(&obj(&[("a", Value::Int((1).into()))])).unwrap();
1263 let c = Doc::of(&obj(&[("a", Value::Int((2).into()))])).unwrap();
1264 assert!(a.eq_doc(&b));
1265 assert!(!a.eq_doc(&c));
1266 }
1267
1268 #[test]
1269 fn eq_doc_is_false_when_shapes_differ() {
1270 let leaf = Doc::of(&Value::Int((1).into())).unwrap();
1273 let internal = Doc::of(&obj(&[("a", Value::Int((1).into()))])).unwrap();
1274 assert!(!leaf.eq_doc(&internal));
1275 assert!(!internal.eq_doc(&leaf));
1276 }
1277
1278 #[test]
1279 fn scalar_display_covers_every_variant() {
1280 assert_eq!(Scalar::Null.to_string(), "null");
1281 assert_eq!(Scalar::Bool(true).to_string(), "true");
1282 assert_eq!(Scalar::Int((1).into()).to_string(), "1");
1283 assert_eq!(Scalar::Float(1.5).to_string(), "1.5");
1284 assert_eq!(Scalar::Str("x".to_string()).to_string(), "\"x\"");
1285 }
1286
1287 #[test]
1290 fn from_raw_to_raw_round_trips_interleaved_repeated_labels() {
1291 let raw = RawNode::Edges(vec![
1295 ("b".to_string(), RawNode::Leaf(Scalar::Int((1).into()))),
1296 ("c".to_string(), RawNode::Leaf(Scalar::Int((2).into()))),
1297 ("b".to_string(), RawNode::Leaf(Scalar::Int((3).into()))),
1298 ]);
1299 let doc = Doc::from_raw(raw.clone()).unwrap();
1300 assert_eq!(doc.to_raw(), raw);
1301 let labels: Vec<String> = doc
1302 .root()
1303 .edges()
1304 .unwrap()
1305 .into_iter()
1306 .map(|(l, _)| l)
1307 .collect();
1308 assert_eq!(labels, vec!["b", "c", "b"]);
1309 }
1310
1311 #[test]
1312 fn from_raw_leaf_round_trips() {
1313 let raw = RawNode::Leaf(Scalar::Str("hi".to_string()));
1314 let doc = Doc::from_raw(raw.clone()).unwrap();
1315 assert!(doc.root().is_leaf());
1316 assert_eq!(doc.to_raw(), raw);
1317 }
1318
1319 #[test]
1320 fn from_raw_enforces_the_depth_guard() {
1321 fn nest_raw(levels: usize) -> RawNode {
1322 let mut n = RawNode::Leaf(Scalar::Int((0).into()));
1323 for _ in 0..levels {
1324 n = RawNode::Edges(vec![("a".to_string(), n)]);
1325 }
1326 n
1327 }
1328 assert!(Doc::from_raw(nest_raw(MAX_DEPTH)).is_ok());
1329 let err = Doc::from_raw(nest_raw(MAX_DEPTH + 1)).unwrap_err();
1330 assert!(err.message.contains("maximum depth"));
1331 }
1332}