From 26a984d2b82ff67eccf3b22faf8f2c364204e897 Mon Sep 17 00:00:00 2001 From: orxfun Date: Wed, 26 Nov 2025 16:30:47 +0100 Subject: [PATCH 1/8] create failing test for issue #183 --- tests/leaf_nodes.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 tests/leaf_nodes.rs diff --git a/tests/leaf_nodes.rs b/tests/leaf_nodes.rs new file mode 100644 index 0000000..a780f67 --- /dev/null +++ b/tests/leaf_nodes.rs @@ -0,0 +1,19 @@ +// tests on nodes without any children + +use orx_tree::*; + +/// https://github.com/orxfun/orx-tree/issues/183 +#[test] +fn leaf_into_new_tree() { + let mut tree = DynTree::new(0); + tree.root_mut().push_child(1); + + // Does not panic if we add a children to 1 + // tree.root_mut().children_mut().next().unwrap().push_child(2); + let tree2: DynTree<_> = tree + .root_mut() + .children_mut() + .nth(0) + .unwrap() + .into_new_tree(); +} From c36e03f6608ecad909f89cf436cc08f349144cd1 Mon Sep 17 00:00:00 2001 From: orxfun Date: Wed, 26 Nov 2025 16:45:50 +0100 Subject: [PATCH 2/8] fix into_new_tree with peekable --- src/common_traits/from_depth_first_iter.rs | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/common_traits/from_depth_first_iter.rs b/src/common_traits/from_depth_first_iter.rs index 33a608b..ef4234d 100644 --- a/src/common_traits/from_depth_first_iter.rs +++ b/src/common_traits/from_depth_first_iter.rs @@ -216,14 +216,18 @@ where Some((d, root)) => match d { 0 => { let mut tree = Tree::new_with_root(root); - match tree.root_mut().try_append_subtree_as_child(iter, 0) { - Ok(_) => Ok(tree), - Err((depth, succeeding_depth)) => { - Err(DepthFirstSequenceError::DepthIncreaseGreaterThanOne { - depth, - succeeding_depth, - }) - } + let mut peekable = iter.peekable(); + match peekable.peek().is_some() { + true => match tree.root_mut().try_append_subtree_as_child(peekable, 0) { + Ok(_) => Ok(tree), + Err((depth, succeeding_depth)) => { + Err(DepthFirstSequenceError::DepthIncreaseGreaterThanOne { + depth, + succeeding_depth, + }) + } + }, + false => Ok(tree), } } _ => Err(DepthFirstSequenceError::NonZeroRootDepth), From 2f7e2be91b9501b3d4482580d504c37bd7f7e3ac Mon Sep 17 00:00:00 2001 From: orxfun Date: Wed, 26 Nov 2025 16:54:48 +0100 Subject: [PATCH 3/8] avoid peekable if lower bound on the iterator is known and it is not zero --- src/common_traits/from_depth_first_iter.rs | 48 ++++++++++++++++------ 1 file changed, 36 insertions(+), 12 deletions(-) diff --git a/src/common_traits/from_depth_first_iter.rs b/src/common_traits/from_depth_first_iter.rs index ef4234d..1f2c1df 100644 --- a/src/common_traits/from_depth_first_iter.rs +++ b/src/common_traits/from_depth_first_iter.rs @@ -190,6 +190,8 @@ pub enum DepthFirstSequenceError { }, } +impl DepthFirstSequence where I: IntoIterator {} + impl TryFrom> for Tree where V: TreeVariant, @@ -215,19 +217,19 @@ where None => Ok(Tree::default()), Some((d, root)) => match d { 0 => { - let mut tree = Tree::new_with_root(root); - let mut peekable = iter.peekable(); - match peekable.peek().is_some() { - true => match tree.root_mut().try_append_subtree_as_child(peekable, 0) { - Ok(_) => Ok(tree), - Err((depth, succeeding_depth)) => { - Err(DepthFirstSequenceError::DepthIncreaseGreaterThanOne { - depth, - succeeding_depth, - }) + let tree = Tree::new_with_root(root); + + let (lb_len, _) = iter.size_hint(); + + match lb_len > 0 { + true => push_dfs_under_root(tree, iter), + false => { + let mut peekable = iter.peekable(); + match peekable.peek().is_some() { + true => push_dfs_under_root(tree, peekable), + false => Ok(tree), } - }, - false => Ok(tree), + } } } _ => Err(DepthFirstSequenceError::NonZeroRootDepth), @@ -235,3 +237,25 @@ where } } } + +fn push_dfs_under_root( + mut tree: Tree, + iter: I, +) -> Result, DepthFirstSequenceError> +where + V: TreeVariant, + M: MemoryPolicy, + P: PinnedStorage, + P::PinnedVec: Default, + I: IntoIterator, +{ + match tree.root_mut().try_append_subtree_as_child(iter, 0) { + Ok(_) => Ok(tree), + Err((depth, succeeding_depth)) => { + Err(DepthFirstSequenceError::DepthIncreaseGreaterThanOne { + depth, + succeeding_depth, + }) + } + } +} From e13fd6bb69b937e1447bd815975169d19cc84960 Mon Sep 17 00:00:00 2001 From: orxfun Date: Wed, 26 Nov 2025 16:56:47 +0100 Subject: [PATCH 4/8] add assertions to leaf_into_new_tree test --- tests/leaf_nodes.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/leaf_nodes.rs b/tests/leaf_nodes.rs index a780f67..9c95f35 100644 --- a/tests/leaf_nodes.rs +++ b/tests/leaf_nodes.rs @@ -16,4 +16,10 @@ fn leaf_into_new_tree() { .nth(0) .unwrap() .into_new_tree(); + + assert_eq!(tree2.len(), 1); + assert_eq!( + tree2.root().walk::().copied().collect::>(), + vec![1] + ); } From 0cfb79d1a9182079b1cdd9222c8f927b13961e22 Mon Sep 17 00:00:00 2001 From: orxfun Date: Wed, 26 Nov 2025 16:57:50 +0100 Subject: [PATCH 5/8] root_into_new_tree test --- tests/leaf_nodes.rs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/tests/leaf_nodes.rs b/tests/leaf_nodes.rs index 9c95f35..2f60b25 100644 --- a/tests/leaf_nodes.rs +++ b/tests/leaf_nodes.rs @@ -2,14 +2,25 @@ use orx_tree::*; +#[test] +fn root_into_new_tree() { + let mut tree = DynTree::new(0); + + let tree2: DynTree<_> = tree.root_mut().into_new_tree(); + + assert_eq!(tree2.len(), 1); + assert_eq!( + tree2.root().walk::().copied().collect::>(), + vec![0] + ); +} + /// https://github.com/orxfun/orx-tree/issues/183 #[test] fn leaf_into_new_tree() { let mut tree = DynTree::new(0); tree.root_mut().push_child(1); - // Does not panic if we add a children to 1 - // tree.root_mut().children_mut().next().unwrap().push_child(2); let tree2: DynTree<_> = tree .root_mut() .children_mut() From 32a966cdfd0a5b82840c3c576e59feed4b516882 Mon Sep 17 00:00:00 2001 From: orxfun Date: Wed, 26 Nov 2025 17:03:44 +0100 Subject: [PATCH 6/8] add leaf_into tests --- tests/leaf_nodes.rs | 60 +++++++++++++++++++++++++++++++++++---------- 1 file changed, 47 insertions(+), 13 deletions(-) diff --git a/tests/leaf_nodes.rs b/tests/leaf_nodes.rs index 2f60b25..22f9740 100644 --- a/tests/leaf_nodes.rs +++ b/tests/leaf_nodes.rs @@ -2,35 +2,69 @@ use orx_tree::*; +/// https://github.com/orxfun/orx-tree/issues/183 #[test] -fn root_into_new_tree() { +fn leaf_into_new_tree() { let mut tree = DynTree::new(0); + let idx = tree.root_mut().push_child(1); - let tree2: DynTree<_> = tree.root_mut().into_new_tree(); + let tree2: DynTree<_> = tree.node_mut(idx).into_new_tree(); assert_eq!(tree2.len(), 1); assert_eq!( tree2.root().walk::().copied().collect::>(), - vec![0] + vec![1] ); } -/// https://github.com/orxfun/orx-tree/issues/183 #[test] -fn leaf_into_new_tree() { +fn root_into_new_tree() { let mut tree = DynTree::new(0); - tree.root_mut().push_child(1); - let tree2: DynTree<_> = tree - .root_mut() - .children_mut() - .nth(0) - .unwrap() - .into_new_tree(); + let tree2: DynTree<_> = tree.root_mut().into_new_tree(); assert_eq!(tree2.len(), 1); assert_eq!( tree2.root().walk::().copied().collect::>(), - vec![1] + vec![0] ); } + +#[test] +fn leaf_into_walk() { + let mut tree = DynTree::new(0); + let idx = tree.root_mut().push_child(1); + + let values: Vec<_> = tree.node_mut(idx).into_walk::().collect(); + assert_eq!(values, vec![1]); + + let remaining: Vec<_> = tree.root().walk::().copied().collect(); + assert_eq!(remaining, vec![0]); +} + +#[test] +fn leaf_into_leaves() { + let mut tree = DynTree::new(0); + let idx = tree.root_mut().push_child(1); + + let values: Vec<_> = tree.node_mut(idx).into_leaves::().collect(); + assert_eq!(values, vec![1]); + + let remaining: Vec<_> = tree.root().walk::().copied().collect(); + assert_eq!(remaining, vec![0]); +} + +#[test] +fn leaf_into_subtree() { + let mut tree = DynTree::new(0); + let idx = tree.root_mut().push_child(1); + + let subtree = tree.node_mut(idx).into_subtree(); + let mut tree2 = DynTree::new(42); + tree2.root_mut().push_child_tree(subtree); + let values: Vec<_> = tree2.root().walk::().copied().collect(); + assert_eq!(values, vec![42, 1]); + + let remaining: Vec<_> = tree.root().walk::().copied().collect(); + assert_eq!(remaining, vec![0]); +} From 826b3abd30504a8c966e20d333b867cf32189167 Mon Sep 17 00:00:00 2001 From: orxfun Date: Wed, 26 Nov 2025 17:08:13 +0100 Subject: [PATCH 7/8] panics and safety documentations added --- src/common_traits/from_depth_first_iter.rs | 10 ++++++++++ src/node_mut.rs | 4 ++++ 2 files changed, 14 insertions(+) diff --git a/src/common_traits/from_depth_first_iter.rs b/src/common_traits/from_depth_first_iter.rs index 1f2c1df..4d8494b 100644 --- a/src/common_traits/from_depth_first_iter.rs +++ b/src/common_traits/from_depth_first_iter.rs @@ -219,6 +219,13 @@ where 0 => { let tree = Tree::new_with_root(root); + // # SAFETY: + // `push_dfs_under_root` panics if iter is empty. Therefore, + // + // * We first check if lower-bound(len) > 0, this allows us to directly use the iter + // which is faster. + // * Otherwise, we use a peekable iterator to make sure that the iterator has at least + // one element. let (lb_len, _) = iter.size_hint(); match lb_len > 0 { @@ -238,6 +245,9 @@ where } } +/// # Panics +/// +/// Panics if the `iter` is an empty iterator. It must contain at least one child node. fn push_dfs_under_root( mut tree: Tree, iter: I, diff --git a/src/node_mut.rs b/src/node_mut.rs index b31cf6b..91c7a27 100644 --- a/src/node_mut.rs +++ b/src/node_mut.rs @@ -3264,6 +3264,10 @@ where /// greater than one. /// The method returns the (depth, succeeding_depth) pair as the error when this error /// is observed. + /// + /// # Panics + /// + /// Panics if the `subtree` is an empty iterator. It must contain at least one child node. #[allow(clippy::unwrap_in_result)] pub(crate) fn try_append_subtree_as_child( &mut self, From 29756d85be3ab56a1eff1110f851bdf7c87dcd05 Mon Sep 17 00:00:00 2001 From: orxfun Date: Wed, 26 Nov 2025 17:09:23 +0100 Subject: [PATCH 8/8] extend panics documentation --- src/common_traits/from_depth_first_iter.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/common_traits/from_depth_first_iter.rs b/src/common_traits/from_depth_first_iter.rs index 4d8494b..0b3a96a 100644 --- a/src/common_traits/from_depth_first_iter.rs +++ b/src/common_traits/from_depth_first_iter.rs @@ -247,7 +247,10 @@ where /// # Panics /// -/// Panics if the `iter` is an empty iterator. It must contain at least one child node. +/// Panics +/// +/// * If the `iter` is an empty iterator; it must contain at least one child node. +/// * If the `tree` is empty, it must have at least the `root`. fn push_dfs_under_root( mut tree: Tree, iter: I,