mirror of
https://github.com/DioxusLabs/dioxus
synced 2024-11-27 06:30:20 +00:00
intial update to new mutations
This commit is contained in:
parent
078b8ba833
commit
dcad2fe8a2
6 changed files with 442 additions and 757 deletions
|
@ -5,8 +5,6 @@ pub mod node;
|
|||
pub mod node_ref;
|
||||
pub mod real_dom;
|
||||
pub mod state;
|
||||
#[doc(hidden)]
|
||||
pub mod traversable;
|
||||
pub mod tree;
|
||||
pub mod utils;
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use crate::{state::State, tree::NodeId, RealNodeId};
|
||||
use crate::{state::State, tree::NodeId};
|
||||
use dioxus_core::ElementId;
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
|
||||
|
@ -13,12 +13,15 @@ pub struct Node<S: State> {
|
|||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct NodeData {
|
||||
/// The id of the node
|
||||
pub node_id: NodeId,
|
||||
/// The id of the node in the vdom.
|
||||
pub element_id: Option<ElementId>,
|
||||
/// Additional inforation specific to the node type
|
||||
pub node_type: NodeType,
|
||||
/// The number of parents before the root node. The root node has height 1.
|
||||
pub height: u16,
|
||||
height_dirty: bool,
|
||||
}
|
||||
|
||||
/// A type of node with data specific to the node type. The types are a subset of the [VNode] types.
|
||||
|
@ -29,7 +32,7 @@ pub enum NodeType {
|
|||
},
|
||||
Element {
|
||||
tag: String,
|
||||
namespace: Option<&'static str>,
|
||||
namespace: Option<String>,
|
||||
attributes: FxHashMap<OwnedAttributeDiscription, OwnedAttributeValue>,
|
||||
listeners: FxHashSet<String>,
|
||||
},
|
||||
|
@ -44,29 +47,12 @@ impl<S: State> Node<S> {
|
|||
element_id: None,
|
||||
node_type,
|
||||
height: 0,
|
||||
node_id: NodeId(0),
|
||||
height_dirty: true,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// link a child node
|
||||
fn add_child(&mut self, child: RealNodeId) {
|
||||
if let NodeType::Element { children, .. } = &mut self.node_data.node_type {
|
||||
children.push(child);
|
||||
}
|
||||
}
|
||||
|
||||
/// remove a child node
|
||||
fn remove_child(&mut self, child: RealNodeId) {
|
||||
if let NodeType::Element { children, .. } = &mut self.node_data.node_type {
|
||||
children.retain(|c| c != &child);
|
||||
}
|
||||
}
|
||||
|
||||
/// link the parent node
|
||||
fn set_parent(&mut self, parent: RealNodeId) {
|
||||
self.node_data.parent = Some(parent);
|
||||
}
|
||||
|
||||
/// get the mounted id of the node
|
||||
pub fn mounted_id(&self) -> Option<ElementId> {
|
||||
self.node_data.element_id
|
||||
|
|
|
@ -42,7 +42,7 @@ impl<'a> NodeView<'a> {
|
|||
self.mask
|
||||
.namespace
|
||||
.then_some(match &self.inner.node_type {
|
||||
NodeType::Element { namespace, .. } => *namespace,
|
||||
NodeType::Element { namespace, .. } => namespace.as_deref(),
|
||||
_ => None,
|
||||
})
|
||||
.flatten()
|
||||
|
@ -93,7 +93,7 @@ impl<'a> NodeView<'a> {
|
|||
pub enum AttributeMask {
|
||||
All,
|
||||
/// A list of attribute names that are visible, this list must be sorted
|
||||
Dynamic(Vec<&'static str>),
|
||||
Dynamic(Vec<String>),
|
||||
/// A list of attribute names that are visible, this list must be sorted
|
||||
Static(&'static [&'static str]),
|
||||
}
|
||||
|
@ -105,14 +105,14 @@ impl AttributeMask {
|
|||
fn contains_attribute(&self, attr: &str) -> bool {
|
||||
match self {
|
||||
AttributeMask::All => true,
|
||||
AttributeMask::Dynamic(l) => l.binary_search(&attr).is_ok(),
|
||||
AttributeMask::Dynamic(l) => l.binary_search_by_key(&attr, |s| s.as_str()).is_ok(),
|
||||
AttributeMask::Static(l) => l.binary_search(&attr).is_ok(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new dynamic attribute mask with a single attribute
|
||||
pub fn single(new: &'static str) -> Self {
|
||||
Self::Dynamic(vec![new])
|
||||
pub fn single(new: &str) -> Self {
|
||||
Self::Dynamic(vec![new.to_string()])
|
||||
}
|
||||
|
||||
/// Ensure the attribute list is sorted.
|
||||
|
@ -133,15 +133,27 @@ impl AttributeMask {
|
|||
/// Combine two attribute masks
|
||||
pub fn union(&self, other: &Self) -> Self {
|
||||
let new = match (self, other) {
|
||||
(AttributeMask::Dynamic(s), AttributeMask::Dynamic(o)) => AttributeMask::Dynamic(
|
||||
union_ordered_iter(s.iter().copied(), o.iter().copied(), s.len() + o.len()),
|
||||
),
|
||||
(AttributeMask::Static(s), AttributeMask::Dynamic(o)) => AttributeMask::Dynamic(
|
||||
union_ordered_iter(s.iter().copied(), o.iter().copied(), s.len() + o.len()),
|
||||
),
|
||||
(AttributeMask::Dynamic(s), AttributeMask::Static(o)) => AttributeMask::Dynamic(
|
||||
union_ordered_iter(s.iter().copied(), o.iter().copied(), s.len() + o.len()),
|
||||
),
|
||||
(AttributeMask::Dynamic(s), AttributeMask::Dynamic(o)) => {
|
||||
AttributeMask::Dynamic(union_ordered_iter(
|
||||
s.iter().map(|s| s.as_str()),
|
||||
o.iter().map(|s| s.as_str()),
|
||||
s.len() + o.len(),
|
||||
))
|
||||
}
|
||||
(AttributeMask::Static(s), AttributeMask::Dynamic(o)) => {
|
||||
AttributeMask::Dynamic(union_ordered_iter(
|
||||
s.iter().copied(),
|
||||
o.iter().map(|s| s.as_str()),
|
||||
s.len() + o.len(),
|
||||
))
|
||||
}
|
||||
(AttributeMask::Dynamic(s), AttributeMask::Static(o)) => {
|
||||
AttributeMask::Dynamic(union_ordered_iter(
|
||||
s.iter().map(|s| s.as_str()),
|
||||
o.iter().copied(),
|
||||
s.len() + o.len(),
|
||||
))
|
||||
}
|
||||
(AttributeMask::Static(s), AttributeMask::Static(o)) => AttributeMask::Dynamic(
|
||||
union_ordered_iter(s.iter().copied(), o.iter().copied(), s.len() + o.len()),
|
||||
),
|
||||
|
@ -153,9 +165,9 @@ impl AttributeMask {
|
|||
|
||||
/// Check if two attribute masks overlap
|
||||
fn overlaps(&self, other: &Self) -> bool {
|
||||
fn overlaps_iter(
|
||||
self_iter: impl Iterator<Item = &'static str>,
|
||||
mut other_iter: impl Iterator<Item = &'static str>,
|
||||
fn overlaps_iter<'a>(
|
||||
self_iter: impl Iterator<Item = &'a str>,
|
||||
mut other_iter: impl Iterator<Item = &'a str>,
|
||||
) -> bool {
|
||||
if let Some(mut other_attr) = other_iter.next() {
|
||||
for self_attr in self_iter {
|
||||
|
@ -180,13 +192,13 @@ impl AttributeMask {
|
|||
(AttributeMask::Dynamic(v), AttributeMask::All) => !v.is_empty(),
|
||||
(AttributeMask::Static(s), AttributeMask::All) => !s.is_empty(),
|
||||
(AttributeMask::Dynamic(v1), AttributeMask::Dynamic(v2)) => {
|
||||
overlaps_iter(v1.iter().copied(), v2.iter().copied())
|
||||
overlaps_iter(v1.iter().map(|s| s.as_str()), v2.iter().map(|s| s.as_str()))
|
||||
}
|
||||
(AttributeMask::Dynamic(v), AttributeMask::Static(s)) => {
|
||||
overlaps_iter(v.iter().copied(), s.iter().copied())
|
||||
overlaps_iter(v.iter().map(|s| s.as_str()), s.iter().copied())
|
||||
}
|
||||
(AttributeMask::Static(s), AttributeMask::Dynamic(v)) => {
|
||||
overlaps_iter(v.iter().copied(), s.iter().copied())
|
||||
overlaps_iter(v.iter().map(|s| s.as_str()), s.iter().copied())
|
||||
}
|
||||
(AttributeMask::Static(s1), AttributeMask::Static(s2)) => {
|
||||
overlaps_iter(s1.iter().copied(), s2.iter().copied())
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,17 +1,18 @@
|
|||
use std::{cmp::Ordering, fmt::Debug};
|
||||
use std::cmp::Ordering;
|
||||
|
||||
use crate::node::NodeData;
|
||||
use crate::node_ref::{NodeMask, NodeView};
|
||||
use crate::tree::TreeView;
|
||||
use crate::RealNodeId;
|
||||
use anymap::AnyMap;
|
||||
use rustc_hash::FxHashSet;
|
||||
|
||||
/// Join two sorted iterators
|
||||
pub(crate) fn union_ordered_iter<T: Ord + Debug>(
|
||||
s_iter: impl Iterator<Item = T>,
|
||||
o_iter: impl Iterator<Item = T>,
|
||||
pub(crate) fn union_ordered_iter<'a>(
|
||||
s_iter: impl Iterator<Item = &'a str>,
|
||||
o_iter: impl Iterator<Item = &'a str>,
|
||||
new_len_guess: usize,
|
||||
) -> Vec<T> {
|
||||
) -> Vec<String> {
|
||||
let mut s_peekable = s_iter.peekable();
|
||||
let mut o_peekable = o_iter.peekable();
|
||||
let mut v = Vec::with_capacity(new_len_guess);
|
||||
|
@ -22,7 +23,7 @@ pub(crate) fn union_ordered_iter<T: Ord + Debug>(
|
|||
break;
|
||||
}
|
||||
Ordering::Less => {
|
||||
v.push(o_peekable.next().unwrap());
|
||||
v.push(o_peekable.next().unwrap().to_string());
|
||||
}
|
||||
Ordering::Equal => {
|
||||
o_peekable.next();
|
||||
|
@ -30,10 +31,10 @@ pub(crate) fn union_ordered_iter<T: Ord + Debug>(
|
|||
}
|
||||
}
|
||||
}
|
||||
v.push(s_peekable.next().unwrap());
|
||||
v.push(s_peekable.next().unwrap().to_string());
|
||||
}
|
||||
for o_i in o_peekable {
|
||||
v.push(o_i);
|
||||
v.push(o_i.to_string());
|
||||
}
|
||||
for w in v.windows(2) {
|
||||
debug_assert!(w[1] > w[0]);
|
||||
|
@ -207,11 +208,7 @@ pub trait NodeDepState<DepState = ()> {
|
|||
/// Do not implement this trait. It is only meant to be derived and used through [crate::real_dom::RealDom].
|
||||
pub trait State: Default + Clone {
|
||||
#[doc(hidden)]
|
||||
fn update<
|
||||
'a,
|
||||
T: Traversable<Node = Self, Id = RealNodeId>,
|
||||
T2: Traversable<Node = NodeData, Id = RealNodeId>,
|
||||
>(
|
||||
fn update<'a, T: TreeView<Self>, T2: TreeView<NodeData>>(
|
||||
dirty: &[(RealNodeId, NodeMask)],
|
||||
state_tree: &'a mut T,
|
||||
rdom: &'a T2,
|
||||
|
|
|
@ -7,7 +7,7 @@ use std::collections::VecDeque;
|
|||
use std::marker::PhantomData;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug, PartialOrd, Ord)]
|
||||
#[derive(Hash, PartialEq, Eq, Clone, Copy, Debug, PartialOrd, Ord)]
|
||||
pub struct NodeId(pub usize);
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Debug)]
|
||||
|
@ -23,6 +23,31 @@ pub struct Tree<T> {
|
|||
root: NodeId,
|
||||
}
|
||||
|
||||
impl<T> Tree<T> {
|
||||
fn try_remove(&mut self, id: NodeId) -> Option<Node<T>> {
|
||||
self.nodes.try_remove(id.0).map(|node| {
|
||||
if let Some(parent) = node.parent {
|
||||
self.nodes
|
||||
.get_mut(parent.0)
|
||||
.unwrap()
|
||||
.children
|
||||
.retain(|child| child != &id);
|
||||
}
|
||||
for child in &node.children {
|
||||
self.remove_recursive(*child);
|
||||
}
|
||||
node
|
||||
})
|
||||
}
|
||||
|
||||
fn remove_recursive(&mut self, node: NodeId) {
|
||||
let node = self.nodes.remove(node.0);
|
||||
for child in node.children {
|
||||
self.remove_recursive(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait TreeView<T>: Sized {
|
||||
type Iterator<'a>: Iterator<Item = &'a T>
|
||||
where
|
||||
|
@ -152,15 +177,19 @@ pub trait TreeView<T>: Sized {
|
|||
pub trait TreeLike<T>: TreeView<T> {
|
||||
fn new(root: T) -> Self;
|
||||
|
||||
fn add_child(&mut self, parent: NodeId, value: T) -> NodeId;
|
||||
fn create_node(&mut self, value: T) -> NodeId;
|
||||
|
||||
fn add_child(&mut self, parent: NodeId, child: NodeId);
|
||||
|
||||
fn remove(&mut self, id: NodeId) -> Option<T>;
|
||||
|
||||
fn replace(&mut self, id: NodeId, value: T);
|
||||
fn remove_all_children(&mut self, id: NodeId) -> Vec<T>;
|
||||
|
||||
fn insert_before(&mut self, id: NodeId, value: T) -> NodeId;
|
||||
fn replace(&mut self, old: NodeId, new: NodeId);
|
||||
|
||||
fn insert_after(&mut self, id: NodeId, value: T) -> NodeId;
|
||||
fn insert_before(&mut self, id: NodeId, new: NodeId);
|
||||
|
||||
fn insert_after(&mut self, id: NodeId, new: NodeId);
|
||||
}
|
||||
|
||||
pub struct ChildNodeIterator<'a, T, Tr: TreeView<T>> {
|
||||
|
@ -304,78 +333,75 @@ impl<T> TreeLike<T> for Tree<T> {
|
|||
Self { nodes, root }
|
||||
}
|
||||
|
||||
fn add_child(&mut self, parent: NodeId, value: T) -> NodeId {
|
||||
let node = Node {
|
||||
fn create_node(&mut self, value: T) -> NodeId {
|
||||
NodeId(self.nodes.insert(Node {
|
||||
value,
|
||||
parent: Some(parent),
|
||||
parent: None,
|
||||
children: Vec::new(),
|
||||
};
|
||||
let id = self.nodes.insert(node);
|
||||
self.nodes
|
||||
.get_mut(parent.0)
|
||||
.unwrap()
|
||||
.children
|
||||
.push(NodeId(id));
|
||||
NodeId(id)
|
||||
}))
|
||||
}
|
||||
|
||||
fn add_child(&mut self, parent: NodeId, new: NodeId) {
|
||||
self.nodes.get_mut(parent.0).unwrap().children.push(new);
|
||||
self.nodes.get_mut(new.0).unwrap().parent = Some(parent);
|
||||
}
|
||||
|
||||
fn remove(&mut self, id: NodeId) -> Option<T> {
|
||||
self.nodes.try_remove(id.0).map(|node| {
|
||||
if let Some(parent) = node.parent {
|
||||
self.nodes
|
||||
.get_mut(parent.0)
|
||||
.unwrap()
|
||||
.children
|
||||
.retain(|child| child != &id);
|
||||
self.try_remove(id).map(|node| node.value)
|
||||
}
|
||||
|
||||
fn remove_all_children(&mut self, id: NodeId) -> Vec<T> {
|
||||
let mut children = Vec::new();
|
||||
let self_mut = self as *mut Self;
|
||||
for child in self.children_ids(id).unwrap() {
|
||||
unsafe {
|
||||
// Safety: No node has itself as a child
|
||||
children.push((*self_mut).remove(*child).unwrap());
|
||||
}
|
||||
node.value
|
||||
})
|
||||
}
|
||||
children
|
||||
}
|
||||
|
||||
fn replace(&mut self, id: NodeId, value: T) {
|
||||
fn replace(&mut self, old_id: NodeId, new_id: NodeId) {
|
||||
// remove the old node
|
||||
let old = self
|
||||
.nodes
|
||||
.get_mut(id.0)
|
||||
.try_remove(old_id)
|
||||
.expect("tried to replace a node that doesn't exist");
|
||||
old.value = value;
|
||||
// update the parent's link to the child
|
||||
if let Some(parent_id) = old.parent {
|
||||
let parent = self.nodes.get_mut(parent_id.0).unwrap();
|
||||
for id in &mut parent.children {
|
||||
if *id == old_id {
|
||||
*id = new_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn insert_before(&mut self, id: NodeId, value: T) -> NodeId {
|
||||
fn insert_before(&mut self, id: NodeId, new: NodeId) {
|
||||
let node = self.nodes.get(id.0).unwrap();
|
||||
let parent_id = node.parent.expect("tried to insert before root");
|
||||
let new = Node {
|
||||
value,
|
||||
parent: Some(parent_id),
|
||||
children: Vec::new(),
|
||||
};
|
||||
let new_id = NodeId(self.nodes.insert(new));
|
||||
self.nodes.get_mut(new.0).unwrap().parent = Some(parent_id);
|
||||
let parent = self.nodes.get_mut(parent_id.0).unwrap();
|
||||
let index = parent
|
||||
.children
|
||||
.iter()
|
||||
.position(|child| child == &id)
|
||||
.unwrap();
|
||||
parent.children.insert(index, new_id);
|
||||
new_id
|
||||
parent.children.insert(index, new);
|
||||
}
|
||||
|
||||
fn insert_after(&mut self, id: NodeId, value: T) -> NodeId {
|
||||
fn insert_after(&mut self, id: NodeId, new: NodeId) {
|
||||
let node = self.nodes.get(id.0).unwrap();
|
||||
let parent_id = node.parent.expect("tried to insert before root");
|
||||
let new = Node {
|
||||
value,
|
||||
parent: Some(parent_id),
|
||||
children: Vec::new(),
|
||||
};
|
||||
let new_id = NodeId(self.nodes.insert(new));
|
||||
self.nodes.get_mut(new.0).unwrap().parent = Some(parent_id);
|
||||
let parent = self.nodes.get_mut(parent_id.0).unwrap();
|
||||
let index = parent
|
||||
.children
|
||||
.iter()
|
||||
.position(|child| child == &id)
|
||||
.unwrap();
|
||||
parent.children.insert(index + 1, new_id);
|
||||
new_id
|
||||
parent.children.insert(index + 1, new);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -498,7 +524,7 @@ pub struct SharedView<'a, T, Tr: TreeView<T>> {
|
|||
impl<'a, T, Tr: TreeView<T>> SharedView<'a, T, Tr> {
|
||||
/// Checks if a node is currently locked. Returns None if the node does not exist.
|
||||
pub fn check_lock(&self, id: NodeId) -> Option<bool> {
|
||||
let mut locks = self.node_locks.read();
|
||||
let locks = self.node_locks.read();
|
||||
locks.get(id.0).map(|lock| lock.is_locked())
|
||||
}
|
||||
}
|
||||
|
@ -616,7 +642,8 @@ impl<'a, T, Tr: TreeView<T>> TreeView<T> for SharedView<'a, T, Tr> {
|
|||
fn creation() {
|
||||
let mut tree = Tree::new(1);
|
||||
let parent = tree.root();
|
||||
let child = tree.add_child(parent, 0);
|
||||
let child = tree.create_node(0);
|
||||
tree.add_child(parent, child);
|
||||
|
||||
println!("Tree: {:#?}", tree);
|
||||
assert_eq!(tree.size(), 2);
|
||||
|
@ -631,9 +658,12 @@ fn creation() {
|
|||
fn insertion() {
|
||||
let mut tree = Tree::new(0);
|
||||
let parent = tree.root();
|
||||
let child = tree.add_child(parent, 2);
|
||||
let before = tree.insert_before(child, 1);
|
||||
let after = tree.insert_after(child, 3);
|
||||
let child = tree.create_node(2);
|
||||
tree.add_child(parent, child);
|
||||
let before = tree.create_node(1);
|
||||
tree.insert_before(child, before);
|
||||
let after = tree.create_node(3);
|
||||
tree.insert_after(child, after);
|
||||
|
||||
println!("Tree: {:#?}", tree);
|
||||
assert_eq!(tree.size(), 4);
|
||||
|
@ -651,9 +681,12 @@ fn insertion() {
|
|||
fn deletion() {
|
||||
let mut tree = Tree::new(0);
|
||||
let parent = tree.root();
|
||||
let child = tree.add_child(parent, 2);
|
||||
let before = tree.insert_before(child, 1);
|
||||
let after = tree.insert_after(child, 3);
|
||||
let child = tree.create_node(2);
|
||||
tree.add_child(parent, child);
|
||||
let before = tree.create_node(1);
|
||||
tree.insert_before(child, before);
|
||||
let after = tree.create_node(3);
|
||||
tree.insert_after(child, after);
|
||||
|
||||
println!("Tree: {:#?}", tree);
|
||||
assert_eq!(tree.size(), 4);
|
||||
|
@ -702,7 +735,8 @@ fn shared_view() {
|
|||
use std::thread;
|
||||
let mut tree = Tree::new(1);
|
||||
let parent = tree.root();
|
||||
let child = tree.add_child(parent, 0);
|
||||
let child = tree.create_node(0);
|
||||
tree.add_child(parent, child);
|
||||
|
||||
let shared = SharedView::new(&mut tree);
|
||||
|
||||
|
@ -737,7 +771,8 @@ fn map() {
|
|||
}
|
||||
let mut tree = Tree::new(Value::new(1));
|
||||
let parent = tree.root();
|
||||
let child = tree.add_child(parent, Value::new(0));
|
||||
let child = tree.create_node(Value::new(0));
|
||||
tree.add_child(parent, child);
|
||||
|
||||
let mut mapped = tree.map(|x| &x.value, |x| &mut x.value);
|
||||
|
||||
|
@ -752,10 +787,14 @@ fn map() {
|
|||
fn traverse_depth_first() {
|
||||
let mut tree = Tree::new(0);
|
||||
let parent = tree.root();
|
||||
let child1 = tree.add_child(parent, 1);
|
||||
tree.add_child(child1, 2);
|
||||
let child2 = tree.add_child(parent, 3);
|
||||
tree.add_child(child2, 4);
|
||||
let child1 = tree.create_node(1);
|
||||
tree.add_child(parent, child1);
|
||||
let grandchild1 = tree.create_node(2);
|
||||
tree.add_child(child1, grandchild1);
|
||||
let child2 = tree.create_node(3);
|
||||
tree.add_child(parent, child2);
|
||||
let grandchild2 = tree.create_node(4);
|
||||
tree.add_child(child2, grandchild2);
|
||||
|
||||
let mut node_count = 0;
|
||||
tree.traverse_depth_first(move |node| {
|
||||
|
@ -768,10 +807,14 @@ fn traverse_depth_first() {
|
|||
fn traverse_breadth_first() {
|
||||
let mut tree = Tree::new(0);
|
||||
let parent = tree.root();
|
||||
let child1 = tree.add_child(parent, 1);
|
||||
tree.add_child(child1, 3);
|
||||
let child2 = tree.add_child(parent, 2);
|
||||
tree.add_child(child2, 4);
|
||||
let child1 = tree.create_node(1);
|
||||
tree.add_child(parent, child1);
|
||||
let grandchild1 = tree.create_node(3);
|
||||
tree.add_child(child1, grandchild1);
|
||||
let child2 = tree.create_node(2);
|
||||
tree.add_child(parent, child2);
|
||||
let grandchild2 = tree.create_node(4);
|
||||
tree.add_child(child2, grandchild2);
|
||||
|
||||
let mut node_count = 0;
|
||||
tree.traverse_breadth_first(move |node| {
|
||||
|
|
Loading…
Reference in a new issue