1use std::collections::HashSet;
6
7use borsh::{BorshDeserialize, BorshSerialize};
8
9pub use calimero_primitives::crdt::CrdtType;
11
12pub const MAX_NODES_PER_RESPONSE: usize = 1000;
21
22pub const MAX_CHILDREN_PER_NODE: usize = 256;
26
27pub const MAX_LEAF_VALUE_SIZE: usize = 1_048_576;
32
33pub const MAX_TREE_DEPTH: usize = 64;
38
39#[derive(Clone, Debug, PartialEq, BorshSerialize, BorshDeserialize)]
48pub struct TreeNodeRequest {
49 pub node_id: [u8; 32],
51
52 max_depth: Option<usize>,
61}
62
63impl TreeNodeRequest {
64 #[must_use]
66 pub fn new(node_id: [u8; 32]) -> Self {
67 Self {
68 node_id,
69 max_depth: None,
70 }
71 }
72
73 #[must_use]
75 pub fn with_depth(node_id: [u8; 32], max_depth: usize) -> Self {
76 Self {
77 node_id,
78 max_depth: Some(max_depth.min(MAX_TREE_DEPTH)),
80 }
81 }
82
83 #[must_use]
85 pub fn root(root_hash: [u8; 32]) -> Self {
86 Self::new(root_hash)
87 }
88
89 #[must_use]
96 pub fn depth(&self) -> Option<usize> {
97 self.max_depth.map(|d| d.min(MAX_TREE_DEPTH))
98 }
99}
100
101#[derive(Clone, Debug, PartialEq, BorshSerialize, BorshDeserialize)]
103pub struct TreeNodeResponse {
104 pub nodes: Vec<TreeNode>,
109
110 pub not_found: bool,
112}
113
114impl TreeNodeResponse {
115 #[must_use]
117 pub fn new(nodes: Vec<TreeNode>) -> Self {
118 Self {
119 nodes,
120 not_found: false,
121 }
122 }
123
124 #[must_use]
126 pub fn not_found() -> Self {
127 Self {
128 nodes: vec![],
129 not_found: true,
130 }
131 }
132
133 #[must_use]
135 pub fn has_leaves(&self) -> bool {
136 self.nodes.iter().any(|n| n.is_leaf())
137 }
138
139 pub fn leaves(&self) -> impl Iterator<Item = &TreeNode> {
144 self.nodes.iter().filter(|n| n.is_leaf())
145 }
146
147 #[must_use]
153 pub fn is_valid(&self) -> bool {
154 self.nodes.len() <= MAX_NODES_PER_RESPONSE && self.nodes.iter().all(TreeNode::is_valid)
155 }
156}
157
158#[derive(Clone, Debug, PartialEq, BorshSerialize, BorshDeserialize)]
166pub struct TreeNode {
167 pub id: [u8; 32],
173
174 pub hash: [u8; 32],
180
181 pub children: Vec<[u8; 32]>,
186
187 pub leaf_data: Option<TreeLeafData>,
189}
190
191impl TreeNode {
192 #[must_use]
194 pub fn internal(id: [u8; 32], hash: [u8; 32], children: Vec<[u8; 32]>) -> Self {
195 Self {
196 id,
197 hash,
198 children,
199 leaf_data: None,
200 }
201 }
202
203 #[must_use]
205 pub fn leaf(id: [u8; 32], hash: [u8; 32], data: TreeLeafData) -> Self {
206 Self {
207 id,
208 hash,
209 children: vec![],
210 leaf_data: Some(data),
211 }
212 }
213
214 #[must_use]
222 pub fn is_valid(&self) -> bool {
223 if self.children.len() > MAX_CHILDREN_PER_NODE {
225 return false;
226 }
227
228 let has_children = !self.children.is_empty();
232 let has_data = self.leaf_data.is_some();
233 if has_children == has_data {
234 return false;
236 }
237
238 if let Some(ref leaf_data) = self.leaf_data {
240 if !leaf_data.is_valid() {
241 return false;
242 }
243 }
244
245 true
246 }
247
248 #[must_use]
250 pub fn is_leaf(&self) -> bool {
251 self.leaf_data.is_some()
252 }
253
254 #[must_use]
256 pub fn is_internal(&self) -> bool {
257 self.leaf_data.is_none()
258 }
259
260 #[must_use]
262 pub fn child_count(&self) -> usize {
263 self.children.len()
264 }
265}
266
267#[derive(Clone, Debug, PartialEq, BorshSerialize, BorshDeserialize)]
276pub struct TreeLeafData {
277 pub key: [u8; 32],
279
280 pub value: Vec<u8>,
282
283 pub metadata: LeafMetadata,
286}
287
288impl TreeLeafData {
289 #[must_use]
291 pub fn new(key: [u8; 32], value: Vec<u8>, metadata: LeafMetadata) -> Self {
292 Self {
293 key,
294 value,
295 metadata,
296 }
297 }
298
299 #[must_use]
303 pub fn is_valid(&self) -> bool {
304 self.value.len() <= MAX_LEAF_VALUE_SIZE
305 }
306}
307
308#[derive(Clone, Debug, PartialEq, BorshSerialize, BorshDeserialize)]
319pub struct LeafMetadata {
320 pub crdt_type: CrdtType,
322
323 pub hlc_timestamp: u64,
325
326 pub version: u64,
328
329 pub collection_id: [u8; 32],
331
332 pub parent_id: Option<[u8; 32]>,
334}
335
336impl LeafMetadata {
337 #[must_use]
339 pub fn new(crdt_type: CrdtType, hlc_timestamp: u64, collection_id: [u8; 32]) -> Self {
340 Self {
341 crdt_type,
342 hlc_timestamp,
343 version: 0,
344 collection_id,
345 parent_id: None,
346 }
347 }
348
349 #[must_use]
351 pub fn with_version(mut self, version: u64) -> Self {
352 self.version = version;
353 self
354 }
355
356 #[must_use]
358 pub fn with_parent(mut self, parent_id: [u8; 32]) -> Self {
359 self.parent_id = Some(parent_id);
360 self
361 }
362}
363
364#[derive(Clone, Debug, PartialEq, BorshSerialize, BorshDeserialize)]
376pub enum TreeCompareResult {
377 Equal,
379 Different {
385 remote_only_children: Vec<[u8; 32]>,
387 local_only_children: Vec<[u8; 32]>,
389 common_children: Vec<[u8; 32]>,
392 },
393 LocalMissing,
395 RemoteMissing,
398}
399
400impl TreeCompareResult {
401 #[must_use]
405 pub fn needs_sync(&self) -> bool {
406 !matches!(self, Self::Equal | Self::RemoteMissing)
407 }
408
409 #[must_use]
417 pub fn needs_push(&self) -> bool {
418 match self {
419 Self::RemoteMissing => true,
420 Self::Different {
421 remote_only_children,
422 local_only_children,
423 common_children,
424 } => {
425 if !local_only_children.is_empty() {
427 return true;
428 }
429 remote_only_children.is_empty() && common_children.is_empty()
433 }
434 _ => false,
435 }
436 }
437}
438
439#[must_use]
463pub fn compare_tree_nodes(
464 local: Option<&TreeNode>,
465 remote: Option<&TreeNode>,
466) -> TreeCompareResult {
467 match (local, remote) {
468 (None, None) => TreeCompareResult::Equal,
469 (None, Some(_)) => TreeCompareResult::LocalMissing,
470 (Some(_), None) => TreeCompareResult::RemoteMissing,
471 (Some(local_node), Some(remote_node)) => {
472 debug_assert_eq!(
474 local_node.id, remote_node.id,
475 "compare_tree_nodes called with nodes at different tree positions"
476 );
477
478 if local_node.hash == remote_node.hash {
479 TreeCompareResult::Equal
480 } else {
481 let local_children: HashSet<&[u8; 32]> = local_node.children.iter().collect();
483 let remote_children: HashSet<&[u8; 32]> = remote_node.children.iter().collect();
484
485 let remote_only_children: Vec<[u8; 32]> = remote_node
487 .children
488 .iter()
489 .filter(|child_id| !local_children.contains(child_id))
490 .copied()
491 .collect();
492
493 let local_only_children: Vec<[u8; 32]> = local_node
495 .children
496 .iter()
497 .filter(|child_id| !remote_children.contains(child_id))
498 .copied()
499 .collect();
500
501 let common_children: Vec<[u8; 32]> = local_node
504 .children
505 .iter()
506 .filter(|child_id| remote_children.contains(child_id))
507 .copied()
508 .collect();
509
510 TreeCompareResult::Different {
511 remote_only_children,
512 local_only_children,
513 common_children,
514 }
515 }
516 }
517 }
518}
519
520#[cfg(test)]
525mod tests {
526 use super::*;
527
528 #[test]
529 fn test_tree_node_request_roundtrip() {
530 let request = TreeNodeRequest::with_depth([1; 32], 3);
531
532 let encoded = borsh::to_vec(&request).expect("serialize");
533 let decoded: TreeNodeRequest = borsh::from_slice(&encoded).expect("deserialize");
534
535 assert_eq!(request, decoded);
536 assert_eq!(decoded.max_depth, Some(3));
537 }
538
539 #[test]
540 fn test_tree_node_request_root() {
541 let root_hash = [42; 32];
542 let request = TreeNodeRequest::root(root_hash);
543
544 assert_eq!(request.node_id, root_hash);
545 assert!(request.max_depth.is_none());
546 }
547
548 #[test]
549 fn test_tree_node_internal() {
550 let node = TreeNode::internal([1; 32], [2; 32], vec![[3; 32], [4; 32]]);
551
552 assert!(node.is_internal());
553 assert!(!node.is_leaf());
554 assert_eq!(node.child_count(), 2);
555 assert!(node.leaf_data.is_none());
556 }
557
558 #[test]
559 fn test_tree_node_leaf() {
560 let metadata = LeafMetadata::new(CrdtType::lww_register("test"), 12345, [5; 32]);
561 let leaf_data = TreeLeafData::new([1; 32], vec![1, 2, 3], metadata);
562 let node = TreeNode::leaf([2; 32], [3; 32], leaf_data);
563
564 assert!(node.is_leaf());
565 assert!(!node.is_internal());
566 assert_eq!(node.child_count(), 0);
567 assert!(node.leaf_data.is_some());
568 }
569
570 #[test]
571 fn test_tree_node_roundtrip() {
572 let metadata = LeafMetadata::new(CrdtType::unordered_map("String", "u64"), 999, [6; 32])
573 .with_version(5)
574 .with_parent([7; 32]);
575 let leaf_data = TreeLeafData::new([1; 32], vec![4, 5, 6], metadata);
576 let node = TreeNode::leaf([2; 32], [3; 32], leaf_data);
577
578 let encoded = borsh::to_vec(&node).expect("serialize");
579 let decoded: TreeNode = borsh::from_slice(&encoded).expect("deserialize");
580
581 assert_eq!(node, decoded);
582 }
583
584 #[test]
585 fn test_tree_node_response_roundtrip() {
586 let internal = TreeNode::internal([1; 32], [2; 32], vec![[3; 32]]);
587 let metadata = LeafMetadata::new(CrdtType::Rga, 100, [4; 32]);
588 let leaf_data = TreeLeafData::new([5; 32], vec![7, 8, 9], metadata);
589 let leaf = TreeNode::leaf([6; 32], [7; 32], leaf_data);
590
591 let response = TreeNodeResponse::new(vec![internal, leaf]);
592
593 let encoded = borsh::to_vec(&response).expect("serialize");
594 let decoded: TreeNodeResponse = borsh::from_slice(&encoded).expect("deserialize");
595
596 assert_eq!(response, decoded);
597 assert!(decoded.has_leaves());
598 assert_eq!(decoded.leaves().count(), 1);
599 }
600
601 #[test]
602 fn test_tree_node_response_not_found() {
603 let response = TreeNodeResponse::not_found();
604
605 assert!(response.not_found);
606 assert!(response.nodes.is_empty());
607 assert!(!response.has_leaves());
608 }
609
610 #[test]
611 fn test_leaf_metadata_builder() {
612 let metadata = LeafMetadata::new(CrdtType::PnCounter, 500, [1; 32])
613 .with_version(10)
614 .with_parent([2; 32]);
615
616 assert_eq!(metadata.crdt_type, CrdtType::PnCounter);
617 assert_eq!(metadata.hlc_timestamp, 500);
618 assert_eq!(metadata.version, 10);
619 assert_eq!(metadata.parent_id, Some([2; 32]));
620 }
621
622 #[test]
623 fn test_crdt_type_variants() {
624 let types = vec![
625 CrdtType::lww_register("test"),
626 CrdtType::GCounter,
627 CrdtType::PnCounter,
628 CrdtType::Rga,
629 CrdtType::unordered_map("String", "u64"),
630 CrdtType::unordered_set("String"),
631 CrdtType::vector("u64"),
632 CrdtType::UserStorage,
633 CrdtType::FrozenStorage,
634 CrdtType::Custom("test".to_string()),
635 ];
636
637 for crdt_type in types {
638 let encoded = borsh::to_vec(&crdt_type).expect("serialize");
639 let decoded: CrdtType = borsh::from_slice(&encoded).expect("deserialize");
640 assert_eq!(crdt_type, decoded);
641 }
642 }
643
644 #[test]
645 fn test_compare_tree_nodes_equal() {
646 let local = TreeNode::internal([1; 32], [99; 32], vec![[2; 32]]);
647 let remote = TreeNode::internal([1; 32], [99; 32], vec![[2; 32]]);
648
649 let result = compare_tree_nodes(Some(&local), Some(&remote));
650 assert_eq!(result, TreeCompareResult::Equal);
651 assert!(!result.needs_sync());
652 }
653
654 #[test]
655 fn test_compare_tree_nodes_local_missing() {
656 let remote = TreeNode::internal([1; 32], [2; 32], vec![[3; 32]]);
657
658 let result = compare_tree_nodes(None, Some(&remote));
659 assert_eq!(result, TreeCompareResult::LocalMissing);
660 assert!(result.needs_sync());
661 }
662
663 #[test]
664 fn test_compare_tree_nodes_different() {
665 let local = TreeNode::internal([1; 32], [10; 32], vec![[2; 32]]);
666 let remote = TreeNode::internal([1; 32], [20; 32], vec![[2; 32], [3; 32]]);
667
668 let result = compare_tree_nodes(Some(&local), Some(&remote));
669
670 match &result {
671 TreeCompareResult::Different {
672 remote_only_children,
673 local_only_children: _,
674 common_children,
675 } => {
676 assert!(remote_only_children.contains(&[3; 32]));
678 assert!(common_children.contains(&[2; 32]));
680 }
681 _ => panic!("Expected Different result"),
682 }
683 assert!(result.needs_sync());
684 }
685
686 #[test]
687 fn test_tree_compare_result_needs_sync() {
688 assert!(!TreeCompareResult::Equal.needs_sync());
689 assert!(!TreeCompareResult::RemoteMissing.needs_sync());
690 assert!(TreeCompareResult::LocalMissing.needs_sync());
691 assert!(TreeCompareResult::Different {
692 remote_only_children: vec![],
693 local_only_children: vec![],
694 common_children: vec![],
695 }
696 .needs_sync());
697 }
698
699 #[test]
700 fn test_tree_compare_result_roundtrip() {
701 let variants = vec![
702 TreeCompareResult::Equal,
703 TreeCompareResult::LocalMissing,
704 TreeCompareResult::RemoteMissing,
705 TreeCompareResult::Different {
706 remote_only_children: vec![[1; 32], [2; 32]],
707 local_only_children: vec![[3; 32]],
708 common_children: vec![[4; 32], [5; 32], [6; 32]],
709 },
710 TreeCompareResult::Different {
711 remote_only_children: vec![],
712 local_only_children: vec![],
713 common_children: vec![],
714 },
715 ];
716
717 for original in variants {
718 let encoded = borsh::to_vec(&original).expect("encode");
719 let decoded: TreeCompareResult = borsh::from_slice(&encoded).expect("decode");
720 assert_eq!(original, decoded);
721 }
722 }
723
724 #[test]
725 fn test_compare_tree_nodes_leaf_content_differs() {
726 let local_metadata = LeafMetadata::new(CrdtType::lww_register("test"), 100, [1; 32]);
727 let local_leaf = TreeLeafData::new([10; 32], vec![1, 2, 3], local_metadata);
728 let local = TreeNode::leaf([1; 32], [100; 32], local_leaf);
729
730 let remote_metadata = LeafMetadata::new(CrdtType::lww_register("test"), 200, [1; 32]);
731 let remote_leaf = TreeLeafData::new([10; 32], vec![4, 5, 6], remote_metadata);
732 let remote = TreeNode::leaf([1; 32], [200; 32], remote_leaf);
733
734 let result = compare_tree_nodes(Some(&local), Some(&remote));
735
736 match &result {
737 TreeCompareResult::Different {
738 remote_only_children,
739 local_only_children,
740 common_children,
741 } => {
742 assert!(remote_only_children.is_empty());
743 assert!(local_only_children.is_empty());
744 assert!(common_children.is_empty());
745 }
746 _ => panic!("Expected Different result for leaves with different content"),
747 }
748 assert!(result.needs_sync());
749 assert!(result.needs_push());
750 }
751
752 #[test]
753 fn test_compare_tree_nodes_remote_missing() {
754 let local = TreeNode::internal([1; 32], [2; 32], vec![[3; 32]]);
755
756 let result = compare_tree_nodes(Some(&local), None);
757 assert_eq!(result, TreeCompareResult::RemoteMissing);
758 assert!(!result.needs_sync());
759 }
760
761 #[test]
762 fn test_compare_tree_nodes_local_only_children() {
763 let local = TreeNode::internal([1; 32], [10; 32], vec![[2; 32], [3; 32], [4; 32]]);
764 let remote = TreeNode::internal([1; 32], [20; 32], vec![[2; 32], [5; 32]]);
765
766 let result = compare_tree_nodes(Some(&local), Some(&remote));
767
768 match &result {
769 TreeCompareResult::Different {
770 remote_only_children,
771 local_only_children,
772 common_children,
773 } => {
774 assert!(remote_only_children.contains(&[5; 32]));
775 assert!(local_only_children.contains(&[3; 32]));
776 assert!(local_only_children.contains(&[4; 32]));
777 assert!(common_children.contains(&[2; 32]));
778 }
779 _ => panic!("Expected Different result"),
780 }
781 }
782
783 #[test]
784 fn test_tree_node_request_max_depth_validation() {
785 let request = TreeNodeRequest::with_depth([1; 32], MAX_TREE_DEPTH);
787 assert_eq!(request.depth(), Some(MAX_TREE_DEPTH));
788
789 let excessive = TreeNodeRequest::with_depth([1; 32], MAX_TREE_DEPTH + 100);
791 assert_eq!(excessive.depth(), Some(MAX_TREE_DEPTH));
792 }
793
794 #[test]
795 fn test_tree_node_request_depth_accessor() {
796 let request_none = TreeNodeRequest::new([1; 32]);
798 assert_eq!(request_none.depth(), None);
799
800 let request_with_depth = TreeNodeRequest::with_depth([1; 32], 5);
802 assert_eq!(request_with_depth.depth(), Some(5));
803 }
804
805 #[test]
806 fn test_tree_node_request_depth_clamping_on_deserialize() {
807 let node_id = [1u8; 32];
810 let malicious_depth: usize = usize::MAX;
811
812 let mut bytes = Vec::new();
814 bytes.extend_from_slice(&node_id);
816 bytes.push(1); bytes.extend_from_slice(&malicious_depth.to_le_bytes());
819
820 let request: TreeNodeRequest = borsh::from_slice(&bytes).expect("deserialize");
822
823 assert_eq!(
825 request.depth(),
826 Some(MAX_TREE_DEPTH),
827 "depth() must clamp deserialized values to MAX_TREE_DEPTH"
828 );
829 }
830
831 #[test]
832 fn test_tree_node_request_private_field_enforces_validation() {
833 let no_depth = TreeNodeRequest::new([0; 32]);
841 assert_eq!(no_depth.depth(), None);
842
843 let clamped = TreeNodeRequest::with_depth([0; 32], 999_999);
845 assert_eq!(clamped.depth(), Some(MAX_TREE_DEPTH));
846
847 let reasonable = TreeNodeRequest::with_depth([0; 32], 3);
849 assert_eq!(reasonable.depth(), Some(3));
850 }
851
852 #[test]
853 fn test_tree_node_response_validation() {
854 let valid_response =
855 TreeNodeResponse::new(vec![TreeNode::internal([1; 32], [2; 32], vec![[3; 32]])]);
856 assert!(valid_response.is_valid());
857
858 let metadata = LeafMetadata::new(CrdtType::lww_register("test"), 100, [1; 32]);
859 let leaf_data = TreeLeafData::new([10; 32], vec![1, 2, 3], metadata);
860 let leaf_response =
861 TreeNodeResponse::new(vec![TreeNode::leaf([1; 32], [2; 32], leaf_data)]);
862 assert!(leaf_response.is_valid());
863
864 let mut nodes = Vec::new();
865 for i in 0..MAX_NODES_PER_RESPONSE {
866 let id = [i as u8; 32];
867 nodes.push(TreeNode::internal(id, id, vec![[0; 32]]));
868 }
869 let at_limit = TreeNodeResponse::new(nodes);
870 assert!(at_limit.is_valid());
871 }
872
873 #[test]
874 fn test_tree_node_validation() {
875 let valid = TreeNode::internal([1; 32], [2; 32], vec![[3; 32], [4; 32]]);
876 assert!(valid.is_valid());
877
878 let children: Vec<[u8; 32]> = (0..MAX_CHILDREN_PER_NODE).map(|i| [i as u8; 32]).collect();
879 let at_limit = TreeNode::internal([1; 32], [2; 32], children);
880 assert!(at_limit.is_valid());
881
882 let over_children: Vec<[u8; 32]> =
883 (0..=MAX_CHILDREN_PER_NODE).map(|i| [i as u8; 32]).collect();
884 let over_limit = TreeNode::internal([1; 32], [2; 32], over_children);
885 assert!(!over_limit.is_valid());
886
887 let metadata = LeafMetadata::new(CrdtType::lww_register("test"), 100, [1; 32]);
888 let leaf_data = TreeLeafData::new([10; 32], vec![1, 2, 3], metadata);
889 let invalid_node = TreeNode {
890 id: [1; 32],
891 hash: [2; 32],
892 children: vec![[3; 32]],
893 leaf_data: Some(leaf_data),
894 };
895 assert!(!invalid_node.is_valid());
896
897 let valid_metadata = LeafMetadata::new(CrdtType::lww_register("test"), 100, [1; 32]);
898 let valid_leaf_data = TreeLeafData::new([10; 32], vec![1, 2, 3], valid_metadata);
899 let valid_leaf = TreeNode::leaf([1; 32], [2; 32], valid_leaf_data);
900 assert!(valid_leaf.is_valid());
901
902 let empty_node = TreeNode::internal([1; 32], [2; 32], vec![]);
903 assert!(!empty_node.is_valid());
904 }
905
906 #[test]
907 fn test_tree_node_response_validation_over_limit() {
908 let mut nodes = Vec::new();
909 for i in 0..=MAX_NODES_PER_RESPONSE {
910 let id = [i as u8; 32];
911 nodes.push(TreeNode::internal(id, id, vec![[0; 32]]));
912 }
913 let over_limit = TreeNodeResponse::new(nodes);
914 assert!(!over_limit.is_valid());
915
916 let over_children: Vec<[u8; 32]> =
917 (0..=MAX_CHILDREN_PER_NODE).map(|i| [i as u8; 32]).collect();
918 let invalid_node = TreeNode::internal([1; 32], [2; 32], over_children);
919 let response_with_invalid = TreeNodeResponse::new(vec![invalid_node]);
920 assert!(!response_with_invalid.is_valid());
921
922 let empty_node = TreeNode::internal([1; 32], [2; 32], vec![]);
923 let response_with_empty = TreeNodeResponse::new(vec![empty_node]);
924 assert!(!response_with_empty.is_valid());
925 }
926
927 #[test]
928 fn test_tree_leaf_data_validation() {
929 let metadata = LeafMetadata::new(CrdtType::lww_register("test"), 100, [1; 32]);
930
931 let valid = TreeLeafData::new([1; 32], vec![1, 2, 3], metadata.clone());
932 assert!(valid.is_valid());
933
934 let at_limit_value = vec![0u8; MAX_LEAF_VALUE_SIZE];
935 let at_limit = TreeLeafData::new([1; 32], at_limit_value, metadata.clone());
936 assert!(at_limit.is_valid());
937
938 let over_limit_value = vec![0u8; MAX_LEAF_VALUE_SIZE + 1];
939 let over_limit = TreeLeafData::new([1; 32], over_limit_value, metadata);
940 assert!(!over_limit.is_valid());
941 }
942
943 #[test]
944 fn test_tree_compare_result_needs_push() {
945 assert!(TreeCompareResult::RemoteMissing.needs_push());
946
947 let with_local_only = TreeCompareResult::Different {
948 remote_only_children: vec![],
949 local_only_children: vec![[1; 32]],
950 common_children: vec![],
951 };
952 assert!(with_local_only.needs_push());
953
954 let with_remote_only = TreeCompareResult::Different {
955 remote_only_children: vec![[1; 32]],
956 local_only_children: vec![],
957 common_children: vec![],
958 };
959 assert!(!with_remote_only.needs_push());
960
961 let with_common_only = TreeCompareResult::Different {
962 remote_only_children: vec![],
963 local_only_children: vec![],
964 common_children: vec![[1; 32]],
965 };
966 assert!(!with_common_only.needs_push());
967
968 let differing_leaves = TreeCompareResult::Different {
969 remote_only_children: vec![],
970 local_only_children: vec![],
971 common_children: vec![],
972 };
973 assert!(differing_leaves.needs_push());
974
975 assert!(!TreeCompareResult::Equal.needs_push());
976 assert!(!TreeCompareResult::LocalMissing.needs_push());
977 }
978}