use std::num::NonZeroUsize;
use std::ops::Index;
use std::hash::{Hash, Hasher};
use rpds::VectorSync;
use rpds::HashTrieMapSync;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use once_cell::sync::Lazy;
use dashmap::DashMap;
use ahash::{AHasher, RandomState};
use std::fmt::{self, Debug};
use crate::error::PoolResult;
use crate::node_definition::NodeTree;
use crate::{
error::error_helpers,
mark::Mark,
node::Node,
ops::{AttrsRef, MarkRef, NodeRef},
types::NodeId,
};
static SHARD_INDEX_CACHE: Lazy<DashMap<NodeId, usize, RandomState>> =
Lazy::new(|| DashMap::with_capacity_and_hasher(10000, RandomState::new()));
type TreeMap = HashTrieMapSync<NodeId, Node>;
type TreeParentMap = HashTrieMapSync<NodeId, NodeId>;
#[derive(Clone, PartialEq, Serialize, Deserialize)]
pub struct Tree {
pub root_id: NodeId,
pub nodes: VectorSync<TreeMap>, pub parent_map: TreeParentMap,
#[serde(skip)]
num_shards: usize, }
impl Debug for Tree {
fn fmt(
&self,
f: &mut fmt::Formatter<'_>,
) -> fmt::Result {
let nodes = self
.nodes
.iter()
.filter(|node| !node.is_empty())
.collect::<Vec<_>>();
f.debug_struct("Tree")
.field("root_id", &self.root_id)
.field("nodes", &nodes)
.field("parent_map", &self.parent_map)
.field("num_shards", &self.num_shards)
.finish()
}
}
impl Tree {
#[inline(always)]
pub fn get_shard_index(
&self,
id: &NodeId,
) -> usize {
if let Some(index) = SHARD_INDEX_CACHE.get(id) {
return *index;
}
self.compute_and_cache_shard_index(id)
}
#[cold]
#[inline(never)]
fn compute_and_cache_shard_index(
&self,
id: &NodeId,
) -> usize {
let mut hasher = AHasher::default();
id.hash(&mut hasher);
let index = (hasher.finish() as usize) % self.num_shards;
SHARD_INDEX_CACHE.insert(id.clone(), index);
index
}
#[inline]
pub fn get_shard_indices(
&self,
ids: &[&NodeId],
) -> Vec<usize> {
ids.iter().map(|id| self.get_shard_index(id)).collect()
}
#[inline]
pub fn get_shard_index_batch<'a>(
&self,
ids: &'a [&'a NodeId],
) -> Vec<(usize, &'a NodeId)> {
ids.iter().map(|&id| (self.get_shard_index(id), id)).collect()
}
pub fn clear_shard_cache() {
SHARD_INDEX_CACHE.clear();
}
pub fn shard_cache_stats() -> (usize, usize) {
let len = SHARD_INDEX_CACHE.len();
let capacity = SHARD_INDEX_CACHE.capacity();
(len, capacity)
}
pub fn contains_node(
&self,
id: &NodeId,
) -> bool {
let shard_index = self.get_shard_index(id);
self.nodes[shard_index].contains_key(id)
}
pub fn get_node(
&self,
id: &NodeId,
) -> Option<&Node> {
let shard_index = self.get_shard_index(id);
self.nodes[shard_index].get(id)
}
pub fn get_parent_node(
&self,
id: &NodeId,
) -> Option<&Node> {
self.parent_map.get(id).and_then(|parent_id| {
let shard_index = self.get_shard_index(parent_id);
self.nodes[shard_index].get(parent_id)
})
}
pub fn from(nodes: NodeTree) -> Self {
let num_shards = std::cmp::max(
std::thread::available_parallelism()
.map(NonZeroUsize::get)
.unwrap_or(2),
2,
);
let mut shards = VectorSync::new_sync(); for _ in 0..num_shards {
shards.push_back_mut(HashTrieMapSync::new_sync());
}
let mut parent_map = HashTrieMapSync::new_sync();
let (root_node, children) = nodes.into_parts();
let root_id = root_node.id.clone();
let mut hasher = AHasher::default();
root_id.hash(&mut hasher);
let shard_index = (hasher.finish() as usize) % num_shards;
shards[shard_index] =
shards[shard_index].insert(root_id.clone(), root_node);
fn process_children(
children: Vec<NodeTree>,
parent_id: &NodeId,
shards: &mut VectorSync<TreeMap>,
parent_map: &mut TreeParentMap,
num_shards: usize,
) {
for child in children {
let (node, grand_children) = child.into_parts();
let node_id = node.id.clone();
let mut hasher = AHasher::default();
node_id.hash(&mut hasher);
let shard_index = (hasher.finish() as usize) % num_shards;
shards[shard_index] =
shards[shard_index].insert(node_id.clone(), node);
parent_map.insert_mut(node_id.clone(), parent_id.clone());
process_children(
grand_children,
&node_id,
shards,
parent_map,
num_shards,
);
}
}
process_children(
children,
&root_id,
&mut shards,
&mut parent_map,
num_shards,
);
Self { root_id, nodes: shards, parent_map, num_shards }
}
pub fn new(root: Node) -> Self {
let num_shards = std::cmp::max(
std::thread::available_parallelism()
.map(NonZeroUsize::get)
.unwrap_or(2),
2,
);
let mut nodes = VectorSync::new_sync();
for _ in 0..num_shards {
nodes.push_back_mut(HashTrieMapSync::new_sync());
}
let root_id = root.id.clone();
let mut hasher = AHasher::default();
root_id.hash(&mut hasher);
let shard_index = (hasher.finish() as usize) % num_shards;
nodes[shard_index] = nodes[shard_index].insert(root_id.clone(), root);
Self {
root_id,
nodes,
parent_map: HashTrieMapSync::new_sync(),
num_shards,
}
}
pub fn update_attr(
&mut self,
id: &NodeId,
new_values: HashTrieMapSync<String, Value>,
) -> PoolResult<()> {
let shard_index = self.get_shard_index(id);
let node = self.nodes[shard_index]
.get(id)
.ok_or(error_helpers::node_not_found(id.clone()))?;
let new_node = node.update_attr(new_values);
self.nodes[shard_index] =
self.nodes[shard_index].insert(id.clone(), new_node);
Ok(())
}
pub fn update_node(
&mut self,
node: Node,
) -> PoolResult<()> {
let shard_index = self.get_shard_index(&node.id);
self.nodes[shard_index] =
self.nodes[shard_index].insert(node.id.clone(), node);
Ok(())
}
pub fn add(
&mut self,
parent_id: &NodeId,
nodes: Vec<NodeTree>,
) -> PoolResult<()> {
let parent_shard_index = self.get_shard_index(parent_id);
let parent_node = self.nodes[parent_shard_index]
.get(parent_id)
.ok_or(error_helpers::parent_not_found(parent_id.clone()))?;
let mut new_parent = parent_node.clone();
let zenliang: VectorSync<NodeId> =
nodes.iter().map(|n| n.0.id.clone()).collect();
for id in zenliang.iter() {
if !new_parent.contains(id) {
new_parent.content = new_parent.content.push_back(id.clone());
}
}
self.nodes[parent_shard_index] = self.nodes[parent_shard_index]
.insert(parent_id.clone(), new_parent);
let mut node_queue = Vec::new();
node_queue.push((nodes, parent_id.clone()));
while let Some((current_children, current_parent_id)) = node_queue.pop()
{
for child in current_children {
let (mut child_node, grand_children) = child.into_parts();
let current_node_id = child_node.id.clone();
let grand_children_ids: VectorSync<NodeId> =
grand_children.iter().map(|n| n.0.id.clone()).collect();
for id in grand_children_ids.iter() {
if !child_node.contains(id) {
child_node.content =
child_node.content.push_back(id.clone());
}
}
let shard_index = self.get_shard_index(¤t_node_id);
self.nodes[shard_index] = self.nodes[shard_index]
.insert(current_node_id.clone(), child_node);
self.parent_map = self
.parent_map
.insert(current_node_id.clone(), current_parent_id.clone());
node_queue.push((grand_children, current_node_id.clone()));
}
}
Ok(())
}
pub fn add_at_index(
&mut self,
parent_id: &NodeId,
index: usize,
node: &Node,
) -> PoolResult<()> {
let parent_shard_index = self.get_shard_index(parent_id);
let parent = self.nodes[parent_shard_index]
.get(parent_id)
.ok_or(error_helpers::parent_not_found(parent_id.clone()))?;
let new_parent = parent.insert_content_at_index(index, &node.id);
self.nodes[parent_shard_index] = self.nodes[parent_shard_index]
.insert(parent_id.clone(), new_parent);
self.parent_map =
self.parent_map.insert(node.id.clone(), parent_id.clone());
let shard_index = self.get_shard_index(&node.id);
self.nodes[shard_index] =
self.nodes[shard_index].insert(node.id.clone(), node.clone());
Ok(())
}
pub fn add_node(
&mut self,
parent_id: &NodeId,
nodes: &Vec<Node>,
) -> PoolResult<()> {
let parent_shard_index = self.get_shard_index(parent_id);
let parent = self.nodes[parent_shard_index]
.get(parent_id)
.ok_or(error_helpers::parent_not_found(parent_id.clone()))?;
let node_ids = nodes.iter().map(|n| n.id.clone()).collect();
let new_parent = parent.insert_contents(&node_ids);
self.nodes[parent_shard_index] = self.nodes[parent_shard_index]
.insert(parent_id.clone(), new_parent);
for node in nodes {
self.parent_map =
self.parent_map.insert(node.id.clone(), parent_id.clone());
for child_id in &node.content {
self.parent_map =
self.parent_map.insert(child_id.clone(), node.id.clone());
}
let shard_index = self.get_shard_index(&node.id);
self.nodes[shard_index] =
self.nodes[shard_index].insert(node.id.clone(), node.clone());
}
Ok(())
}
pub fn node(
&mut self,
key: &str,
) -> NodeRef<'_> {
NodeRef::new(self, key.into())
}
pub fn mark(
&mut self,
key: &str,
) -> MarkRef<'_> {
MarkRef::new(self, key.into())
}
pub fn attrs(
&mut self,
key: &str,
) -> AttrsRef<'_> {
AttrsRef::new(self, key.into())
}
pub fn children(
&self,
parent_id: &NodeId,
) -> Option<VectorSync<NodeId>> {
self.get_node(parent_id).map(|n| n.content.clone())
}
pub fn children_node(
&self,
parent_id: &NodeId,
) -> Option<VectorSync<&Node>> {
self.children(parent_id)
.map(|ids| ids.iter().filter_map(|id| self.get_node(id)).collect())
}
pub fn all_children(
&self,
parent_id: &NodeId,
filter: Option<&dyn Fn(&Node) -> bool>,
) -> Option<NodeTree> {
if let Some(node) = self.get_node(parent_id) {
let mut child_enums = Vec::new();
for child_id in &node.content {
if let Some(child_node) = self.get_node(child_id) {
if let Some(filter_fn) = filter {
if !filter_fn(child_node) {
continue; }
}
if let Some(child_enum) =
self.all_children(child_id, filter)
{
child_enums.push(child_enum);
}
}
}
Some(NodeTree(node.clone(), child_enums))
} else {
None
}
}
pub fn children_count(
&self,
parent_id: &NodeId,
) -> usize {
self.get_node(parent_id).map(|n| n.content.len()).unwrap_or(0)
}
pub fn remove_mark_by_name(
&mut self,
id: &NodeId,
mark_name: &str,
) -> PoolResult<()> {
let shard_index = self.get_shard_index(id);
let node = self.nodes[shard_index]
.get(id)
.ok_or(error_helpers::node_not_found(id.clone()))?;
let new_node = node.remove_mark_by_name(mark_name);
self.nodes[shard_index] =
self.nodes[shard_index].insert(id.clone(), new_node);
Ok(())
}
pub fn get_marks(
&self,
id: &NodeId,
) -> Option<VectorSync<Mark>> {
self.get_node(id).map(|n| n.marks.clone())
}
pub fn remove_mark(
&mut self,
id: &NodeId,
mark_types: &[String],
) -> PoolResult<()> {
let shard_index = self.get_shard_index(id);
let node = self.nodes[shard_index]
.get(id)
.ok_or(error_helpers::node_not_found(id.clone()))?;
let new_node = node.remove_mark(mark_types);
self.nodes[shard_index] =
self.nodes[shard_index].insert(id.clone(), new_node);
Ok(())
}
pub fn add_mark(
&mut self,
id: &NodeId,
marks: &[Mark],
) -> PoolResult<()> {
let shard_index = self.get_shard_index(id);
let node = self.nodes[shard_index]
.get(id)
.ok_or(error_helpers::node_not_found(id.clone()))?;
let new_node = node.add_marks(marks);
self.nodes[shard_index] =
self.nodes[shard_index].insert(id.clone(), new_node);
Ok(())
}
pub fn move_node(
&mut self,
source_parent_id: &NodeId,
target_parent_id: &NodeId,
node_id: &NodeId,
position: Option<usize>,
) -> PoolResult<()> {
let source_shard_index = self.get_shard_index(source_parent_id);
let target_shard_index = self.get_shard_index(target_parent_id);
let node_shard_index = self.get_shard_index(node_id);
let source_parent = self.nodes[source_shard_index]
.get(source_parent_id)
.ok_or(error_helpers::parent_not_found(source_parent_id.clone()))?;
let target_parent = self.nodes[target_shard_index]
.get(target_parent_id)
.ok_or(error_helpers::parent_not_found(target_parent_id.clone()))?;
let _node = self.nodes[node_shard_index]
.get(node_id)
.ok_or(error_helpers::node_not_found(node_id.clone()))?;
if !source_parent.contains(node_id) {
return Err(error_helpers::invalid_parenting(
node_id.clone(),
source_parent_id.clone(),
));
}
let mut new_source_parent = source_parent.clone();
new_source_parent.content = new_source_parent
.content
.iter()
.filter(|&id| id != node_id)
.cloned()
.collect();
let mut new_target_parent = target_parent.clone();
if let Some(pos) = position {
let insert_pos = pos.min(new_target_parent.content.len());
new_target_parent =
new_target_parent.insert_content_at_index(insert_pos, node_id);
} else {
new_target_parent.content =
new_target_parent.content.push_back(node_id.clone());
}
self.nodes[source_shard_index] = self.nodes[source_shard_index]
.insert(source_parent_id.clone(), new_source_parent);
self.nodes[target_shard_index] = self.nodes[target_shard_index]
.insert(target_parent_id.clone(), new_target_parent);
self.parent_map =
self.parent_map.insert(node_id.clone(), target_parent_id.clone());
Ok(())
}
pub fn remove_node(
&mut self,
parent_id: &NodeId,
nodes: Vec<NodeId>,
) -> PoolResult<()> {
let parent_shard_index = self.get_shard_index(parent_id);
let parent = self.nodes[parent_shard_index]
.get(parent_id)
.ok_or(error_helpers::parent_not_found(parent_id.clone()))?;
if nodes.contains(&self.root_id) {
return Err(error_helpers::cannot_remove_root());
}
for node_id in &nodes {
if !parent.contains(node_id) {
return Err(error_helpers::invalid_parenting(
node_id.clone(),
parent_id.clone(),
));
}
}
let nodes_to_remove: std::collections::HashSet<_> =
nodes.iter().collect();
let filtered_children: VectorSync<NodeId> = parent
.content
.iter()
.filter(|&id| !nodes_to_remove.contains(id))
.cloned()
.collect();
let mut parent_node = parent.clone();
parent_node.content = filtered_children;
self.nodes[parent_shard_index] = self.nodes[parent_shard_index]
.insert(parent_id.clone(), parent_node);
let mut remove_nodes = Vec::new();
for node_id in nodes {
self.remove_subtree(&node_id, &mut remove_nodes)?;
}
Ok(())
}
pub fn remove_node_by_id(
&mut self,
node_id: &NodeId,
) -> PoolResult<()> {
if node_id == &self.root_id {
return Err(error_helpers::cannot_remove_root());
}
let shard_index = self.get_shard_index(node_id);
let _ = self.nodes[shard_index]
.get(node_id)
.ok_or(error_helpers::node_not_found(node_id.clone()))?;
if let Some(parent_id) = self.parent_map.get(node_id).cloned() {
let parent_shard_index = self.get_shard_index(&parent_id);
if let Some(parent_node) =
self.nodes[parent_shard_index].get(&parent_id)
{
let mut new_parent = parent_node.clone();
new_parent.content = new_parent
.content
.iter()
.filter(|&id| id != node_id)
.cloned()
.collect();
self.nodes[parent_shard_index] = self.nodes[parent_shard_index]
.insert(parent_id.clone(), new_parent);
}
}
let mut remove_nodes = Vec::new();
self.remove_subtree(node_id, &mut remove_nodes)?;
Ok(())
}
pub fn remove_node_by_index(
&mut self,
parent_id: &NodeId,
index: usize,
) -> PoolResult<()> {
let shard_index = self.get_shard_index(parent_id);
let parent = self.nodes[shard_index]
.get(parent_id)
.ok_or(error_helpers::parent_not_found(parent_id.clone()))?;
let mut new_parent = parent.clone();
let remove_node_id = {
match new_parent.content.get(index) {
Some(id) => id.clone(),
None => return Err(anyhow::anyhow!("index out of bounds")),
}
};
new_parent = new_parent.remove_content(&remove_node_id);
self.nodes[shard_index] =
self.nodes[shard_index].insert(parent_id.clone(), new_parent);
let mut remove_nodes = Vec::new();
self.remove_subtree(&remove_node_id, &mut remove_nodes)?;
Ok(())
}
fn remove_subtree(
&mut self,
node_id: &NodeId,
remove_nodes: &mut Vec<Node>,
) -> PoolResult<()> {
if node_id == &self.root_id {
return Err(error_helpers::cannot_remove_root());
}
let shard_index = self.get_shard_index(node_id);
let _ = self.nodes[shard_index]
.get(node_id)
.ok_or(error_helpers::node_not_found(node_id.clone()))?;
if let Some(children) = self.children(node_id) {
for child_id in children.iter() {
self.remove_subtree(&child_id, remove_nodes)?;
}
}
self.parent_map = self.parent_map.remove(node_id);
if let Some(remove_node) = self.nodes[shard_index].get(node_id) {
remove_nodes.push(remove_node.clone());
self.nodes[shard_index] = self.nodes[shard_index].remove(node_id);
}
Ok(())
}
}
impl Index<&NodeId> for Tree {
type Output = Node;
fn index(
&self,
index: &NodeId,
) -> &Self::Output {
let shard_index = self.get_shard_index(index);
self.nodes[shard_index].get(index).expect("Node not found")
}
}
impl Index<&str> for Tree {
type Output = Node;
fn index(
&self,
index: &str,
) -> &Self::Output {
let node_id = NodeId::from(index);
let shard_index = self.get_shard_index(&node_id);
self.nodes[shard_index].get(&node_id).expect("Node not found")
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::node::Node;
use crate::attrs::Attrs;
use crate::mark::Mark;
use serde_json::json;
fn create_test_node(id: &str) -> Node {
Node::new(id, "test".to_string(), Attrs::default(), vec![], vec![])
}
#[test]
fn test_tree_creation() {
let root = create_test_node("root");
let tree = Tree::new(root.clone());
assert_eq!(tree.root_id, root.id);
assert!(tree.contains_node(&root.id));
}
#[test]
fn test_add_node() {
let root = create_test_node("root");
let mut tree = Tree::new(root.clone());
let child = create_test_node("child");
let nodes = vec![child.clone()];
tree.add_node(&root.id, &nodes).unwrap();
#[cfg(feature = "debug-logs")]
dbg!(&tree);
assert!(tree.contains_node(&child.id));
assert_eq!(tree.children(&root.id).unwrap().len(), 1);
}
#[test]
fn test_remove_node() {
let root = create_test_node("root");
let mut tree = Tree::new(root.clone());
let child = create_test_node("child");
let nodes = vec![child.clone()];
tree.add_node(&root.id, &nodes).unwrap();
#[cfg(feature = "debug-logs")]
dbg!(&tree);
tree.remove_node(&root.id, vec![child.id.clone()]).unwrap();
#[cfg(feature = "debug-logs")]
dbg!(&tree);
assert!(!tree.contains_node(&child.id));
assert_eq!(tree.children(&root.id).unwrap().len(), 0);
}
#[test]
fn test_move_node() {
let parent1 = create_test_node("parent1");
let parent2 = create_test_node("parent2");
let mut tree = Tree::new(parent1.clone());
tree.add_node(&parent1.id, &vec![parent2.clone()]).unwrap();
let child1 = create_test_node("child1");
let child2 = create_test_node("child2");
let child3 = create_test_node("child3");
tree.add_node(&parent1.id, &vec![child1.clone()]).unwrap();
tree.add_node(&parent1.id, &vec![child2.clone()]).unwrap();
tree.add_node(&parent1.id, &vec![child3.clone()]).unwrap();
let parent1_children = tree.children(&parent1.id).unwrap();
assert_eq!(parent1_children.len(), 4); assert_eq!(parent1_children[0], parent2.id);
assert_eq!(parent1_children[1], child1.id);
assert_eq!(parent1_children[2], child2.id);
assert_eq!(parent1_children[3], child3.id);
tree.move_node(&parent1.id, &parent2.id, &child1.id, None).unwrap();
let parent1_children = tree.children(&parent1.id).unwrap();
let parent2_children = tree.children(&parent2.id).unwrap();
assert_eq!(parent1_children.len(), 3); assert_eq!(parent2_children.len(), 1); assert_eq!(parent2_children[0], child1.id);
tree.move_node(&parent1.id, &parent2.id, &child2.id, Some(1)).unwrap();
let parent1_children = tree.children(&parent1.id).unwrap();
let parent2_children = tree.children(&parent2.id).unwrap();
assert_eq!(parent1_children.len(), 2); assert_eq!(parent2_children.len(), 2); assert_eq!(parent2_children[0], child1.id);
assert_eq!(parent2_children[1], child2.id);
let child1_parent = tree.get_parent_node(&child1.id).unwrap();
let child2_parent = tree.get_parent_node(&child2.id).unwrap();
assert_eq!(child1_parent.id, parent2.id);
assert_eq!(child2_parent.id, parent2.id);
}
#[test]
fn test_update_attr() {
let root = create_test_node("root");
let mut tree = Tree::new(root.clone());
let mut attrs = HashTrieMapSync::new_sync();
attrs = attrs.insert("key".to_string(), json!("value"));
tree.update_attr(&root.id, attrs).unwrap();
let node = tree.get_node(&root.id).unwrap();
#[cfg(feature = "debug-logs")]
dbg!(&node);
assert_eq!(node.attrs.get("key").unwrap(), &json!("value"));
}
#[test]
fn test_add_mark() {
let root = create_test_node("root");
let mut tree = Tree::new(root.clone());
let mark = Mark { r#type: "test".to_string(), attrs: Attrs::default() };
tree.add_mark(&root.id, &[mark.clone()]).unwrap();
#[cfg(feature = "debug-logs")]
dbg!(&tree);
}
#[test]
fn test_remove_mark() {
let root = create_test_node("root");
let mut tree = Tree::new(root.clone());
let mark = Mark { r#type: "test".to_string(), attrs: Attrs::default() };
tree.add_mark(&root.id, &[mark.clone()]).unwrap();
#[cfg(feature = "debug-logs")]
dbg!(&tree);
tree.remove_mark(&root.id, &[mark.r#type.clone()]).unwrap();
#[cfg(feature = "debug-logs")]
dbg!(&tree);
let node = tree.get_node(&root.id).unwrap();
assert!(!node.marks.iter().any(|m| m.r#type == mark.r#type));
}
#[test]
fn test_all_children() {
let root = create_test_node("root");
let mut tree = Tree::new(root.clone());
let child1 = create_test_node("child1");
let child2 = create_test_node("child2");
tree.add_node(&root.id, &vec![child1.clone()]).unwrap();
tree.add_node(&root.id, &vec![child2.clone()]).unwrap();
#[cfg(feature = "debug-logs")]
dbg!(&tree);
let all_children = tree.all_children(&root.id, None).unwrap();
assert_eq!(all_children.1.len(), 2);
}
#[test]
fn test_children_count() {
let root = create_test_node("root");
let mut tree = Tree::new(root.clone());
let child1 = create_test_node("child1");
let child2 = create_test_node("child2");
tree.add_node(&root.id, &vec![child1.clone()]).unwrap();
tree.add_node(&root.id, &vec![child2.clone()]).unwrap();
assert_eq!(tree.children_count(&root.id), 2);
}
#[test]
fn test_remove_node_by_id_updates_parent() {
let root = create_test_node("root");
let mut tree = Tree::new(root.clone());
let child = create_test_node("child");
tree.add_node(&root.id, &vec![child.clone()]).unwrap();
assert_eq!(tree.children_count(&root.id), 1);
assert!(tree.contains_node(&child.id));
tree.remove_node_by_id(&child.id).unwrap();
assert_eq!(tree.children_count(&root.id), 0);
assert!(!tree.contains_node(&child.id));
}
#[test]
fn test_move_node_position_edge_cases() {
let root = create_test_node("root");
let mut tree = Tree::new(root.clone());
let container = create_test_node("container");
tree.add_node(&root.id, &vec![container.clone()]).unwrap();
let child1 = create_test_node("child1");
let child2 = create_test_node("child2");
let child3 = create_test_node("child3");
tree.add_node(&root.id, &vec![child1.clone()]).unwrap();
tree.add_node(&root.id, &vec![child2.clone()]).unwrap();
tree.add_node(&root.id, &vec![child3.clone()]).unwrap();
tree.move_node(&root.id, &container.id, &child1.id, Some(100)).unwrap();
let container_children = tree.children(&container.id).unwrap();
assert_eq!(container_children.len(), 1);
assert_eq!(container_children[0], child1.id);
tree.move_node(&root.id, &container.id, &child2.id, Some(0)).unwrap();
let container_children = tree.children(&container.id).unwrap();
assert_eq!(container_children.len(), 2);
assert_eq!(container_children[0], child2.id);
assert_eq!(container_children[1], child1.id);
}
#[test]
fn test_cannot_remove_root_node() {
let root = create_test_node("root");
let mut tree = Tree::new(root.clone());
let result = tree.remove_node_by_id(&root.id);
assert!(result.is_err());
}
#[test]
fn test_get_parent_node() {
let root = create_test_node("root");
let mut tree = Tree::new(root.clone());
let child = create_test_node("child");
tree.add_node(&root.id, &vec![child.clone()]).unwrap();
let parent = tree.get_parent_node(&child.id).unwrap();
assert_eq!(parent.id, root.id);
}
}