diff --git a/src/lib.rs b/src/lib.rs index 967bcb1..a7c2225 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -282,6 +282,15 @@ impl<'a, T: 'a> NodeRef<'a, T> { .map(|(_, id)| unsafe { self.tree.get_unchecked(id) }) } + /// Returns the index of the given child or None if child doesn't exist. + /// This function may take up to linear time in worst case scenarios. + pub fn index_of_child(&self, child: &NodeRef) -> Option { + self.children() + .enumerate() + .find(|(_, each)| each == child) + .map(|(i, _)| i) + } + /// Returns true if this node has siblings. pub fn has_siblings(&self) -> bool { self.node.prev_sibling.is_some() || self.node.next_sibling.is_some() @@ -365,6 +374,17 @@ impl<'a, T: 'a> NodeMut<'a, T> { self.prepend_id(id) } + /// Insert a new child into this node at given index. + /// This function may take up to linear time in worst case scenarios. + /// + /// # Panics + /// + /// Panics if `index` is not valid. + pub fn insert(&mut self, value: T, index: usize) -> NodeMut { + let id = self.tree.orphan(value).id; + self.insert_id(id, index) + } + /// Appends a subtree, return the root of the merged subtree. pub fn append_subtree(&mut self, subtree: Tree) -> NodeMut { let root_id = self.tree.extend_tree(subtree).id; @@ -465,6 +485,31 @@ impl<'a, T: 'a> NodeMut<'a, T> { unsafe { self.tree.get_unchecked_mut(new_child_id) } } + /// Insert a child into this node at given index. + /// This function may take up to linear time in worst case scenarios. + /// + /// # Panics + /// + /// Panics if `new_child_id` or `index` are not valid. + pub fn insert_id(&mut self, new_child_id: NodeId, index: usize) -> NodeMut { + if index == 0 { + return self.prepend_id(new_child_id); + } + + let mut pre_sibling: NodeMut = unsafe { + self.tree + .get_unchecked(self.id) + .children() + .nth(index - 1) // worst case O(n) + .map(|node| node.id) + .map(|id| self.tree.get_unchecked_mut(id)) + .unwrap_or_else(|| panic!("No child found at index {}", index - 1)) + }; + + pre_sibling.insert_id_after(new_child_id); + unsafe { self.tree.get_unchecked_mut(new_child_id) } + } + /// Prepends a child to this node. /// /// # Panics diff --git a/tests/node_mut.rs b/tests/node_mut.rs old mode 100644 new mode 100755 index d9b00af..2c82104 --- a/tests/node_mut.rs +++ b/tests/node_mut.rs @@ -199,6 +199,47 @@ fn prepend_3() { assert_eq!(None, d.next_sibling()); } +#[test] +fn insert() { + let mut tree = tree!('a'); + tree.root_mut().insert('c', 0); + tree.root_mut().insert('b', 0); + tree.root_mut().insert('d', 2); + + let root = tree.root(); + let b = root.first_child().unwrap(); + let c = b.next_sibling().unwrap(); + let d = root.last_child().unwrap(); + + assert_eq!(&'b', b.value()); + assert_eq!(&'c', c.value()); + assert_eq!(&'d', d.value()); + assert_eq!(Some(root), b.parent()); + assert_eq!(Some(root), c.parent()); + assert_eq!(Some(root), d.parent()); + assert_eq!(None, b.prev_sibling()); + assert_eq!(Some(c), b.next_sibling()); + assert_eq!(Some(b), c.prev_sibling()); + assert_eq!(Some(d), c.next_sibling()); + assert_eq!(Some(c), d.prev_sibling()); + assert_eq!(None, d.next_sibling()); +} + +#[test] +#[should_panic] +fn insert_should_panic_1() { + let mut tree = tree!('a'); + tree.root_mut().insert('b', 1); +} + +#[test] +#[should_panic] +fn insert_should_panic_2() { + let mut tree = tree!('a'); + tree.root_mut().insert('b', 0); + tree.root_mut().insert('c', 2); +} + #[test] fn insert_before_first() { let mut tree = tree!('a' => { 'c' }); diff --git a/tests/node_ref.rs b/tests/node_ref.rs index c46cb65..490015c 100644 --- a/tests/node_ref.rs +++ b/tests/node_ref.rs @@ -40,6 +40,18 @@ fn last_child() { assert_eq!(&'c', tree.root().last_child().unwrap().value()); } +#[test] +fn index_of_child() { + let tree = tree!('a' => { 'b', 'c' }); + let root = tree.root(); + let b = root.first_child().unwrap(); + let c = root.last_child().unwrap(); + + assert_eq!(0, root.index_of_child(&b).unwrap()); + assert_eq!(1, root.index_of_child(&c).unwrap()); + assert!(root.index_of_child(&root).is_none()); +} + #[test] fn has_siblings() { let tree = tree!('a' => { 'b', 'c' });