mirror of
https://github.com/chylex/Bark-Browser.git
synced 2024-11-26 01:42:51 +01:00
Compare commits
No commits in common. "63b56de63f2a440b3d2a759c3d0e821b339a4cdb" and "995e0fc8f8d696195c89dfa8b8bf685a2b305f29" have entirely different histories.
63b56de63f
...
995e0fc8f8
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -311,6 +311,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "slab_tree"
|
name = "slab_tree"
|
||||||
version = "0.3.2"
|
version = "0.3.2"
|
||||||
|
source = "git+https://github.com/jsinger67/slab-tree.git?branch=set_root_fix#75a8dfa3b1f96f76ebe09e2e988deeffe6db54b6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"snowflake",
|
"snowflake",
|
||||||
]
|
]
|
||||||
|
@ -21,4 +21,5 @@ slab_tree = "0.3.2"
|
|||||||
users = "0.11"
|
users = "0.11"
|
||||||
|
|
||||||
[patch.crates-io.slab_tree]
|
[patch.crates-io.slab_tree]
|
||||||
path = "./lib/slab-tree"
|
git = "https://github.com/jsinger67/slab-tree.git"
|
||||||
|
branch = "set_root_fix"
|
||||||
|
@ -42,9 +42,3 @@ Run `docker build --output out .` to build a release binary into the `out/` fold
|
|||||||
This project exists 1) because I couldn't find any tree-based file manager I liked and 2) because I wanted to have fun writing Rust, and I don't really want to spend time reading and reviewing pull requests.
|
This project exists 1) because I couldn't find any tree-based file manager I liked and 2) because I wanted to have fun writing Rust, and I don't really want to spend time reading and reviewing pull requests.
|
||||||
|
|
||||||
For now, issues are closed, and I'm not accepting any major contributions — especially ones related to the roadmap. If you have a small idea, issue, or pull request, feel free to start a [discussion](https://github.com/chylex/Bark-Browser/discussions).
|
For now, issues are closed, and I'm not accepting any major contributions — especially ones related to the roadmap. If you have a small idea, issue, or pull request, feel free to start a [discussion](https://github.com/chylex/Bark-Browser/discussions).
|
||||||
|
|
||||||
# Dependencies
|
|
||||||
|
|
||||||
For a full list of dependencies, see [Cargo.toml](Cargo.toml).
|
|
||||||
|
|
||||||
This repository includes the sources of [slab-tree](https://github.com/iwburns/slab-tree) (by [iwburns](https://github.com/iwburns)) with a [bug fix for `set_root`](https://github.com/iwburns/slab-tree/pull/28) (by [jsinger67](https://github.com/jsinger67)) and additional modifications from me.
|
|
||||||
|
2
lib/slab-tree/.gitignore
vendored
2
lib/slab-tree/.gitignore
vendored
@ -1,2 +0,0 @@
|
|||||||
/.idea/
|
|
||||||
/target/
|
|
16
lib/slab-tree/Cargo.lock
generated
16
lib/slab-tree/Cargo.lock
generated
@ -1,16 +0,0 @@
|
|||||||
# This file is automatically @generated by Cargo.
|
|
||||||
# It is not intended for manual editing.
|
|
||||||
version = 3
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "slab_tree"
|
|
||||||
version = "0.3.2"
|
|
||||||
dependencies = [
|
|
||||||
"snowflake",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "snowflake"
|
|
||||||
version = "1.3.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "27207bb65232eda1f588cf46db2fee75c0808d557f6b3cf19a75f5d6d7c94df1"
|
|
@ -1,15 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "slab_tree"
|
|
||||||
version = "0.3.2"
|
|
||||||
authors = ["Ian <iwburns8@gmail.com>"]
|
|
||||||
description = "A vec-backed tree structure with tree-specific generational indexes."
|
|
||||||
documentation = "https://docs.rs/slab_tree"
|
|
||||||
repository = "https://github.com/iwburns/slab-tree"
|
|
||||||
readme = "README.md"
|
|
||||||
keywords = ["tree", "slab", "slab-tree"]
|
|
||||||
categories = ["data-structures"]
|
|
||||||
license = "MIT"
|
|
||||||
edition = "2018"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
snowflake = "1.3.0"
|
|
@ -1,69 +0,0 @@
|
|||||||
# slab_tree
|
|
||||||
|
|
||||||
[![Build Status](https://travis-ci.org/iwburns/slab-tree.svg?branch=master)](https://travis-ci.org/iwburns/slab-tree)
|
|
||||||
[![](https://tokei.rs/b1/github/iwburns/slab-tree)](https://github.com/iwburns/slab-tree)
|
|
||||||
|
|
||||||
A vec-backed tree structure with tree-specific generational indexes.
|
|
||||||
|
|
||||||
## Overview
|
|
||||||
|
|
||||||
This library provides a `Tree<T>` struct which allows the creation and manipulation of in-memory trees.
|
|
||||||
The tree itself is backed by a vector and the tree's node relationships are managed by tree-specific
|
|
||||||
generational indexes called `NodeId`s (more below). In addition, "views" of tree nodes are handed out
|
|
||||||
which are either immutable (`NodeRef`) or mutable (`NodeMut`) instead of handing out references
|
|
||||||
directly. Most tree mutations are achieved by modifying `NodeMut`s instead of talking to the tree
|
|
||||||
itself.
|
|
||||||
|
|
||||||
`Tree`s in this crate are "just" trees. They do not allow cycles, and they do not allow arbitrary
|
|
||||||
graph structures to be created. Each node in the tree can have an arbitrary number of children, and
|
|
||||||
there is no weight associated with edges between the nodes in the tree.
|
|
||||||
|
|
||||||
**Please Note:** this crate does not support comparison-based data insertion. In other words, this is
|
|
||||||
not a binary search tree (or any other kind of search tree) crate. It is purely a crate for storing
|
|
||||||
data in a hierarchical manner. The caller must know the structure that they wish to build and then use
|
|
||||||
this crate to do so; this library will not make those structural decisions for you.
|
|
||||||
|
|
||||||
## Safety
|
|
||||||
This crate uses `#![forbid(unsafe_code)]` to prevent any and all `unsafe` code usage.
|
|
||||||
|
|
||||||
## Example Usage
|
|
||||||
```rust
|
|
||||||
use slab_tree::*;
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
|
|
||||||
// "hello"
|
|
||||||
// / \
|
|
||||||
// "world" "trees"
|
|
||||||
// |
|
|
||||||
// "are"
|
|
||||||
// |
|
|
||||||
// "cool"
|
|
||||||
|
|
||||||
let mut tree = TreeBuilder::new().with_root("hello").build();
|
|
||||||
let root_id = tree.root_id().expect("root doesn't exist?");
|
|
||||||
let mut hello = tree.get_mut(root_id).unwrap();
|
|
||||||
|
|
||||||
hello.append("world");
|
|
||||||
hello
|
|
||||||
.append("trees")
|
|
||||||
.append("are")
|
|
||||||
.append("cool");
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## `NodeId`s
|
|
||||||
`NodeId`s are tree-specific generational indexes. Using generational indexes means that we can re-use
|
|
||||||
space inside the tree (after nodes have been removed) without also having to re-use the same tree
|
|
||||||
indexes which could potentially cause confusion and bugs. The "tree-specific" part means that indexes
|
|
||||||
from one tree cannot be confused for indexes for another tree. This is because each index contains a
|
|
||||||
process-unique-id which is shared by the tree from which that index originated.
|
|
||||||
|
|
||||||
## Project Goals
|
|
||||||
* Allow caller control of as many allocations as possible (through pre-allocation)
|
|
||||||
* Fast and Ergonomic Node insertion and removal
|
|
||||||
* Arbitrary Tree structure creation and manipulation
|
|
||||||
|
|
||||||
## Non-Goals
|
|
||||||
* Arbitrary _Graph_ structure creation and manipulation
|
|
||||||
* Comparison-based node insertion of any kind
|
|
@ -1,22 +0,0 @@
|
|||||||
///
|
|
||||||
/// Describes all the possible ways to remove a Node from a Tree.
|
|
||||||
///
|
|
||||||
pub enum RemoveBehavior {
|
|
||||||
///
|
|
||||||
/// All children of the removed Node will be dropped from the Tree. All children (and all
|
|
||||||
/// Nodes in each of their sub-trees) will no longer exist in the Tree after this operation.
|
|
||||||
///
|
|
||||||
/// This is slower than `OrphanChildren` but frees up space inside the Tree.
|
|
||||||
///
|
|
||||||
DropChildren,
|
|
||||||
|
|
||||||
///
|
|
||||||
/// All children of the removed Node will be left in the Tree (still accessible via NodeIds).
|
|
||||||
/// However, each child (and their sub-trees) will no longer be connected to the rest of the
|
|
||||||
/// Nodes in the Tree.
|
|
||||||
///
|
|
||||||
/// Orphaned nodes will live in the Tree until they are manually removed or until the Tree is
|
|
||||||
/// Dropped. This is faster than `DropChildren` but doesn't free up any space inside the Tree.
|
|
||||||
///
|
|
||||||
OrphanChildren,
|
|
||||||
}
|
|
@ -1,136 +0,0 @@
|
|||||||
use crate::node::Node;
|
|
||||||
use crate::slab;
|
|
||||||
use crate::NodeId;
|
|
||||||
use snowflake::ProcessUniqueId;
|
|
||||||
|
|
||||||
///
|
|
||||||
/// A wrapper around a Slab containing Node<T> values.
|
|
||||||
///
|
|
||||||
/// Groups a collection of Node<T>s with a process unique id.
|
|
||||||
///
|
|
||||||
#[derive(Debug, PartialEq)]
|
|
||||||
pub(crate) struct CoreTree<T> {
|
|
||||||
id: ProcessUniqueId,
|
|
||||||
slab: slab::Slab<Node<T>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> CoreTree<T> {
|
|
||||||
pub(crate) fn new(capacity: usize) -> CoreTree<T> {
|
|
||||||
CoreTree {
|
|
||||||
id: ProcessUniqueId::new(),
|
|
||||||
slab: slab::Slab::new(capacity),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn capacity(&self) -> usize {
|
|
||||||
self.slab.capacity()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn insert(&mut self, data: T) -> NodeId {
|
|
||||||
let key = self.slab.insert(Node::new(data));
|
|
||||||
self.new_node_id(key)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn remove(&mut self, node_id: NodeId) -> Option<T> {
|
|
||||||
self.filter_by_tree_id(node_id)
|
|
||||||
.and_then(|id| self.slab.remove(id.index))
|
|
||||||
.map(|node| node.data)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn get(&self, node_id: NodeId) -> Option<&Node<T>> {
|
|
||||||
self.filter_by_tree_id(node_id)
|
|
||||||
.and_then(|id| self.slab.get(id.index))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn get_mut(&mut self, node_id: NodeId) -> Option<&mut Node<T>> {
|
|
||||||
self.filter_by_tree_id(node_id)
|
|
||||||
.and_then(move |id| self.slab.get_mut(id.index))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn new_node_id(&self, index: slab::Index) -> NodeId {
|
|
||||||
NodeId {
|
|
||||||
tree_id: self.id,
|
|
||||||
index,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn filter_by_tree_id(&self, node_id: NodeId) -> Option<NodeId> {
|
|
||||||
if node_id.tree_id != self.id {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
Some(node_id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg_attr(tarpaulin, skip)]
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn capacity() {
|
|
||||||
let capacity = 5;
|
|
||||||
let tree = CoreTree::<i32>::new(capacity);
|
|
||||||
assert_eq!(tree.capacity(), capacity);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn insert() {
|
|
||||||
let mut tree = CoreTree::new(0);
|
|
||||||
|
|
||||||
let id = tree.insert(1);
|
|
||||||
let id2 = tree.insert(3);
|
|
||||||
|
|
||||||
assert_eq!(tree.get(id).unwrap().data, 1);
|
|
||||||
assert_eq!(tree.get(id2).unwrap().data, 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn remove() {
|
|
||||||
let mut tree = CoreTree::new(0);
|
|
||||||
|
|
||||||
let id = tree.insert(1);
|
|
||||||
assert_eq!(tree.get(id).unwrap().data, 1);
|
|
||||||
|
|
||||||
let one = tree.remove(id);
|
|
||||||
assert!(one.is_some());
|
|
||||||
|
|
||||||
let one = one.unwrap();
|
|
||||||
assert_eq!(one, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn get() {
|
|
||||||
let mut tree = CoreTree::new(0);
|
|
||||||
|
|
||||||
let id = tree.insert(1);
|
|
||||||
let id2 = tree.insert(3);
|
|
||||||
|
|
||||||
assert_eq!(tree.get(id).unwrap().data, 1);
|
|
||||||
assert_eq!(tree.get(id2).unwrap().data, 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn get_mut() {
|
|
||||||
let mut tree = CoreTree::new(0);
|
|
||||||
|
|
||||||
let id = tree.insert(1);
|
|
||||||
let id2 = tree.insert(3);
|
|
||||||
|
|
||||||
assert_eq!(tree.get_mut(id).unwrap().data, 1);
|
|
||||||
assert_eq!(tree.get_mut(id2).unwrap().data, 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn get_with_bad_id() {
|
|
||||||
let mut tree = CoreTree::new(0);
|
|
||||||
let tree2: CoreTree<i32> = CoreTree::new(0);
|
|
||||||
|
|
||||||
let mut id = tree.insert(1);
|
|
||||||
id.tree_id = tree2.id; // oops, wrong tree id.
|
|
||||||
|
|
||||||
let result = tree.get(id);
|
|
||||||
|
|
||||||
assert!(result.is_none());
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,223 +0,0 @@
|
|||||||
use crate::node::*;
|
|
||||||
use crate::tree::Tree;
|
|
||||||
use crate::NodeId;
|
|
||||||
|
|
||||||
// todo: document this
|
|
||||||
|
|
||||||
pub struct Ancestors<'a, T> {
|
|
||||||
node_id: Option<NodeId>,
|
|
||||||
tree: &'a Tree<T>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T> Ancestors<'a, T> {
|
|
||||||
pub(crate) fn new(node_id: Option<NodeId>, tree: &'a Tree<T>) -> Ancestors<T> {
|
|
||||||
Ancestors { node_id, tree }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T> Iterator for Ancestors<'a, T> {
|
|
||||||
type Item = NodeRef<'a, T>;
|
|
||||||
|
|
||||||
fn next(&mut self) -> Option<NodeRef<'a, T>> {
|
|
||||||
self.node_id
|
|
||||||
.take()
|
|
||||||
.and_then(|node_id| self.tree.get_node_relatives(node_id).parent)
|
|
||||||
.map(|id| {
|
|
||||||
self.node_id = Some(id);
|
|
||||||
NodeRef::new(id, self.tree)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// possibly re-name this, not sure how I feel about it
|
|
||||||
pub struct NextSiblings<'a, T> {
|
|
||||||
node_id: Option<NodeId>,
|
|
||||||
tree: &'a Tree<T>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T> NextSiblings<'a, T> {
|
|
||||||
pub(crate) fn new(node_id: Option<NodeId>, tree: &'a Tree<T>) -> NextSiblings<T> {
|
|
||||||
NextSiblings { node_id, tree }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T> Iterator for NextSiblings<'a, T> {
|
|
||||||
type Item = NodeRef<'a, T>;
|
|
||||||
|
|
||||||
fn next(&mut self) -> Option<NodeRef<'a, T>> {
|
|
||||||
self.node_id.take().map(|node_id| {
|
|
||||||
self.node_id = self.tree.get_node_relatives(node_id).next_sibling;
|
|
||||||
NodeRef::new(node_id, self.tree)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Depth-first pre-order iterator
|
|
||||||
pub struct PreOrder<'a, T> {
|
|
||||||
start: Option<NodeRef<'a, T>>,
|
|
||||||
children: Vec<NextSiblings<'a, T>>,
|
|
||||||
tree: &'a Tree<T>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T> PreOrder<'a, T> {
|
|
||||||
pub(crate) fn new(node: &NodeRef<'a, T>, tree: &'a Tree<T>) -> PreOrder<'a, T> {
|
|
||||||
let children = vec![];
|
|
||||||
let start = tree.get(node.node_id());
|
|
||||||
PreOrder {
|
|
||||||
start,
|
|
||||||
children,
|
|
||||||
tree,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T> Iterator for PreOrder<'a, T> {
|
|
||||||
type Item = NodeRef<'a, T>;
|
|
||||||
|
|
||||||
fn next(&mut self) -> Option<NodeRef<'a, T>> {
|
|
||||||
if let Some(node) = self.start.take() {
|
|
||||||
let first_child_id = node.first_child().map(|child_ref| child_ref.node_id());
|
|
||||||
self.children
|
|
||||||
.push(NextSiblings::new(first_child_id, self.tree));
|
|
||||||
Some(node)
|
|
||||||
} else {
|
|
||||||
while !self.children.is_empty() {
|
|
||||||
if let Some(node_ref) = self.children.last_mut().and_then(Iterator::next) {
|
|
||||||
if let Some(first_child) = node_ref.first_child() {
|
|
||||||
self.children
|
|
||||||
.push(NextSiblings::new(Some(first_child.node_id()), self.tree));
|
|
||||||
}
|
|
||||||
return Some(node_ref);
|
|
||||||
}
|
|
||||||
self.children.pop();
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Depth-first post-order iterator
|
|
||||||
pub struct PostOrder<'a, T> {
|
|
||||||
nodes: Vec<(NodeRef<'a, T>, NextSiblings<'a, T>)>,
|
|
||||||
tree: &'a Tree<T>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T> PostOrder<'a, T> {
|
|
||||||
pub(crate) fn new(node: &NodeRef<'a, T>, tree: &'a Tree<T>) -> PostOrder<'a, T> {
|
|
||||||
let node = tree
|
|
||||||
.get(node.node_id())
|
|
||||||
.expect("getting node of node ref id");
|
|
||||||
let first_child_id = node.first_child().map(|first_child| first_child.node_id());
|
|
||||||
let nodes = vec![(node, NextSiblings::new(first_child_id, tree))];
|
|
||||||
PostOrder { nodes, tree }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T> Iterator for PostOrder<'a, T> {
|
|
||||||
type Item = NodeRef<'a, T>;
|
|
||||||
|
|
||||||
fn next(&mut self) -> Option<NodeRef<'a, T>> {
|
|
||||||
if let Some((node, mut children)) = self.nodes.pop() {
|
|
||||||
if let Some(next) = children.next() {
|
|
||||||
self.nodes.push((node, children));
|
|
||||||
let mut node_id = next.node_id();
|
|
||||||
loop {
|
|
||||||
let node = self.tree.get(node_id).expect("getting node of node ref id");
|
|
||||||
if let Some(first_child) = node.first_child() {
|
|
||||||
node_id = first_child.node_id();
|
|
||||||
let mut children = NextSiblings::new(Some(node_id), self.tree);
|
|
||||||
assert!(children.next().is_some(), "skipping first child");
|
|
||||||
self.nodes.push((node, children));
|
|
||||||
} else {
|
|
||||||
break Some(node);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Some(node)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Depth-first level-order iterator
|
|
||||||
pub struct LevelOrder<'a, T> {
|
|
||||||
start: NodeRef<'a, T>,
|
|
||||||
levels: Vec<(NodeId, NextSiblings<'a, T>)>,
|
|
||||||
tree: &'a Tree<T>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T> LevelOrder<'a, T> {
|
|
||||||
pub(crate) fn new(node: &NodeRef<'a, T>, tree: &'a Tree<T>) -> LevelOrder<'a, T> {
|
|
||||||
let start = tree
|
|
||||||
.get(node.node_id())
|
|
||||||
.expect("getting node of node ref id");
|
|
||||||
let levels = Vec::new();
|
|
||||||
LevelOrder {
|
|
||||||
start,
|
|
||||||
levels,
|
|
||||||
tree,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T> Iterator for LevelOrder<'a, T> {
|
|
||||||
type Item = NodeRef<'a, T>;
|
|
||||||
|
|
||||||
fn next(&mut self) -> Option<NodeRef<'a, T>> {
|
|
||||||
if self.levels.is_empty() {
|
|
||||||
let first_child_id = self.start.first_child().map(|child| child.node_id());
|
|
||||||
self.levels.push((
|
|
||||||
self.start.node_id(),
|
|
||||||
NextSiblings::new(first_child_id, self.tree),
|
|
||||||
));
|
|
||||||
let node = self
|
|
||||||
.tree
|
|
||||||
.get(self.start.node_id())
|
|
||||||
.expect("getting node of existing node ref id");
|
|
||||||
Some(node)
|
|
||||||
} else {
|
|
||||||
let mut on_level = self.levels.len();
|
|
||||||
let next_level = on_level + 1;
|
|
||||||
let mut level = on_level;
|
|
||||||
while level > 0 {
|
|
||||||
if let Some(node) = self.levels.last_mut().expect("non-empty levels").1.next() {
|
|
||||||
if level >= on_level {
|
|
||||||
return Some(node);
|
|
||||||
} else {
|
|
||||||
let first_child_id = node.first_child().map(|child| child.node_id());
|
|
||||||
self.levels
|
|
||||||
.push((node.node_id(), NextSiblings::new(first_child_id, self.tree)));
|
|
||||||
level += 1;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let (node_id, _) = self.levels.pop().expect("on level > 0");
|
|
||||||
if let Some(next) = self.levels.last_mut().and_then(|level| level.1.next()) {
|
|
||||||
let first_child_id = next.first_child().map(|child| child.node_id());
|
|
||||||
self.levels
|
|
||||||
.push((next.node_id(), NextSiblings::new(first_child_id, self.tree)));
|
|
||||||
} else if level == 1 {
|
|
||||||
if on_level < next_level {
|
|
||||||
on_level += 1;
|
|
||||||
let node = self
|
|
||||||
.tree
|
|
||||||
.get(node_id)
|
|
||||||
.expect("getting node of existing node ref id");
|
|
||||||
let first_child_id = node.first_child().map(|child| child.node_id());
|
|
||||||
self.levels.push((
|
|
||||||
node.node_id(),
|
|
||||||
NextSiblings::new(first_child_id, self.tree),
|
|
||||||
));
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
level -= 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,96 +0,0 @@
|
|||||||
#![forbid(unsafe_code)]
|
|
||||||
|
|
||||||
//!
|
|
||||||
//! # slab_tree
|
|
||||||
//!
|
|
||||||
//! [![Build Status](https://travis-ci.org/iwburns/slab-tree.svg?branch=master)](https://travis-ci.org/iwburns/slab-tree)
|
|
||||||
//! [![](https://tokei.rs/b1/github/iwburns/slab-tree)](https://github.com/iwburns/slab-tree)
|
|
||||||
//!
|
|
||||||
//! A vec-backed tree structure with tree-specific generational indexes.
|
|
||||||
//!
|
|
||||||
//! ## Overview
|
|
||||||
//!
|
|
||||||
//! This library provides a `Tree<T>` struct which allows the creation and manipulation of in-memory trees.
|
|
||||||
//! The tree itself is backed by a vector and the tree's node relationships are managed by tree-specific
|
|
||||||
//! generational indexes called `NodeId`s (more below). In addition, "views" of tree nodes are handed out
|
|
||||||
//! which are either immutable (`NodeRef`) or mutable (`NodeMut`) instead of handing out references
|
|
||||||
//! directly. Most tree mutations are achieved by modifying `NodeMut`s instead of talking to the tree
|
|
||||||
//! itself.
|
|
||||||
//!
|
|
||||||
//! `Tree`s in this crate are "just" trees. They do not allow cycles, and they do not allow arbitrary
|
|
||||||
//! graph structures to be created. Each node in the tree can have an arbitrary number of children, and
|
|
||||||
//! there is no weight associated with edges between the nodes in the tree.
|
|
||||||
//!
|
|
||||||
//! **Please Note:** this crate does not support comparison-based data insertion. In other words, this is
|
|
||||||
//! not a binary search tree (or any other kind of search tree) crate. It is purely a crate for storing
|
|
||||||
//! data in a hierarchical manner. The caller must know the structure that they wish to build and then use
|
|
||||||
//! this crate to do so; this library will not make those structural decisions for you.
|
|
||||||
//!
|
|
||||||
//! ## Safety
|
|
||||||
//! This crate uses `#![forbid(unsafe_code)]` to prevent any and all `unsafe` code usage.
|
|
||||||
//!
|
|
||||||
//! ## Example Usage
|
|
||||||
//! ```
|
|
||||||
//! use slab_tree::*;
|
|
||||||
//!
|
|
||||||
//! // "hello"
|
|
||||||
//! // / \
|
|
||||||
//! // "world" "trees"
|
|
||||||
//! // |
|
|
||||||
//! // "are"
|
|
||||||
//! // |
|
|
||||||
//! // "cool"
|
|
||||||
//!
|
|
||||||
//! let mut tree = TreeBuilder::new().with_root("hello").build();
|
|
||||||
//! let root_id = tree.root_id().expect("root doesn't exist?");
|
|
||||||
//! let mut hello = tree.get_mut(root_id).unwrap();
|
|
||||||
//!
|
|
||||||
//! hello.append("world");
|
|
||||||
//! hello
|
|
||||||
//! .append("trees")
|
|
||||||
//! .append("are")
|
|
||||||
//! .append("cool");
|
|
||||||
//! ```
|
|
||||||
//!
|
|
||||||
//! ## `NodeId`s
|
|
||||||
//! `NodeId`s are tree-specific generational indexes. Using generational indexes means that we can re-use
|
|
||||||
//! space inside the tree (after nodes have been removed) without also having to re-use the same tree
|
|
||||||
//! indexes which could potentially cause confusion and bugs. The "tree-specific" part means that indexes
|
|
||||||
//! from one tree cannot be confused for indexes for another tree. This is because each index contains a
|
|
||||||
//! process-unique-id which is shared by the tree from which that index originated.
|
|
||||||
//!
|
|
||||||
//! ## Project Goals
|
|
||||||
//! * Allow caller control of as many allocations as possible (through pre-allocation)
|
|
||||||
//! * Fast and Ergonomic Node insertion and removal
|
|
||||||
//! * Arbitrary Tree structure creation and manipulation
|
|
||||||
//!
|
|
||||||
//! ## Non-Goals
|
|
||||||
//! * Arbitrary _Graph_ structure creation and manipulation
|
|
||||||
//! * Comparison-based node insertion of any kind
|
|
||||||
//!
|
|
||||||
|
|
||||||
pub mod behaviors;
|
|
||||||
mod core_tree;
|
|
||||||
pub mod iter;
|
|
||||||
pub mod node;
|
|
||||||
mod slab;
|
|
||||||
pub mod tree;
|
|
||||||
|
|
||||||
pub use crate::behaviors::RemoveBehavior;
|
|
||||||
pub use crate::iter::Ancestors;
|
|
||||||
pub use crate::iter::NextSiblings;
|
|
||||||
pub use crate::node::NodeMut;
|
|
||||||
pub use crate::node::NodeRef;
|
|
||||||
pub use crate::tree::Tree;
|
|
||||||
pub use crate::tree::TreeBuilder;
|
|
||||||
use snowflake::ProcessUniqueId;
|
|
||||||
|
|
||||||
///
|
|
||||||
/// An identifier used to differentiate between Nodes and tie
|
|
||||||
/// them to a specific tree.
|
|
||||||
///
|
|
||||||
#[derive(Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)]
|
|
||||||
pub struct NodeId {
|
|
||||||
tree_id: ProcessUniqueId,
|
|
||||||
index: slab::Index,
|
|
||||||
}
|
|
@ -1,39 +0,0 @@
|
|||||||
mod bark_extensions;
|
|
||||||
mod node_mut;
|
|
||||||
mod node_ref;
|
|
||||||
|
|
||||||
pub use self::bark_extensions::*;
|
|
||||||
pub use self::node_mut::NodeMut;
|
|
||||||
pub use self::node_ref::NodeRef;
|
|
||||||
|
|
||||||
use crate::NodeId;
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
|
||||||
pub(crate) struct Relatives {
|
|
||||||
pub(crate) parent: Option<NodeId>,
|
|
||||||
pub(crate) prev_sibling: Option<NodeId>,
|
|
||||||
pub(crate) next_sibling: Option<NodeId>,
|
|
||||||
pub(crate) first_child: Option<NodeId>,
|
|
||||||
pub(crate) last_child: Option<NodeId>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
|
||||||
pub(crate) struct Node<T> {
|
|
||||||
pub(crate) data: T,
|
|
||||||
pub(crate) relatives: Relatives,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Node<T> {
|
|
||||||
pub(crate) fn new(data: T) -> Node<T> {
|
|
||||||
Node {
|
|
||||||
data,
|
|
||||||
relatives: Relatives {
|
|
||||||
parent: None,
|
|
||||||
prev_sibling: None,
|
|
||||||
next_sibling: None,
|
|
||||||
first_child: None,
|
|
||||||
last_child: None,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,79 +0,0 @@
|
|||||||
use crate::NodeId;
|
|
||||||
use crate::NodeMut;
|
|
||||||
use crate::NodeRef;
|
|
||||||
|
|
||||||
impl<'a, T> NodeRef<'a, T> {
|
|
||||||
pub fn parent_id(&self) -> Option<NodeId> {
|
|
||||||
self.parent().map(|node| node.node_id())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn first_child_id(&self) -> Option<NodeId> {
|
|
||||||
self.first_child().map(|node| node.node_id())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn last_child_id(&self) -> Option<NodeId> {
|
|
||||||
self.last_child().map(|node| node.node_id())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn prev_sibling_id(&self) -> Option<NodeId> {
|
|
||||||
self.prev_sibling().map(|node| node.node_id())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn next_sibling_id(&self) -> Option<NodeId> {
|
|
||||||
self.next_sibling().map(|node| node.node_id())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn last_descendant_or_self(&self) -> NodeRef<'a, T> {
|
|
||||||
let mut node = NodeRef::new(self.node_id(), self.tree);
|
|
||||||
|
|
||||||
while let Some(id) = node.get_self_as_node().relatives.last_child {
|
|
||||||
node = NodeRef::new(id, self.tree);
|
|
||||||
}
|
|
||||||
|
|
||||||
node
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn above(&self) -> Option<NodeRef<T>> {
|
|
||||||
if let Some(prev_sibling) = self.prev_sibling() {
|
|
||||||
Some(prev_sibling.last_descendant_or_self())
|
|
||||||
} else {
|
|
||||||
self.parent()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn above_id(&self) -> Option<NodeId> {
|
|
||||||
self.above().map(|node| node.node_id())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn below(&self) -> Option<NodeRef<T>> {
|
|
||||||
self.first_child()
|
|
||||||
.or_else(|| self.next_sibling())
|
|
||||||
.or_else(|| self.ancestors().find_map(|ancestor| ancestor.next_sibling_id()).map(|id| NodeRef::new(id, self.tree)))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn below_id(&self) -> Option<NodeId> {
|
|
||||||
self.below().map(|node| node.node_id())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T> NodeMut<'a, T> {
|
|
||||||
pub fn parent_id(&mut self) -> Option<NodeId> {
|
|
||||||
self.parent().map(|node| node.node_id())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn first_child_id(&mut self) -> Option<NodeId> {
|
|
||||||
self.first_child().map(|node| node.node_id())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn last_child_id(&mut self) -> Option<NodeId> {
|
|
||||||
self.last_child().map(|node| node.node_id())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn prev_sibling_id(&mut self) -> Option<NodeId> {
|
|
||||||
self.prev_sibling().map(|node| node.node_id())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn next_sibling_id(&mut self) -> Option<NodeId> {
|
|
||||||
self.next_sibling().map(|node| node.node_id())
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
@ -1,381 +0,0 @@
|
|||||||
use crate::iter::Ancestors;
|
|
||||||
use crate::iter::LevelOrder;
|
|
||||||
use crate::iter::NextSiblings;
|
|
||||||
use crate::iter::PostOrder;
|
|
||||||
use crate::iter::PreOrder;
|
|
||||||
use crate::node::Node;
|
|
||||||
use crate::tree::Tree;
|
|
||||||
use crate::NodeId;
|
|
||||||
|
|
||||||
///
|
|
||||||
/// An immutable reference to a given `Node`'s data and its relatives.
|
|
||||||
///
|
|
||||||
pub struct NodeRef<'a, T> {
|
|
||||||
pub(super) node_id: NodeId,
|
|
||||||
pub(super) tree: &'a Tree<T>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T> NodeRef<'a, T> {
|
|
||||||
pub(crate) fn new(node_id: NodeId, tree: &'a Tree<T>) -> NodeRef<T> {
|
|
||||||
NodeRef { node_id, tree }
|
|
||||||
}
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Returns the `NodeId` that identifies this `Node` in the tree.
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use slab_tree::tree::TreeBuilder;
|
|
||||||
///
|
|
||||||
/// let mut tree = TreeBuilder::new().with_root(1).build();
|
|
||||||
/// let root_id = tree.root_id().expect("root doesn't exist?");
|
|
||||||
/// let root = tree.root_mut().expect("root doesn't exist?");
|
|
||||||
///
|
|
||||||
/// let root_id_again = root.as_ref().node_id();
|
|
||||||
///
|
|
||||||
/// assert_eq!(root_id_again, root_id);
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
pub fn node_id(&self) -> NodeId {
|
|
||||||
self.node_id
|
|
||||||
}
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Returns a reference to the data contained by the given `Node`.
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use slab_tree::tree::TreeBuilder;
|
|
||||||
///
|
|
||||||
/// let mut tree = TreeBuilder::new().with_root(1).build();
|
|
||||||
///
|
|
||||||
/// let root = tree.root().expect("root doesn't exist?");
|
|
||||||
///
|
|
||||||
/// assert_eq!(root.data(), &1);
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
pub fn data(&self) -> &'a T {
|
|
||||||
if let Some(node) = self.tree.get_node(self.node_id) {
|
|
||||||
&node.data
|
|
||||||
} else {
|
|
||||||
unreachable!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Returns a `NodeRef` pointing to this `Node`'s parent. Returns a `Some`-value containing
|
|
||||||
/// the `NodeRef` if this `Node` has a parent; otherwise returns a `None`.
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use slab_tree::tree::TreeBuilder;
|
|
||||||
///
|
|
||||||
/// let mut tree = TreeBuilder::new().with_root(1).build();
|
|
||||||
///
|
|
||||||
/// let root = tree.root().expect("root doesn't exist?");
|
|
||||||
///
|
|
||||||
/// assert!(root.parent().is_none());
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
pub fn parent(&self) -> Option<NodeRef<T>> {
|
|
||||||
self.get_self_as_node()
|
|
||||||
.relatives
|
|
||||||
.parent
|
|
||||||
.map(|id| NodeRef::new(id, self.tree))
|
|
||||||
}
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Returns a `NodeRef` pointing to this `Node`'s previous sibling. Returns a `Some`-value
|
|
||||||
/// containing the `NodeRef` if this `Node` has a previous sibling; otherwise returns a `None`.
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use slab_tree::tree::TreeBuilder;
|
|
||||||
///
|
|
||||||
/// let mut tree = TreeBuilder::new().with_root(1).build();
|
|
||||||
///
|
|
||||||
/// let root = tree.root().expect("root doesn't exist?");
|
|
||||||
///
|
|
||||||
/// assert!(root.prev_sibling().is_none());
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
pub fn prev_sibling(&self) -> Option<NodeRef<T>> {
|
|
||||||
self.get_self_as_node()
|
|
||||||
.relatives
|
|
||||||
.prev_sibling
|
|
||||||
.map(|id| NodeRef::new(id, self.tree))
|
|
||||||
}
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Returns a `NodeRef` pointing to this `Node`'s next sibling. Returns a `Some`-value
|
|
||||||
/// containing the `NodeRef` if this `Node` has a next sibling; otherwise returns a `None`.
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use slab_tree::tree::TreeBuilder;
|
|
||||||
///
|
|
||||||
/// let mut tree = TreeBuilder::new().with_root(1).build();
|
|
||||||
///
|
|
||||||
/// let root = tree.root().expect("root doesn't exist?");
|
|
||||||
///
|
|
||||||
/// assert!(root.next_sibling().is_none());
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
pub fn next_sibling(&self) -> Option<NodeRef<T>> {
|
|
||||||
self.get_self_as_node()
|
|
||||||
.relatives
|
|
||||||
.next_sibling
|
|
||||||
.map(|id| NodeRef::new(id, self.tree))
|
|
||||||
}
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Returns a `NodeRef` pointing to this `Node`'s first child. Returns a `Some`-value
|
|
||||||
/// containing the `NodeRef` if this `Node` has a first child; otherwise returns a `None`.
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use slab_tree::tree::TreeBuilder;
|
|
||||||
///
|
|
||||||
/// let mut tree = TreeBuilder::new().with_root(1).build();
|
|
||||||
///
|
|
||||||
/// let root = tree.root().expect("root doesn't exist?");
|
|
||||||
///
|
|
||||||
/// assert!(root.first_child().is_none());
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
pub fn first_child(&self) -> Option<NodeRef<T>> {
|
|
||||||
self.get_self_as_node()
|
|
||||||
.relatives
|
|
||||||
.first_child
|
|
||||||
.map(|id| NodeRef::new(id, self.tree))
|
|
||||||
}
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Returns a `NodeRef` pointing to this `Node`'s last child. Returns a `Some`-value
|
|
||||||
/// containing the `NodeRef` if this `Node` has a last child; otherwise returns a `None`.
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use slab_tree::tree::TreeBuilder;
|
|
||||||
///
|
|
||||||
/// let mut tree = TreeBuilder::new().with_root(1).build();
|
|
||||||
///
|
|
||||||
/// let root = tree.root().expect("root doesn't exist?");
|
|
||||||
///
|
|
||||||
/// assert!(root.last_child().is_none());
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
pub fn last_child(&self) -> Option<NodeRef<T>> {
|
|
||||||
self.get_self_as_node()
|
|
||||||
.relatives
|
|
||||||
.last_child
|
|
||||||
.map(|id| NodeRef::new(id, self.tree))
|
|
||||||
}
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Returns a `Iterator` over the given `Node`'s ancestors. Each call to `Iterator::next()`
|
|
||||||
/// returns a `NodeRef` pointing to the current `Node`'s parent.
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use slab_tree::tree::TreeBuilder;
|
|
||||||
///
|
|
||||||
/// let mut tree = TreeBuilder::new().with_root(1).build();
|
|
||||||
///
|
|
||||||
/// let leaf_id = tree.root_mut().expect("root doesn't exist?")
|
|
||||||
/// .append(2)
|
|
||||||
/// .append(3)
|
|
||||||
/// .append(4)
|
|
||||||
/// .node_id();
|
|
||||||
///
|
|
||||||
/// let leaf = tree.get(leaf_id).unwrap();
|
|
||||||
///
|
|
||||||
/// let values = [3, 2, 1];
|
|
||||||
/// for (i, ancestor) in leaf.ancestors().enumerate() {
|
|
||||||
/// assert_eq!(ancestor.data(), &values[i]);
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
pub fn ancestors(&self) -> Ancestors<'a, T> {
|
|
||||||
Ancestors::new(Some(self.node_id), self.tree)
|
|
||||||
}
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Returns a `Iterator` over the given `Node`'s children. Each call to `Iterator::next()`
|
|
||||||
/// returns a `NodeRef` pointing to the next child of the given `Node`.
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use slab_tree::tree::TreeBuilder;
|
|
||||||
///
|
|
||||||
/// let mut tree = TreeBuilder::new().with_root(1).build();
|
|
||||||
///
|
|
||||||
/// let mut root = tree.root_mut().expect("root doesn't exist?");
|
|
||||||
/// root.append(2);
|
|
||||||
/// root.append(3);
|
|
||||||
/// root.append(4);
|
|
||||||
///
|
|
||||||
/// let root = root.as_ref();
|
|
||||||
///
|
|
||||||
/// let values = [2, 3, 4];
|
|
||||||
/// for (i, child) in root.children().enumerate() {
|
|
||||||
/// assert_eq!(child.data(), &values[i]);
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
pub fn children(&self) -> NextSiblings<'a, T> {
|
|
||||||
let first_child_id = self.tree.get_node_relatives(self.node_id).first_child;
|
|
||||||
NextSiblings::new(first_child_id, self.tree)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Depth-first pre-order traversal.
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use slab_tree::tree::TreeBuilder;
|
|
||||||
///
|
|
||||||
/// let mut tree = TreeBuilder::new().with_root(0i64).build();
|
|
||||||
/// let root_id = tree.root().unwrap().node_id();
|
|
||||||
/// let one_id = tree.get_mut(root_id).unwrap().append(1).node_id();
|
|
||||||
/// tree.get_mut(one_id).unwrap().append(2);
|
|
||||||
/// tree.get_mut(one_id).unwrap().append(3);
|
|
||||||
/// tree.get_mut(root_id).unwrap().append(4);
|
|
||||||
/// let pre_order = tree.root().unwrap().traverse_pre_order()
|
|
||||||
/// .map(|node_ref| node_ref.data().clone()).collect::<Vec<i64>>();
|
|
||||||
/// assert_eq!(pre_order, vec![0, 1, 2, 3, 4]);
|
|
||||||
/// ```
|
|
||||||
pub fn traverse_pre_order(&self) -> PreOrder<'a, T> {
|
|
||||||
PreOrder::new(self, self.tree)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Depth-first post-order traversal.
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use slab_tree::tree::TreeBuilder;
|
|
||||||
///
|
|
||||||
/// let mut tree = TreeBuilder::new().with_root(0i64).build();
|
|
||||||
/// let root_id = tree.root().unwrap().node_id();
|
|
||||||
/// let one_id = tree.get_mut(root_id).unwrap().append(1).node_id();
|
|
||||||
/// tree.get_mut(one_id).unwrap().append(2);
|
|
||||||
/// tree.get_mut(one_id).unwrap().append(3);
|
|
||||||
/// tree.get_mut(root_id).unwrap().append(4);
|
|
||||||
/// let post_order = tree.root().unwrap().traverse_post_order()
|
|
||||||
/// .map(|node_ref| node_ref.data().clone()).collect::<Vec<i64>>();
|
|
||||||
/// assert_eq!(post_order, vec![2, 3, 1, 4, 0]);
|
|
||||||
/// ```
|
|
||||||
pub fn traverse_post_order(&self) -> PostOrder<'a, T> {
|
|
||||||
PostOrder::new(self, self.tree)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Depth-first level-order traversal.
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use slab_tree::tree::TreeBuilder;
|
|
||||||
///
|
|
||||||
/// let mut tree = TreeBuilder::new().with_root(0i64).build();
|
|
||||||
/// let root_id = tree.root().unwrap().node_id();
|
|
||||||
/// let one_id = tree.get_mut(root_id).unwrap().append(1).node_id();
|
|
||||||
/// tree.get_mut(one_id).unwrap().append(2);
|
|
||||||
/// tree.get_mut(one_id).unwrap().append(3);
|
|
||||||
/// tree.get_mut(root_id).unwrap().append(4);
|
|
||||||
/// let level_order = tree.root().unwrap().traverse_level_order()
|
|
||||||
/// .map(|node_ref| node_ref.data().clone()).collect::<Vec<i64>>();
|
|
||||||
/// assert_eq!(level_order, vec![0, 1, 4, 2, 3]);
|
|
||||||
/// ```
|
|
||||||
pub fn traverse_level_order(&self) -> LevelOrder<'a, T> {
|
|
||||||
LevelOrder::new(self, self.tree)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn get_self_as_node(&self) -> &Node<T> {
|
|
||||||
if let Some(node) = self.tree.get_node(self.node_id) {
|
|
||||||
&node
|
|
||||||
} else {
|
|
||||||
unreachable!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg_attr(tarpaulin, skip)]
|
|
||||||
#[cfg(test)]
|
|
||||||
mod node_ref_tests {
|
|
||||||
use crate::tree::Tree;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn data() {
|
|
||||||
let mut tree = Tree::new();
|
|
||||||
tree.set_root(1);
|
|
||||||
let root_id = tree.root_id().expect("root doesn't exist?");
|
|
||||||
let root_ref = tree.get(root_id).unwrap();
|
|
||||||
assert_eq!(root_ref.data(), &1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn parent() {
|
|
||||||
let mut tree = Tree::new();
|
|
||||||
tree.set_root(1);
|
|
||||||
let root_id = tree.root_id().expect("root doesn't exist?");
|
|
||||||
let root_ref = tree.get(root_id).unwrap();
|
|
||||||
assert!(root_ref.parent().is_none());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn prev_sibling() {
|
|
||||||
let mut tree = Tree::new();
|
|
||||||
tree.set_root(1);
|
|
||||||
let root_id = tree.root_id().expect("root doesn't exist?");
|
|
||||||
let root_ref = tree.get(root_id).unwrap();
|
|
||||||
assert!(root_ref.prev_sibling().is_none());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn next_sibling() {
|
|
||||||
let mut tree = Tree::new();
|
|
||||||
tree.set_root(1);
|
|
||||||
let root_id = tree.root_id().expect("root doesn't exist?");
|
|
||||||
let root_ref = tree.get(root_id).unwrap();
|
|
||||||
assert!(root_ref.next_sibling().is_none());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn first_child() {
|
|
||||||
let mut tree = Tree::new();
|
|
||||||
tree.set_root(1);
|
|
||||||
let root_id = tree.root_id().expect("root doesn't exist?");
|
|
||||||
let root_ref = tree.get(root_id).unwrap();
|
|
||||||
assert!(root_ref.first_child().is_none());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn last_child() {
|
|
||||||
let mut tree = Tree::new();
|
|
||||||
tree.set_root(1);
|
|
||||||
let root_id = tree.root_id().expect("root doesn't exist?");
|
|
||||||
let root_ref = tree.get(root_id).unwrap();
|
|
||||||
assert!(root_ref.last_child().is_none());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn ancestors() {
|
|
||||||
let mut tree = Tree::new();
|
|
||||||
tree.set_root(1);
|
|
||||||
|
|
||||||
let mut root_mut = tree.root_mut().expect("root doesn't exist");
|
|
||||||
let node_id = root_mut.append(2).append(3).append(4).append(5).node_id();
|
|
||||||
|
|
||||||
let values = [4, 3, 2, 1];
|
|
||||||
|
|
||||||
let bottom_node = tree.get(node_id).unwrap();
|
|
||||||
for (i, node_ref) in bottom_node.ancestors().enumerate() {
|
|
||||||
assert_eq!(node_ref.data(), &values[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn children() {
|
|
||||||
let mut tree = Tree::new();
|
|
||||||
tree.set_root(1);
|
|
||||||
|
|
||||||
let mut root = tree.root_mut().expect("root doesn't exist");
|
|
||||||
root.append(2);
|
|
||||||
root.append(3);
|
|
||||||
root.append(4);
|
|
||||||
root.append(5);
|
|
||||||
|
|
||||||
let values = [2, 3, 4, 5];
|
|
||||||
let root = root.as_ref();
|
|
||||||
|
|
||||||
for (i, node_ref) in root.children().enumerate() {
|
|
||||||
assert_eq!(node_ref.data(), &values[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,497 +0,0 @@
|
|||||||
use std::mem;
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)]
|
|
||||||
pub(super) struct Index {
|
|
||||||
index: usize,
|
|
||||||
generation: u64,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
|
||||||
enum Slot<T> {
|
|
||||||
Empty { next_free_slot: Option<usize> },
|
|
||||||
Filled { item: T, generation: u64 },
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
|
||||||
pub(super) struct Slab<T> {
|
|
||||||
data: Vec<Slot<T>>,
|
|
||||||
first_free_slot: Option<usize>,
|
|
||||||
generation: u64,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Slab<T> {
|
|
||||||
pub(super) fn new(capacity: usize) -> Slab<T> {
|
|
||||||
Slab {
|
|
||||||
data: Vec::with_capacity(capacity),
|
|
||||||
first_free_slot: None,
|
|
||||||
generation: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn capacity(&self) -> usize {
|
|
||||||
self.data.capacity()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn insert(&mut self, item: T) -> Index {
|
|
||||||
let new_slot = Slot::Filled {
|
|
||||||
item,
|
|
||||||
generation: self.generation,
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(index) = self.first_free_slot {
|
|
||||||
match mem::replace(&mut self.data[index], new_slot) {
|
|
||||||
Slot::Empty { next_free_slot } => {
|
|
||||||
self.first_free_slot = next_free_slot;
|
|
||||||
}
|
|
||||||
_ => unreachable!(),
|
|
||||||
};
|
|
||||||
|
|
||||||
Index {
|
|
||||||
index,
|
|
||||||
generation: self.generation,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
self.data.push(new_slot);
|
|
||||||
Index {
|
|
||||||
index: self.data.len() - 1,
|
|
||||||
generation: self.generation,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn remove(&mut self, index: Index) -> Option<T> {
|
|
||||||
if index.index >= self.data.len() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let slot = mem::replace(
|
|
||||||
&mut self.data[index.index],
|
|
||||||
Slot::Empty {
|
|
||||||
next_free_slot: self.first_free_slot,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
match slot {
|
|
||||||
Slot::Filled { item, generation } => {
|
|
||||||
if index.generation == generation {
|
|
||||||
self.generation += 1;
|
|
||||||
self.first_free_slot = Some(index.index);
|
|
||||||
Some(item)
|
|
||||||
} else {
|
|
||||||
self.data[index.index] = Slot::Filled { item, generation };
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
s => {
|
|
||||||
self.data[index.index] = s;
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn get(&self, index: Index) -> Option<&T> {
|
|
||||||
self.data.get(index.index).and_then(|slot| match slot {
|
|
||||||
Slot::Filled { item, generation } => {
|
|
||||||
if index.generation == *generation {
|
|
||||||
return Some(item);
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
_ => None,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn get_mut(&mut self, index: Index) -> Option<&mut T> {
|
|
||||||
self.data.get_mut(index.index).and_then(|slot| match slot {
|
|
||||||
Slot::Filled { item, generation } => {
|
|
||||||
if index.generation == *generation {
|
|
||||||
return Some(item);
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
_ => None,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg_attr(tarpaulin, skip)]
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn capacity() {
|
|
||||||
let capacity = 5;
|
|
||||||
let slab = Slab::<i32>::new(capacity);
|
|
||||||
|
|
||||||
assert_eq!(slab.capacity(), capacity);
|
|
||||||
assert!(slab.first_free_slot.is_none());
|
|
||||||
assert_eq!(slab.generation, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn insert() {
|
|
||||||
let capacity = 2;
|
|
||||||
let mut slab = Slab::new(capacity);
|
|
||||||
|
|
||||||
let six = slab.insert(6);
|
|
||||||
|
|
||||||
assert!(slab.first_free_slot.is_none());
|
|
||||||
assert_eq!(slab.generation, 0);
|
|
||||||
assert_eq!(slab.data.len(), 1);
|
|
||||||
assert_eq!(slab.data.capacity(), capacity);
|
|
||||||
|
|
||||||
assert_eq!(six.generation, 0);
|
|
||||||
assert_eq!(six.index, 0);
|
|
||||||
|
|
||||||
let seven = slab.insert(7);
|
|
||||||
|
|
||||||
assert!(slab.first_free_slot.is_none());
|
|
||||||
assert_eq!(slab.generation, 0);
|
|
||||||
assert_eq!(slab.data.len(), 2);
|
|
||||||
assert_eq!(slab.data.capacity(), capacity);
|
|
||||||
|
|
||||||
assert_eq!(seven.generation, 0);
|
|
||||||
assert_eq!(seven.index, 1);
|
|
||||||
|
|
||||||
let eight = slab.insert(8);
|
|
||||||
|
|
||||||
assert!(slab.first_free_slot.is_none());
|
|
||||||
assert_eq!(slab.generation, 0);
|
|
||||||
assert_eq!(slab.data.len(), 3);
|
|
||||||
assert!(slab.data.capacity() >= capacity);
|
|
||||||
|
|
||||||
assert_eq!(eight.generation, 0);
|
|
||||||
assert_eq!(eight.index, 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn remove_basic() {
|
|
||||||
let mut slab = Slab::new(5);
|
|
||||||
let _six = slab.insert(6);
|
|
||||||
let seven = slab.insert(7);
|
|
||||||
let _eight = slab.insert(8);
|
|
||||||
// |6|7|8|
|
|
||||||
|
|
||||||
let seven_rem = slab.remove(seven);
|
|
||||||
// |6|.|8|
|
|
||||||
assert!(seven_rem.is_some());
|
|
||||||
assert_eq!(seven_rem.unwrap(), 7);
|
|
||||||
|
|
||||||
assert_eq!(slab.first_free_slot.unwrap_or(10), 1);
|
|
||||||
assert_eq!(slab.generation, 1);
|
|
||||||
|
|
||||||
let six_slot = slab.data.get(0);
|
|
||||||
assert!(six_slot.is_some());
|
|
||||||
|
|
||||||
match six_slot.unwrap() {
|
|
||||||
Slot::Empty { .. } => {
|
|
||||||
panic!("Slot should be filled after call to insert.");
|
|
||||||
}
|
|
||||||
Slot::Filled { item, generation } => {
|
|
||||||
assert_eq!(item, &6);
|
|
||||||
assert_eq!(generation, &0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let seven_slot = slab.data.get(1);
|
|
||||||
assert!(seven_slot.is_some());
|
|
||||||
|
|
||||||
match seven_slot.unwrap() {
|
|
||||||
Slot::Empty { next_free_slot } => {
|
|
||||||
assert!(next_free_slot.is_none());
|
|
||||||
}
|
|
||||||
Slot::Filled { .. } => {
|
|
||||||
panic!("Slot should be empty after call to remove.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let eight_slot = slab.data.get(2);
|
|
||||||
assert!(eight_slot.is_some());
|
|
||||||
|
|
||||||
match eight_slot.unwrap() {
|
|
||||||
Slot::Empty { .. } => {
|
|
||||||
panic!("Slot should be filled after call to insert.");
|
|
||||||
}
|
|
||||||
Slot::Filled { item, generation } => {
|
|
||||||
assert_eq!(item, &8);
|
|
||||||
assert_eq!(generation, &0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn double_remove() {
|
|
||||||
let mut slab = Slab::new(5);
|
|
||||||
let _six = slab.insert(6);
|
|
||||||
let seven = slab.insert(7);
|
|
||||||
let _eight = slab.insert(8);
|
|
||||||
// |6|7|8|
|
|
||||||
|
|
||||||
let seven_rem = slab.remove(seven);
|
|
||||||
// |6|.|8|
|
|
||||||
assert!(seven_rem.is_some());
|
|
||||||
assert_eq!(seven_rem.unwrap(), 7);
|
|
||||||
|
|
||||||
let seven_again = slab.remove(seven);
|
|
||||||
assert!(seven_again.is_none());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn remove_multiple() {
|
|
||||||
let mut slab = Slab::new(5);
|
|
||||||
let _six = slab.insert(6);
|
|
||||||
let seven = slab.insert(7);
|
|
||||||
let eight = slab.insert(8);
|
|
||||||
// |6|7|8|
|
|
||||||
|
|
||||||
let seven_rem = slab.remove(seven);
|
|
||||||
// |6|.|8|
|
|
||||||
assert!(seven_rem.is_some());
|
|
||||||
assert_eq!(seven_rem.unwrap(), 7);
|
|
||||||
|
|
||||||
assert_eq!(slab.first_free_slot.unwrap_or(10), 1);
|
|
||||||
assert_eq!(slab.generation, 1);
|
|
||||||
|
|
||||||
let six_slot = slab.data.get(0);
|
|
||||||
assert!(six_slot.is_some());
|
|
||||||
|
|
||||||
match six_slot.unwrap() {
|
|
||||||
Slot::Empty { .. } => {
|
|
||||||
panic!("Slot should be filled after call to insert.");
|
|
||||||
}
|
|
||||||
Slot::Filled { item, generation } => {
|
|
||||||
assert_eq!(item, &6);
|
|
||||||
assert_eq!(generation, &0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let seven_slot = slab.data.get(1);
|
|
||||||
assert!(seven_slot.is_some());
|
|
||||||
|
|
||||||
match seven_slot.unwrap() {
|
|
||||||
Slot::Empty { next_free_slot } => {
|
|
||||||
assert!(next_free_slot.is_none());
|
|
||||||
}
|
|
||||||
Slot::Filled { .. } => {
|
|
||||||
panic!("Slot should be empty after call to remove.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let eight_slot = slab.data.get(2);
|
|
||||||
assert!(eight_slot.is_some());
|
|
||||||
|
|
||||||
match eight_slot.unwrap() {
|
|
||||||
Slot::Empty { .. } => {
|
|
||||||
panic!("Slot should be filled after call to insert.");
|
|
||||||
}
|
|
||||||
Slot::Filled { item, generation } => {
|
|
||||||
assert_eq!(item, &8);
|
|
||||||
assert_eq!(generation, &0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let eight_rem = slab.remove(eight);
|
|
||||||
// |6|.|.|
|
|
||||||
assert!(eight_rem.is_some());
|
|
||||||
assert_eq!(eight_rem.unwrap(), 8);
|
|
||||||
|
|
||||||
assert_eq!(slab.first_free_slot.unwrap_or(10), 2);
|
|
||||||
assert_eq!(slab.generation, 2);
|
|
||||||
|
|
||||||
let six_slot = slab.data.get(0);
|
|
||||||
assert!(six_slot.is_some());
|
|
||||||
|
|
||||||
match six_slot.unwrap() {
|
|
||||||
Slot::Empty { .. } => {
|
|
||||||
panic!("Slot should be filled after call to insert.");
|
|
||||||
}
|
|
||||||
Slot::Filled { item, generation } => {
|
|
||||||
assert_eq!(item, &6);
|
|
||||||
assert_eq!(generation, &0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let seven_slot = slab.data.get(1);
|
|
||||||
assert!(seven_slot.is_some());
|
|
||||||
|
|
||||||
match seven_slot.unwrap() {
|
|
||||||
Slot::Empty { next_free_slot } => {
|
|
||||||
assert!(next_free_slot.is_none());
|
|
||||||
}
|
|
||||||
Slot::Filled { .. } => {
|
|
||||||
panic!("Slot should be empty after call to remove.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let eight_slot = slab.data.get(2);
|
|
||||||
assert!(eight_slot.is_some());
|
|
||||||
|
|
||||||
match eight_slot.unwrap() {
|
|
||||||
Slot::Empty { next_free_slot } => {
|
|
||||||
assert!(next_free_slot.is_some());
|
|
||||||
assert_eq!(next_free_slot.unwrap(), 1);
|
|
||||||
}
|
|
||||||
Slot::Filled { .. } => {
|
|
||||||
panic!("Slot should be empty after call to remove.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn remove_and_reinsert() {
|
|
||||||
let mut slab = Slab::new(5);
|
|
||||||
let _six = slab.insert(6);
|
|
||||||
let seven = slab.insert(7);
|
|
||||||
let eight = slab.insert(8);
|
|
||||||
// |6|7|8|
|
|
||||||
|
|
||||||
let seven_rem = slab.remove(seven);
|
|
||||||
// |6|.|8|
|
|
||||||
assert!(seven_rem.is_some());
|
|
||||||
assert_eq!(seven_rem.unwrap(), 7);
|
|
||||||
|
|
||||||
assert_eq!(slab.first_free_slot.unwrap_or(10), 1);
|
|
||||||
assert_eq!(slab.generation, 1);
|
|
||||||
|
|
||||||
let eight_rem = slab.remove(eight);
|
|
||||||
// |6|.|.|
|
|
||||||
assert!(eight_rem.is_some());
|
|
||||||
assert_eq!(eight_rem.unwrap(), 8);
|
|
||||||
|
|
||||||
assert_eq!(slab.first_free_slot.unwrap_or(10), 2);
|
|
||||||
assert_eq!(slab.generation, 2);
|
|
||||||
|
|
||||||
let nine = slab.insert(9);
|
|
||||||
// |6|.|9|
|
|
||||||
assert_eq!(nine.index, 2);
|
|
||||||
assert_eq!(nine.generation, 2);
|
|
||||||
|
|
||||||
let eight_again = slab.remove(eight);
|
|
||||||
assert!(eight_again.is_none());
|
|
||||||
|
|
||||||
let six_slot = slab.data.get(0);
|
|
||||||
assert!(six_slot.is_some());
|
|
||||||
|
|
||||||
match six_slot.unwrap() {
|
|
||||||
Slot::Empty { .. } => {
|
|
||||||
panic!("Slot should be filled after call to insert.");
|
|
||||||
}
|
|
||||||
Slot::Filled { item, generation } => {
|
|
||||||
assert_eq!(item, &6);
|
|
||||||
assert_eq!(generation, &0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let seven_slot = slab.data.get(1);
|
|
||||||
assert!(seven_slot.is_some());
|
|
||||||
|
|
||||||
match seven_slot.unwrap() {
|
|
||||||
Slot::Empty { next_free_slot } => {
|
|
||||||
assert!(next_free_slot.is_none());
|
|
||||||
}
|
|
||||||
Slot::Filled { .. } => {
|
|
||||||
panic!("Slot should be empty after call to remove.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let nine_slot = slab.data.get(2);
|
|
||||||
assert!(nine_slot.is_some());
|
|
||||||
|
|
||||||
match nine_slot.unwrap() {
|
|
||||||
Slot::Empty { .. } => {
|
|
||||||
panic!("Slot should be filled after call to insert.");
|
|
||||||
}
|
|
||||||
Slot::Filled { item, generation } => {
|
|
||||||
assert_eq!(item, &9);
|
|
||||||
assert_eq!(generation, &2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn remove_with_bad_index() {
|
|
||||||
let mut slab = Slab::new(5);
|
|
||||||
let _six = slab.insert(6);
|
|
||||||
let _seven = slab.insert(7);
|
|
||||||
let mut eight = slab.insert(8);
|
|
||||||
// |0|1|2| index
|
|
||||||
// |6|7|8| value
|
|
||||||
|
|
||||||
eight.index = 3; // oops, this should be 2
|
|
||||||
|
|
||||||
let eight_rem = slab.remove(eight);
|
|
||||||
assert!(eight_rem.is_none());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn get() {
|
|
||||||
let mut slab = Slab::new(5);
|
|
||||||
|
|
||||||
let six = slab.insert(6);
|
|
||||||
assert_eq!(six.index, 0);
|
|
||||||
assert_eq!(six.generation, 0);
|
|
||||||
|
|
||||||
let seven = slab.insert(7);
|
|
||||||
assert_eq!(seven.index, 1);
|
|
||||||
assert_eq!(seven.generation, 0);
|
|
||||||
|
|
||||||
let six_ref = slab.get(six);
|
|
||||||
assert!(six_ref.is_some());
|
|
||||||
assert_eq!(six_ref.unwrap(), &6);
|
|
||||||
|
|
||||||
slab.remove(six);
|
|
||||||
|
|
||||||
let six_ref = slab.get(six);
|
|
||||||
assert!(six_ref.is_none());
|
|
||||||
|
|
||||||
let eight = slab.insert(8);
|
|
||||||
assert_eq!(eight.index, 0);
|
|
||||||
assert_eq!(eight.generation, 1);
|
|
||||||
|
|
||||||
let eight_ref = slab.get(eight);
|
|
||||||
assert!(eight_ref.is_some());
|
|
||||||
assert_eq!(eight_ref.unwrap(), &8);
|
|
||||||
|
|
||||||
let six_ref = slab.get(six);
|
|
||||||
assert!(six_ref.is_none());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn get_mut() {
|
|
||||||
let mut slab = Slab::new(5);
|
|
||||||
|
|
||||||
let six = slab.insert(6);
|
|
||||||
assert_eq!(six.index, 0);
|
|
||||||
assert_eq!(six.generation, 0);
|
|
||||||
|
|
||||||
let seven = slab.insert(7);
|
|
||||||
assert_eq!(seven.index, 1);
|
|
||||||
assert_eq!(seven.generation, 0);
|
|
||||||
|
|
||||||
let six_mut = slab.get_mut(six);
|
|
||||||
assert!(six_mut.is_some());
|
|
||||||
|
|
||||||
let six_mut = six_mut.unwrap();
|
|
||||||
assert_eq!(six_mut, &mut 6);
|
|
||||||
|
|
||||||
*six_mut = 60;
|
|
||||||
assert_eq!(six_mut, &mut 60);
|
|
||||||
|
|
||||||
slab.remove(six);
|
|
||||||
|
|
||||||
let six_ref = slab.get_mut(six);
|
|
||||||
assert!(six_ref.is_none());
|
|
||||||
|
|
||||||
let eight = slab.insert(8);
|
|
||||||
assert_eq!(eight.index, 0);
|
|
||||||
assert_eq!(eight.generation, 1);
|
|
||||||
|
|
||||||
let eight_ref = slab.get_mut(eight);
|
|
||||||
assert!(eight_ref.is_some());
|
|
||||||
assert_eq!(eight_ref.unwrap(), &mut 8);
|
|
||||||
|
|
||||||
let six_ref = slab.get_mut(six);
|
|
||||||
assert!(six_ref.is_none());
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,813 +0,0 @@
|
|||||||
use crate::behaviors::*;
|
|
||||||
use crate::core_tree::CoreTree;
|
|
||||||
use crate::node::*;
|
|
||||||
use crate::NodeId;
|
|
||||||
|
|
||||||
///
|
|
||||||
/// A `Tree` builder. Provides more control over how a `Tree` is created.
|
|
||||||
///
|
|
||||||
pub struct TreeBuilder<T> {
|
|
||||||
root: Option<T>,
|
|
||||||
capacity: Option<usize>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Default for TreeBuilder<T> {
|
|
||||||
fn default() -> Self {
|
|
||||||
TreeBuilder::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> TreeBuilder<T> {
|
|
||||||
///
|
|
||||||
/// Creates a new `TreeBuilder` with the default settings.
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use slab_tree::tree::TreeBuilder;
|
|
||||||
///
|
|
||||||
/// let _tree_builder = TreeBuilder::new();
|
|
||||||
///
|
|
||||||
/// # _tree_builder.with_root(1);
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
pub fn new() -> TreeBuilder<T> {
|
|
||||||
TreeBuilder {
|
|
||||||
root: None,
|
|
||||||
capacity: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Sets the root `Node` of the `TreeBuilder`.
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use slab_tree::tree::TreeBuilder;
|
|
||||||
///
|
|
||||||
/// let _tree_builder = TreeBuilder::new().with_root(1);
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
pub fn with_root(self, root: T) -> TreeBuilder<T> {
|
|
||||||
TreeBuilder {
|
|
||||||
root: Some(root),
|
|
||||||
capacity: self.capacity,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Sets the capacity of the `TreeBuilder`.
|
|
||||||
///
|
|
||||||
/// This can be used to pre-allocate space in the `Tree` if you know you'll be adding a large
|
|
||||||
/// number of `Node`s to the `Tree`.
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use slab_tree::tree::TreeBuilder;
|
|
||||||
///
|
|
||||||
/// let _tree_builder = TreeBuilder::new().with_capacity(10);
|
|
||||||
///
|
|
||||||
/// # _tree_builder.with_root(1);
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
pub fn with_capacity(self, capacity: usize) -> TreeBuilder<T> {
|
|
||||||
TreeBuilder {
|
|
||||||
root: self.root,
|
|
||||||
capacity: Some(capacity),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Build a `Tree` based upon the current settings in the `TreeBuilder`.
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use slab_tree::tree::TreeBuilder;
|
|
||||||
///
|
|
||||||
/// let _tree = TreeBuilder::new().with_root(1).with_capacity(10).build();
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
pub fn build(self) -> Tree<T> {
|
|
||||||
let capacity = self.capacity.unwrap_or(0);
|
|
||||||
let mut core_tree: CoreTree<T> = CoreTree::new(capacity);
|
|
||||||
let root_id = self.root.map(|val| core_tree.insert(val));
|
|
||||||
|
|
||||||
Tree { root_id, core_tree }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
///
|
|
||||||
/// A tree structure containing `Node`s.
|
|
||||||
///
|
|
||||||
#[derive(Debug, PartialEq)]
|
|
||||||
pub struct Tree<T> {
|
|
||||||
pub(crate) root_id: Option<NodeId>,
|
|
||||||
pub(crate) core_tree: CoreTree<T>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Tree<T> {
|
|
||||||
///
|
|
||||||
/// Creates a new `Tree` with a capacity of 0.
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use slab_tree::tree::Tree;
|
|
||||||
///
|
|
||||||
/// let tree: Tree<i32> = Tree::new();
|
|
||||||
///
|
|
||||||
/// # assert_eq!(tree.capacity(), 0);
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
pub fn new() -> Tree<T> {
|
|
||||||
TreeBuilder::new().build()
|
|
||||||
}
|
|
||||||
|
|
||||||
//todo: write test for this
|
|
||||||
///
|
|
||||||
/// Sets the "root" of the `Tree` to be `root`.
|
|
||||||
///
|
|
||||||
/// If there is already a "root" node in the `Tree`, that node is shifted down and the new
|
|
||||||
/// one takes its place.
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use slab_tree::tree::Tree;
|
|
||||||
///
|
|
||||||
/// let mut tree = Tree::new();
|
|
||||||
///
|
|
||||||
/// let root_id = tree.set_root(1);
|
|
||||||
///
|
|
||||||
/// assert_eq!(tree.root_id().unwrap(), root_id);
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
pub fn set_root(&mut self, root: T) -> NodeId {
|
|
||||||
let old_root_id = self.root_id.take();
|
|
||||||
let new_root_id = self.core_tree.insert(root);
|
|
||||||
|
|
||||||
self.root_id = Some(new_root_id);
|
|
||||||
|
|
||||||
self.set_first_child(new_root_id, old_root_id);
|
|
||||||
self.set_last_child(new_root_id, old_root_id);
|
|
||||||
|
|
||||||
if let Some(node_id) = old_root_id {
|
|
||||||
self.set_parent(node_id, self.root_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
new_root_id
|
|
||||||
}
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Returns the `Tree`'s current capacity. Capacity is defined as the number of times new
|
|
||||||
/// `Node`s can be added to the `Tree` before it must allocate more memory.
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use slab_tree::tree::Tree;
|
|
||||||
///
|
|
||||||
/// let tree: Tree<i32> = Tree::new();
|
|
||||||
///
|
|
||||||
/// assert_eq!(tree.capacity(), 0);
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
pub fn capacity(&self) -> usize {
|
|
||||||
self.core_tree.capacity()
|
|
||||||
}
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Returns the `NodeId` of the root node of the `Tree`.
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use slab_tree::tree::Tree;
|
|
||||||
///
|
|
||||||
/// let mut tree = Tree::new();
|
|
||||||
/// tree.set_root(1);
|
|
||||||
///
|
|
||||||
/// let root_id = tree.root_id().expect("root doesn't exist?");
|
|
||||||
///
|
|
||||||
/// assert_eq!(tree.get(root_id).unwrap().data(), &1);
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
pub fn root_id(&self) -> Option<NodeId> {
|
|
||||||
self.root_id
|
|
||||||
}
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Returns a `NodeRef` pointing to the root `Node` of the `Tree`.
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use slab_tree::tree::Tree;
|
|
||||||
///
|
|
||||||
/// let mut tree = Tree::new();
|
|
||||||
/// tree.set_root(1);
|
|
||||||
///
|
|
||||||
/// let root = tree.root().expect("root doesn't exist?");
|
|
||||||
///
|
|
||||||
/// assert_eq!(root.data(), &1);
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
pub fn root(&self) -> Option<NodeRef<T>> {
|
|
||||||
self.root_id.map(|id| self.new_node_ref(id))
|
|
||||||
}
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Returns a `NodeMut` pointing to the root `Node` of the `Tree`.
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use slab_tree::tree::Tree;
|
|
||||||
///
|
|
||||||
/// let mut tree = Tree::new();
|
|
||||||
/// tree.set_root(1);
|
|
||||||
///
|
|
||||||
/// let mut root = tree.root_mut().expect("root doesn't exist?");
|
|
||||||
/// assert_eq!(root.data(), &mut 1);
|
|
||||||
///
|
|
||||||
/// *root.data() = 2;
|
|
||||||
/// assert_eq!(root.data(), &mut 2);
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
pub fn root_mut(&mut self) -> Option<NodeMut<T>> {
|
|
||||||
self.root_id.map(move |id| self.new_node_mut(id))
|
|
||||||
}
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Returns the `NodeRef` pointing to the `Node` that the given `NodeId` identifies. If the
|
|
||||||
/// `NodeId` in question points to nothing (or belongs to a different `Tree`) a `None`-value
|
|
||||||
/// will be returned; otherwise, a `Some`-value will be returned.
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use slab_tree::tree::Tree;
|
|
||||||
///
|
|
||||||
/// let mut tree = Tree::new();
|
|
||||||
/// tree.set_root(1);
|
|
||||||
/// let root_id = tree.root_id().expect("root doesn't exist?");
|
|
||||||
///
|
|
||||||
/// let root = tree.get(root_id);
|
|
||||||
/// assert!(root.is_some());
|
|
||||||
///
|
|
||||||
/// let root = root.unwrap();
|
|
||||||
/// assert_eq!(root.data(), &1);
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
pub fn get(&self, node_id: NodeId) -> Option<NodeRef<T>> {
|
|
||||||
let _ = self.core_tree.get(node_id)?;
|
|
||||||
Some(self.new_node_ref(node_id))
|
|
||||||
}
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Returns the `NodeMut` pointing to the `Node` that the given `NodeId` identifies. If the
|
|
||||||
/// `NodeId` in question points to nothing (or belongs to a different `Tree`) a `None`-value
|
|
||||||
/// will be returned; otherwise, a `Some`-value will be returned.
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use slab_tree::tree::Tree;
|
|
||||||
///
|
|
||||||
/// let mut tree = Tree::new();
|
|
||||||
/// tree.set_root(1);
|
|
||||||
/// let root_id = tree.root_id().expect("root doesn't exist?");
|
|
||||||
///
|
|
||||||
/// let root = tree.get_mut(root_id);
|
|
||||||
/// assert!(root.is_some());
|
|
||||||
///
|
|
||||||
/// let mut root = root.unwrap();
|
|
||||||
///
|
|
||||||
/// *root.data() = 2;
|
|
||||||
/// assert_eq!(root.data(), &mut 2);
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
pub fn get_mut(&mut self, node_id: NodeId) -> Option<NodeMut<T>> {
|
|
||||||
let _ = self.core_tree.get_mut(node_id)?;
|
|
||||||
Some(self.new_node_mut(node_id))
|
|
||||||
}
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Remove a `Node` by its `NodeId` and return the data that it contained.
|
|
||||||
/// Returns a `Some`-value if the `Node` exists; returns a `None`-value otherwise.
|
|
||||||
///
|
|
||||||
/// Children of the removed `Node` can either be dropped with `DropChildren` or orphaned with
|
|
||||||
/// `OrphanChildren`.
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use slab_tree::tree::TreeBuilder;
|
|
||||||
/// use slab_tree::behaviors::RemoveBehavior::*;
|
|
||||||
///
|
|
||||||
/// let mut tree = TreeBuilder::new().with_root(1).build();
|
|
||||||
/// let two_id = {
|
|
||||||
/// let mut root = tree.root_mut().expect("root doesn't exist?");
|
|
||||||
/// let two_id = root.append(2).node_id();
|
|
||||||
/// root.append(3);
|
|
||||||
/// two_id
|
|
||||||
/// };
|
|
||||||
///
|
|
||||||
/// let two = tree.remove(two_id, DropChildren);
|
|
||||||
///
|
|
||||||
/// assert!(two.is_some());
|
|
||||||
/// assert_eq!(two.unwrap(), 2);
|
|
||||||
///
|
|
||||||
/// let root = tree.root().expect("root doesn't exist?");
|
|
||||||
/// assert!(root.first_child().is_some());
|
|
||||||
/// assert_eq!(root.first_child().unwrap().data(), &mut 3);
|
|
||||||
///
|
|
||||||
/// assert!(root.last_child().is_some());
|
|
||||||
/// assert_eq!(root.last_child().unwrap().data(), &mut 3);
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
pub fn remove(&mut self, node_id: NodeId, behavior: RemoveBehavior) -> Option<T> {
|
|
||||||
if let Some(node) = self.get_node(node_id) {
|
|
||||||
let Relatives {
|
|
||||||
parent,
|
|
||||||
prev_sibling,
|
|
||||||
next_sibling,
|
|
||||||
..
|
|
||||||
} = node.relatives;
|
|
||||||
|
|
||||||
let (is_first_child, is_last_child) = self.is_node_first_last_child(node_id);
|
|
||||||
|
|
||||||
if is_first_child {
|
|
||||||
// parent first child = my next sibling
|
|
||||||
self.set_first_child(parent.expect("parent must exist"), next_sibling);
|
|
||||||
}
|
|
||||||
if is_last_child {
|
|
||||||
// parent last child = my prev sibling
|
|
||||||
self.set_last_child(parent.expect("parent must exist"), prev_sibling);
|
|
||||||
}
|
|
||||||
if let Some(prev) = prev_sibling {
|
|
||||||
self.set_next_sibling(prev, next_sibling);
|
|
||||||
}
|
|
||||||
if let Some(next) = next_sibling {
|
|
||||||
self.set_prev_sibling(next, prev_sibling);
|
|
||||||
}
|
|
||||||
|
|
||||||
match behavior {
|
|
||||||
RemoveBehavior::DropChildren => self.drop_children(node_id),
|
|
||||||
RemoveBehavior::OrphanChildren => self.orphan_children(node_id),
|
|
||||||
};
|
|
||||||
if self.root_id == Some(node_id) {
|
|
||||||
self.root_id = None;
|
|
||||||
}
|
|
||||||
self.core_tree.remove(node_id)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn get_node(&self, node_id: NodeId) -> Option<&Node<T>> {
|
|
||||||
self.core_tree.get(node_id)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn get_node_mut(&mut self, node_id: NodeId) -> Option<&mut Node<T>> {
|
|
||||||
self.core_tree.get_mut(node_id)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn set_prev_siblings_next_sibling(
|
|
||||||
&mut self,
|
|
||||||
current_id: NodeId,
|
|
||||||
next_sibling: Option<NodeId>,
|
|
||||||
) {
|
|
||||||
if let Some(prev_sibling_id) = self.get_node_prev_sibling_id(current_id) {
|
|
||||||
self.set_next_sibling(prev_sibling_id, next_sibling);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn set_next_siblings_prev_sibling(
|
|
||||||
&mut self,
|
|
||||||
current_id: NodeId,
|
|
||||||
prev_sibling: Option<NodeId>,
|
|
||||||
) {
|
|
||||||
if let Some(next_sibling_id) = self.get_node_next_sibling_id(current_id) {
|
|
||||||
self.set_prev_sibling(next_sibling_id, prev_sibling);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn set_parent(&mut self, node_id: NodeId, parent_id: Option<NodeId>) {
|
|
||||||
if let Some(node) = self.get_node_mut(node_id) {
|
|
||||||
node.relatives.parent = parent_id;
|
|
||||||
} else {
|
|
||||||
unreachable!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn set_prev_sibling(&mut self, node_id: NodeId, prev_sibling: Option<NodeId>) {
|
|
||||||
if let Some(node) = self.get_node_mut(node_id) {
|
|
||||||
node.relatives.prev_sibling = prev_sibling;
|
|
||||||
} else {
|
|
||||||
unreachable!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn set_next_sibling(&mut self, node_id: NodeId, next_sibling: Option<NodeId>) {
|
|
||||||
if let Some(node) = self.get_node_mut(node_id) {
|
|
||||||
node.relatives.next_sibling = next_sibling;
|
|
||||||
} else {
|
|
||||||
unreachable!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn set_first_child(&mut self, node_id: NodeId, first_child: Option<NodeId>) {
|
|
||||||
if let Some(node) = self.get_node_mut(node_id) {
|
|
||||||
node.relatives.first_child = first_child;
|
|
||||||
} else {
|
|
||||||
unreachable!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn set_last_child(&mut self, node_id: NodeId, last_child: Option<NodeId>) {
|
|
||||||
if let Some(node) = self.get_node_mut(node_id) {
|
|
||||||
node.relatives.last_child = last_child;
|
|
||||||
} else {
|
|
||||||
unreachable!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn get_node_prev_sibling_id(&self, node_id: NodeId) -> Option<NodeId> {
|
|
||||||
if let Some(node) = self.get_node(node_id) {
|
|
||||||
node.relatives.prev_sibling
|
|
||||||
} else {
|
|
||||||
unreachable!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn get_node_next_sibling_id(&self, node_id: NodeId) -> Option<NodeId> {
|
|
||||||
if let Some(node) = self.get_node(node_id) {
|
|
||||||
node.relatives.next_sibling
|
|
||||||
} else {
|
|
||||||
unreachable!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn get_node_relatives(&self, node_id: NodeId) -> Relatives {
|
|
||||||
if let Some(node) = self.get_node(node_id) {
|
|
||||||
node.relatives
|
|
||||||
} else {
|
|
||||||
unreachable!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn drop_children(&mut self, node_id: NodeId) {
|
|
||||||
let sub_tree_ids: Vec<NodeId> = self
|
|
||||||
.get(node_id)
|
|
||||||
.expect("node must exist")
|
|
||||||
.traverse_level_order()
|
|
||||||
.skip(1) // skip the "root" of the sub-tree, which is the "current" node
|
|
||||||
.map(|node_ref| node_ref.node_id())
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
for id in sub_tree_ids {
|
|
||||||
self.core_tree.remove(id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn orphan_children(&mut self, node_id: NodeId) {
|
|
||||||
let child_ids: Vec<NodeId> = self
|
|
||||||
.get(node_id)
|
|
||||||
.expect("node must exist")
|
|
||||||
.children()
|
|
||||||
.map(|node_ref| node_ref.node_id())
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
for id in child_ids {
|
|
||||||
self.set_parent(id, None);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn new_node_ref(&self, node_id: NodeId) -> NodeRef<T> {
|
|
||||||
NodeRef::new(node_id, self)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn new_node_mut(&mut self, node_id: NodeId) -> NodeMut<T> {
|
|
||||||
NodeMut::new(node_id, self)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_node_first_last_child(&self, node_id: NodeId) -> (bool, bool) {
|
|
||||||
if let Some(node) = self.get_node(node_id) {
|
|
||||||
node.relatives
|
|
||||||
.parent
|
|
||||||
.and_then(|parent_id| self.get_node(parent_id))
|
|
||||||
.map(|parent| {
|
|
||||||
let Relatives {
|
|
||||||
first_child: first,
|
|
||||||
last_child: last,
|
|
||||||
..
|
|
||||||
} = parent.relatives;
|
|
||||||
(
|
|
||||||
first.map(|child_id| child_id == node_id).unwrap_or(false),
|
|
||||||
last.map(|child_id| child_id == node_id).unwrap_or(false),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.unwrap_or((false, false))
|
|
||||||
} else {
|
|
||||||
(false, false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Default for Tree<T> {
|
|
||||||
fn default() -> Self {
|
|
||||||
TreeBuilder::new().build()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: std::fmt::Debug> Tree<T> {
|
|
||||||
/// Write formatted tree representation and nodes with debug formatting.
|
|
||||||
///
|
|
||||||
/// Example:
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use slab_tree::tree::TreeBuilder;
|
|
||||||
///
|
|
||||||
/// let mut tree = TreeBuilder::new().with_root(0).build();
|
|
||||||
/// let mut root = tree.root_mut().unwrap();
|
|
||||||
/// root.append(1)
|
|
||||||
/// .append(2);
|
|
||||||
/// root.append(3);
|
|
||||||
/// let mut s = String::new();
|
|
||||||
/// tree.write_formatted(&mut s).unwrap();
|
|
||||||
/// assert_eq!(&s, "\
|
|
||||||
/// 0
|
|
||||||
/// ├── 1
|
|
||||||
/// │ └── 2
|
|
||||||
/// └── 3
|
|
||||||
/// ");
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// Writes nothing if the tree is empty.
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use slab_tree::tree::TreeBuilder;
|
|
||||||
///
|
|
||||||
/// let tree = TreeBuilder::<i32>::new().build();
|
|
||||||
/// let mut s = String::new();
|
|
||||||
/// tree.write_formatted(&mut s).unwrap();
|
|
||||||
/// assert_eq!(&s, "");
|
|
||||||
/// ```
|
|
||||||
pub fn write_formatted<W: std::fmt::Write>(&self, w: &mut W) -> std::fmt::Result {
|
|
||||||
if let Some(root) = self.root() {
|
|
||||||
let node_id = root.node_id();
|
|
||||||
let childn = 0;
|
|
||||||
let level = 0;
|
|
||||||
let last = vec![];
|
|
||||||
let mut stack = vec![(node_id, childn, level, last)];
|
|
||||||
while let Some((node_id, childn, level, last)) = stack.pop() {
|
|
||||||
debug_assert_eq!(
|
|
||||||
last.len(),
|
|
||||||
level,
|
|
||||||
"each previous level should indicate whether it has reached the last node"
|
|
||||||
);
|
|
||||||
let node = self
|
|
||||||
.get(node_id)
|
|
||||||
.expect("getting node of existing node ref id");
|
|
||||||
if childn == 0 {
|
|
||||||
for i in 1..level {
|
|
||||||
if last[i - 1] {
|
|
||||||
write!(w, " ")?;
|
|
||||||
} else {
|
|
||||||
write!(w, "│ ")?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if level > 0 {
|
|
||||||
if last[level - 1] {
|
|
||||||
write!(w, "└── ")?;
|
|
||||||
} else {
|
|
||||||
write!(w, "├── ")?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
writeln!(w, "{:?}", node.data())?;
|
|
||||||
}
|
|
||||||
let mut children = node.children().skip(childn);
|
|
||||||
if let Some(child) = children.next() {
|
|
||||||
let mut next_last = last.clone();
|
|
||||||
if children.next().is_some() {
|
|
||||||
stack.push((node_id, childn + 1, level, last));
|
|
||||||
next_last.push(false);
|
|
||||||
} else {
|
|
||||||
next_last.push(true);
|
|
||||||
}
|
|
||||||
stack.push((child.node_id(), 0, level + 1, next_last));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg_attr(tarpaulin, skip)]
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tree_tests {
|
|
||||||
use super::*;
|
|
||||||
use crate::behaviors::RemoveBehavior::{DropChildren, OrphanChildren};
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn capacity() {
|
|
||||||
let tree = TreeBuilder::new().with_root(1).with_capacity(5).build();
|
|
||||||
assert_eq!(tree.capacity(), 5);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn root_id() {
|
|
||||||
let tree = TreeBuilder::new().with_root(1).build();
|
|
||||||
let root_id = tree.root_id().expect("root doesn't exist?");
|
|
||||||
let root = tree.get(root_id).unwrap();
|
|
||||||
assert_eq!(root.data(), &1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn remove_root_drop() {
|
|
||||||
let mut tree = TreeBuilder::new().with_root(1).build();
|
|
||||||
let root_id = tree.root_id().expect("root doesn't exist?");
|
|
||||||
|
|
||||||
tree.remove(root_id, RemoveBehavior::DropChildren);
|
|
||||||
assert!(tree.root().is_none());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn remove_root_orphan() {
|
|
||||||
let mut tree = TreeBuilder::new().with_root(1).build();
|
|
||||||
let root_id = tree.root_id().expect("root doesn't exist?");
|
|
||||||
|
|
||||||
tree.remove(root_id, RemoveBehavior::OrphanChildren);
|
|
||||||
assert!(tree.root().is_none());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn root() {
|
|
||||||
let tree = TreeBuilder::new().with_root(1).build();
|
|
||||||
let root = tree.root().expect("root doesn't exist?");
|
|
||||||
assert_eq!(root.data(), &1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn root_mut() {
|
|
||||||
let mut tree = TreeBuilder::new().with_root(1).build();
|
|
||||||
let mut root = tree.root_mut().expect("root doesn't exist?");
|
|
||||||
|
|
||||||
assert_eq!(root.data(), &mut 1);
|
|
||||||
|
|
||||||
*root.data() = 2;
|
|
||||||
assert_eq!(root.data(), &mut 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn get() {
|
|
||||||
let tree = TreeBuilder::new().with_root(1).build();
|
|
||||||
|
|
||||||
let root_id = tree.root_id().expect("root doesn't exist?");
|
|
||||||
let root = tree.get(root_id);
|
|
||||||
assert!(root.is_some());
|
|
||||||
|
|
||||||
let root = root.unwrap();
|
|
||||||
assert_eq!(root.data(), &1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn get_mut() {
|
|
||||||
let mut tree = TreeBuilder::new().with_root(1).build();
|
|
||||||
|
|
||||||
let root_id = tree.root_id().expect("root doesn't exist?");
|
|
||||||
let root = tree.get_mut(root_id);
|
|
||||||
assert!(root.is_some());
|
|
||||||
|
|
||||||
let mut root = root.unwrap();
|
|
||||||
assert_eq!(root.data(), &mut 1);
|
|
||||||
|
|
||||||
*root.data() = 2;
|
|
||||||
assert_eq!(root.data(), &mut 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn get_node() {
|
|
||||||
let tree = TreeBuilder::new().with_root(1).build();
|
|
||||||
|
|
||||||
let root_id = tree.root_id().expect("root doesn't exist?");
|
|
||||||
let root = tree.get_node(root_id);
|
|
||||||
assert!(root.is_some());
|
|
||||||
|
|
||||||
let root = root.unwrap();
|
|
||||||
assert_eq!(root.data, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn get_node_mut() {
|
|
||||||
let mut tree = TreeBuilder::new().with_root(1).build();
|
|
||||||
|
|
||||||
let root_id = tree.root_id().expect("root doesn't exist?");
|
|
||||||
let root = tree.get_node_mut(root_id);
|
|
||||||
assert!(root.is_some());
|
|
||||||
|
|
||||||
let root = root.unwrap();
|
|
||||||
assert_eq!(root.data, 1);
|
|
||||||
|
|
||||||
root.data = 2;
|
|
||||||
assert_eq!(root.data, 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn remove_drop() {
|
|
||||||
let mut tree = TreeBuilder::new().with_root(1).build();
|
|
||||||
|
|
||||||
let two_id;
|
|
||||||
let three_id;
|
|
||||||
let four_id;
|
|
||||||
let five_id;
|
|
||||||
{
|
|
||||||
let mut root = tree.root_mut().expect("root doesn't exist?");
|
|
||||||
two_id = root.append(2).node_id();
|
|
||||||
three_id = root.append(3).node_id();
|
|
||||||
four_id = root.append(4).node_id();
|
|
||||||
}
|
|
||||||
{
|
|
||||||
five_id = tree
|
|
||||||
.get_mut(three_id)
|
|
||||||
.expect("three doesn't exist?")
|
|
||||||
.append(5)
|
|
||||||
.node_id();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 1
|
|
||||||
// / | \
|
|
||||||
// 2 3 4
|
|
||||||
// |
|
|
||||||
// 5
|
|
||||||
|
|
||||||
tree.remove(three_id, DropChildren);
|
|
||||||
|
|
||||||
let root = tree
|
|
||||||
.get_node(tree.root_id().expect("tree doesn't exist?"))
|
|
||||||
.unwrap();
|
|
||||||
assert!(root.relatives.first_child.is_some());
|
|
||||||
assert!(root.relatives.last_child.is_some());
|
|
||||||
assert_eq!(root.relatives.first_child.unwrap(), two_id);
|
|
||||||
assert_eq!(root.relatives.last_child.unwrap(), four_id);
|
|
||||||
|
|
||||||
let two = tree.get_node(two_id);
|
|
||||||
assert!(two.is_some());
|
|
||||||
|
|
||||||
let two = two.unwrap();
|
|
||||||
assert_eq!(two.relatives.next_sibling, Some(four_id));
|
|
||||||
|
|
||||||
let four = tree.get_node(four_id);
|
|
||||||
assert!(four.is_some());
|
|
||||||
|
|
||||||
let four = four.unwrap();
|
|
||||||
assert_eq!(four.relatives.prev_sibling, Some(two_id));
|
|
||||||
|
|
||||||
let five = tree.get_node(five_id);
|
|
||||||
assert!(five.is_none());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Test that there is no panic if caller tries to remove a removed node
|
|
||||||
#[test]
|
|
||||||
fn address_dropped() {
|
|
||||||
let mut tree = TreeBuilder::new().with_root(1).build();
|
|
||||||
let two_id = tree.root_mut().expect("root doesn't exist").node_id();
|
|
||||||
tree.remove(two_id, DropChildren);
|
|
||||||
tree.remove(two_id, DropChildren);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn remove_orphan() {
|
|
||||||
let mut tree = TreeBuilder::new().with_root(1).build();
|
|
||||||
|
|
||||||
let two_id;
|
|
||||||
let three_id;
|
|
||||||
let four_id;
|
|
||||||
let five_id;
|
|
||||||
{
|
|
||||||
let mut root = tree.root_mut().expect("root doesn't exist?");
|
|
||||||
two_id = root.append(2).node_id();
|
|
||||||
three_id = root.append(3).node_id();
|
|
||||||
four_id = root.append(4).node_id();
|
|
||||||
}
|
|
||||||
{
|
|
||||||
five_id = tree
|
|
||||||
.get_mut(three_id)
|
|
||||||
.expect("three doesn't exist?")
|
|
||||||
.append(5)
|
|
||||||
.node_id();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 1
|
|
||||||
// / | \
|
|
||||||
// 2 3 4
|
|
||||||
// |
|
|
||||||
// 5
|
|
||||||
|
|
||||||
tree.remove(three_id, OrphanChildren);
|
|
||||||
|
|
||||||
let root = tree
|
|
||||||
.get_node(tree.root_id().expect("tree doesn't exist?"))
|
|
||||||
.unwrap();
|
|
||||||
assert!(root.relatives.first_child.is_some());
|
|
||||||
assert!(root.relatives.last_child.is_some());
|
|
||||||
assert_eq!(root.relatives.first_child.unwrap(), two_id);
|
|
||||||
assert_eq!(root.relatives.last_child.unwrap(), four_id);
|
|
||||||
|
|
||||||
let two = tree.get_node(two_id);
|
|
||||||
assert!(two.is_some());
|
|
||||||
|
|
||||||
let two = two.unwrap();
|
|
||||||
assert_eq!(two.relatives.next_sibling, Some(four_id));
|
|
||||||
|
|
||||||
let four = tree.get_node(four_id);
|
|
||||||
assert!(four.is_some());
|
|
||||||
|
|
||||||
let four = four.unwrap();
|
|
||||||
assert_eq!(four.relatives.prev_sibling, Some(two_id));
|
|
||||||
|
|
||||||
let five = tree.get_node(five_id);
|
|
||||||
assert!(five.is_some());
|
|
||||||
|
|
||||||
let five = five.unwrap();
|
|
||||||
assert_eq!(five.relatives.parent, None);
|
|
||||||
}
|
|
||||||
}
|
|
@ -2,7 +2,6 @@ use std::{fs, io};
|
|||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
use ratatui::style::Color;
|
use ratatui::style::Color;
|
||||||
|
|
||||||
use slab_tree::NodeId;
|
use slab_tree::NodeId;
|
||||||
|
|
||||||
use crate::component::dialog::input::InputFieldDialogLayer;
|
use crate::component::dialog::input::InputFieldDialogLayer;
|
||||||
@ -12,6 +11,7 @@ use crate::component::filesystem::FsLayer;
|
|||||||
use crate::file::FileKind;
|
use crate::file::FileKind;
|
||||||
use crate::state::action::{Action, ActionResult};
|
use crate::state::action::{Action, ActionResult};
|
||||||
use crate::state::Environment;
|
use crate::state::Environment;
|
||||||
|
use crate::util::slab_tree::NodeRefExtensions;
|
||||||
|
|
||||||
trait CreateEntry {
|
trait CreateEntry {
|
||||||
fn title() -> &'static str;
|
fn title() -> &'static str;
|
||||||
|
@ -13,6 +13,7 @@ use crate::component::filesystem::tree::FsTreeViewNode;
|
|||||||
use crate::state::action::{Action, ActionResult};
|
use crate::state::action::{Action, ActionResult};
|
||||||
use crate::state::Environment;
|
use crate::state::Environment;
|
||||||
use crate::state::event::EventResult;
|
use crate::state::event::EventResult;
|
||||||
|
use crate::util::slab_tree::NodeRefExtensions;
|
||||||
|
|
||||||
pub struct EditSelectedEntry;
|
pub struct EditSelectedEntry;
|
||||||
|
|
||||||
|
@ -3,7 +3,6 @@ use std::{fs, io};
|
|||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use ratatui::style::Color;
|
use ratatui::style::Color;
|
||||||
|
|
||||||
use slab_tree::NodeRef;
|
use slab_tree::NodeRef;
|
||||||
|
|
||||||
use crate::component::dialog::input::InputFieldDialogLayer;
|
use crate::component::dialog::input::InputFieldDialogLayer;
|
||||||
@ -14,6 +13,7 @@ use crate::component::filesystem::tree::FsTreeViewNode;
|
|||||||
use crate::file::FileEntry;
|
use crate::file::FileEntry;
|
||||||
use crate::state::action::{Action, ActionResult};
|
use crate::state::action::{Action, ActionResult};
|
||||||
use crate::state::Environment;
|
use crate::state::Environment;
|
||||||
|
use crate::util::slab_tree::NodeRefExtensions;
|
||||||
|
|
||||||
pub struct RenameSelectedEntry {
|
pub struct RenameSelectedEntry {
|
||||||
pub prefill: bool,
|
pub prefill: bool,
|
||||||
|
@ -2,13 +2,14 @@ use slab_tree::{NodeId, NodeRef};
|
|||||||
|
|
||||||
use crate::component::filesystem::action::movement::{MovementAction, perform_movement_with_count_from_register, SimpleMovementAction};
|
use crate::component::filesystem::action::movement::{MovementAction, perform_movement_with_count_from_register, SimpleMovementAction};
|
||||||
use crate::component::filesystem::FsLayer;
|
use crate::component::filesystem::FsLayer;
|
||||||
use crate::component::filesystem::tree::{FsTree, FsTreeViewNode};
|
use crate::component::filesystem::tree::{FsTree, FsTreeView, FsTreeViewNode};
|
||||||
use crate::state::Environment;
|
use crate::state::Environment;
|
||||||
|
use crate::util::slab_tree::NodeRefExtensions;
|
||||||
|
|
||||||
pub struct MoveToNextSibling;
|
pub struct MoveToNextSibling;
|
||||||
|
|
||||||
impl SimpleMovementAction for MoveToNextSibling {
|
impl SimpleMovementAction for MoveToNextSibling {
|
||||||
fn get_target(selected_node: &NodeRef<FsTreeViewNode>) -> Option<NodeId> where Self: Sized {
|
fn get_target(_view: &FsTreeView, selected_node: &NodeRef<FsTreeViewNode>) -> Option<NodeId> where Self: Sized {
|
||||||
selected_node.next_sibling_id()
|
selected_node.next_sibling_id()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -16,7 +17,7 @@ impl SimpleMovementAction for MoveToNextSibling {
|
|||||||
pub struct MoveToPreviousSibling;
|
pub struct MoveToPreviousSibling;
|
||||||
|
|
||||||
impl SimpleMovementAction for MoveToPreviousSibling {
|
impl SimpleMovementAction for MoveToPreviousSibling {
|
||||||
fn get_target(selected_node: &NodeRef<FsTreeViewNode>) -> Option<NodeId> where Self: Sized {
|
fn get_target(_view: &FsTreeView, selected_node: &NodeRef<FsTreeViewNode>) -> Option<NodeId> where Self: Sized {
|
||||||
selected_node.prev_sibling_id()
|
selected_node.prev_sibling_id()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -24,7 +25,7 @@ impl SimpleMovementAction for MoveToPreviousSibling {
|
|||||||
pub struct MoveBetweenFirstAndLastSibling;
|
pub struct MoveBetweenFirstAndLastSibling;
|
||||||
|
|
||||||
impl SimpleMovementAction for MoveBetweenFirstAndLastSibling {
|
impl SimpleMovementAction for MoveBetweenFirstAndLastSibling {
|
||||||
fn get_target(selected_node: &NodeRef<FsTreeViewNode>) -> Option<NodeId> where Self: Sized {
|
fn get_target(_view: &FsTreeView, selected_node: &NodeRef<FsTreeViewNode>) -> Option<NodeId> where Self: Sized {
|
||||||
if selected_node.next_sibling().is_none() {
|
if selected_node.next_sibling().is_none() {
|
||||||
selected_node.parent().and_then(|node| node.first_child_id())
|
selected_node.parent().and_then(|node| node.first_child_id())
|
||||||
} else {
|
} else {
|
||||||
@ -36,7 +37,7 @@ impl SimpleMovementAction for MoveBetweenFirstAndLastSibling {
|
|||||||
pub struct MoveToParent;
|
pub struct MoveToParent;
|
||||||
|
|
||||||
impl SimpleMovementAction for MoveToParent {
|
impl SimpleMovementAction for MoveToParent {
|
||||||
fn get_target(selected_node: &NodeRef<FsTreeViewNode>) -> Option<NodeId> where Self: Sized {
|
fn get_target(_view: &FsTreeView, selected_node: &NodeRef<FsTreeViewNode>) -> Option<NodeId> where Self: Sized {
|
||||||
selected_node.parent_id()
|
selected_node.parent_id()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -51,8 +52,8 @@ impl MovementAction for MoveOrTraverseUpParent {
|
|||||||
|
|
||||||
impl MoveOrTraverseUpParent {
|
impl MoveOrTraverseUpParent {
|
||||||
fn get_target(tree: &mut FsTree, node_id: NodeId) -> Option<NodeId> {
|
fn get_target(tree: &mut FsTree, node_id: NodeId) -> Option<NodeId> {
|
||||||
if let Some(node) = tree.get_view_node(node_id) {
|
if let Some(node) = tree.view.get(node_id) {
|
||||||
let target_node_id = <MoveToParent as SimpleMovementAction>::get_target(&node);
|
let target_node_id = <MoveToParent as SimpleMovementAction>::get_target(&tree.view, &node);
|
||||||
if target_node_id.is_some() {
|
if target_node_id.is_some() {
|
||||||
return target_node_id;
|
return target_node_id;
|
||||||
} else if let Some(target_node_id) = tree.traverse_up_root() {
|
} else if let Some(target_node_id) = tree.traverse_up_root() {
|
||||||
|
@ -2,15 +2,15 @@ use slab_tree::{NodeId, NodeRef};
|
|||||||
|
|
||||||
use crate::component::filesystem::action::movement::{get_simple_movement_target, MovementAction, MoveToParent, perform_movement_with_count_from, SimpleMovementAction};
|
use crate::component::filesystem::action::movement::{get_simple_movement_target, MovementAction, MoveToParent, perform_movement_with_count_from, SimpleMovementAction};
|
||||||
use crate::component::filesystem::FsLayer;
|
use crate::component::filesystem::FsLayer;
|
||||||
use crate::component::filesystem::tree::{FsTree, FsTreeViewNode};
|
use crate::component::filesystem::tree::{FsTree, FsTreeView, FsTreeViewNode};
|
||||||
use crate::state::Environment;
|
use crate::state::Environment;
|
||||||
|
|
||||||
/// Moves up `count` lines (1 line by default).
|
/// Moves up `count` lines (1 line by default).
|
||||||
pub struct MoveUp;
|
pub struct MoveUp;
|
||||||
|
|
||||||
impl SimpleMovementAction for MoveUp {
|
impl SimpleMovementAction for MoveUp {
|
||||||
fn get_target(selected_node: &NodeRef<FsTreeViewNode>) -> Option<NodeId> where Self: Sized {
|
fn get_target(tree: &FsTreeView, selected_node: &NodeRef<FsTreeViewNode>) -> Option<NodeId> where Self: Sized {
|
||||||
selected_node.above_id()
|
tree.get_node_above(selected_node)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -18,8 +18,8 @@ impl SimpleMovementAction for MoveUp {
|
|||||||
pub struct MoveDown;
|
pub struct MoveDown;
|
||||||
|
|
||||||
impl SimpleMovementAction for MoveDown {
|
impl SimpleMovementAction for MoveDown {
|
||||||
fn get_target(selected_node: &NodeRef<FsTreeViewNode>) -> Option<NodeId> {
|
fn get_target(tree: &FsTreeView, selected_node: &NodeRef<FsTreeViewNode>) -> Option<NodeId> {
|
||||||
selected_node.below_id()
|
tree.get_node_below(selected_node)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -36,7 +36,7 @@ impl MoveToFirst {
|
|||||||
fn get_target(tree: &mut FsTree) -> NodeId where Self: Sized {
|
fn get_target(tree: &mut FsTree) -> NodeId where Self: Sized {
|
||||||
let mut target_node_id = tree.selected_view_node_id;
|
let mut target_node_id = tree.selected_view_node_id;
|
||||||
|
|
||||||
while let Some(node_id) = tree.get_view_node(target_node_id).and_then(|node| <MoveToParent as SimpleMovementAction>::get_target(&node)) {
|
while let Some(node_id) = tree.view.get(target_node_id).and_then(|node| <MoveToParent as SimpleMovementAction>::get_target(&tree.view, &node)) {
|
||||||
target_node_id = node_id;
|
target_node_id = node_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,8 +50,8 @@ pub struct MoveToLast;
|
|||||||
impl MovementAction for MoveToLast {
|
impl MovementAction for MoveToLast {
|
||||||
fn get_target(&self, layer: &mut FsLayer, _environment: &Environment) -> Option<NodeId> where Self: Sized {
|
fn get_target(&self, layer: &mut FsLayer, _environment: &Environment) -> Option<NodeId> where Self: Sized {
|
||||||
let first_node_id = MoveToFirst::get_target(&mut layer.tree);
|
let first_node_id = MoveToFirst::get_target(&mut layer.tree);
|
||||||
let last_node_id = layer.tree.get_view_node(first_node_id).map(|node| node.last_descendant_or_self().node_id());
|
let last_node_id = layer.tree.view.get_last_descendant_or_self(first_node_id);
|
||||||
last_node_id
|
Some(last_node_id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use slab_tree::{NodeId, NodeRef};
|
use slab_tree::{NodeId, NodeRef};
|
||||||
|
|
||||||
use crate::component::filesystem::FsLayer;
|
use crate::component::filesystem::FsLayer;
|
||||||
use crate::component::filesystem::tree::{FsTree, FsTreeViewNode};
|
use crate::component::filesystem::tree::{FsTree, FsTreeView, FsTreeViewNode};
|
||||||
use crate::state::action::{Action, ActionResult};
|
use crate::state::action::{Action, ActionResult};
|
||||||
use crate::state::Environment;
|
use crate::state::Environment;
|
||||||
|
|
||||||
@ -56,7 +56,7 @@ fn perform_movement_with_count_from<F>(tree: &mut FsTree, count: Option<usize>,
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub trait SimpleMovementAction {
|
pub trait SimpleMovementAction {
|
||||||
fn get_target(selected_node: &NodeRef<FsTreeViewNode>) -> Option<NodeId> where Self: Sized;
|
fn get_target(view: &FsTreeView, selected_node: &NodeRef<FsTreeViewNode>) -> Option<NodeId> where Self: Sized;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: SimpleMovementAction> MovementAction for T {
|
impl<T: SimpleMovementAction> MovementAction for T {
|
||||||
@ -66,5 +66,5 @@ impl<T: SimpleMovementAction> MovementAction for T {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn get_simple_movement_target<T: SimpleMovementAction>(tree: &mut FsTree, node_id: NodeId) -> Option<NodeId> {
|
fn get_simple_movement_target<T: SimpleMovementAction>(tree: &mut FsTree, node_id: NodeId) -> Option<NodeId> {
|
||||||
tree.get_view_node(node_id).and_then(|node| T::get_target(&node))
|
tree.view.get(node_id).and_then(|node| T::get_target(&tree.view, &node))
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@ use std::marker::PhantomData;
|
|||||||
use slab_tree::{NodeId, NodeRef};
|
use slab_tree::{NodeId, NodeRef};
|
||||||
|
|
||||||
use crate::component::filesystem::action::movement::SimpleMovementAction;
|
use crate::component::filesystem::action::movement::SimpleMovementAction;
|
||||||
use crate::component::filesystem::tree::FsTreeViewNode;
|
use crate::component::filesystem::tree::{FsTreeView, FsTreeViewNode};
|
||||||
|
|
||||||
/// A movement action with fallback.
|
/// A movement action with fallback.
|
||||||
pub struct MovementWithFallback<A: SimpleMovementAction, F: SimpleMovementAction>(PhantomData<A>, PhantomData<F>);
|
pub struct MovementWithFallback<A: SimpleMovementAction, F: SimpleMovementAction>(PhantomData<A>, PhantomData<F>);
|
||||||
@ -21,7 +21,7 @@ impl<A: SimpleMovementAction, F: SimpleMovementAction> MovementWithFallbackFacto
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<A: SimpleMovementAction, F: SimpleMovementAction> SimpleMovementAction for MovementWithFallback<A, F> {
|
impl<A: SimpleMovementAction, F: SimpleMovementAction> SimpleMovementAction for MovementWithFallback<A, F> {
|
||||||
fn get_target(selected_node: &NodeRef<FsTreeViewNode>) -> Option<NodeId> where Self: Sized {
|
fn get_target(view: &FsTreeView, selected_node: &NodeRef<FsTreeViewNode>) -> Option<NodeId> where Self: Sized {
|
||||||
A::get_target(selected_node).or_else(|| F::get_target(selected_node))
|
A::get_target(view, selected_node).or_else(|| F::get_target(view, selected_node))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ use ratatui::widgets::{Clear, Widget};
|
|||||||
use slab_tree::{NodeId, NodeRef};
|
use slab_tree::{NodeId, NodeRef};
|
||||||
|
|
||||||
use crate::component::filesystem::{ColumnWidths, FsLayer};
|
use crate::component::filesystem::{ColumnWidths, FsLayer};
|
||||||
use crate::component::filesystem::tree::{FsTree, FsTreeViewNode};
|
use crate::component::filesystem::tree::{FsTree, FsTreeView, FsTreeViewNode};
|
||||||
use crate::file::{FileEntry, FileKind, FileOwnerNameCache};
|
use crate::file::{FileEntry, FileKind, FileOwnerNameCache};
|
||||||
use crate::state::view::Frame;
|
use crate::state::view::Frame;
|
||||||
|
|
||||||
@ -37,7 +37,7 @@ fn get_or_update_column_widths(layer: &mut FsLayer, cols: u16) -> ColumnWidths {
|
|||||||
let mut user: usize = 0;
|
let mut user: usize = 0;
|
||||||
let mut group: usize = 0;
|
let mut group: usize = 0;
|
||||||
|
|
||||||
for node in layer.tree.view_iter() {
|
for node in &layer.tree.view {
|
||||||
let entry = layer.tree.get_entry(&node).unwrap_or_else(|| FileEntry::dummy_as_ref());
|
let entry = layer.tree.get_entry(&node).unwrap_or_else(|| FileEntry::dummy_as_ref());
|
||||||
|
|
||||||
name = max(name, get_node_level(&node).saturating_add(Span::from(entry.name().str()).width()));
|
name = max(name, get_node_level(&node).saturating_add(Span::from(entry.name().str()).width()));
|
||||||
@ -63,7 +63,7 @@ fn collect_displayed_rows(tree: &FsTree, selected_node_id: NodeId, terminal_rows
|
|||||||
let mut displayed_rows = Vec::with_capacity(terminal_rows);
|
let mut displayed_rows = Vec::with_capacity(terminal_rows);
|
||||||
let mut cursor_y: u16 = 0;
|
let mut cursor_y: u16 = 0;
|
||||||
|
|
||||||
if let Some(middle_node) = tree.selected_node().or_else(|| tree.view_root_node()) {
|
if let Some(middle_node) = tree.selected_node().or_else(|| tree.view.root()) {
|
||||||
let middle_node_id = middle_node.node_id();
|
let middle_node_id = middle_node.node_id();
|
||||||
|
|
||||||
displayed_rows.push(NodeRow::from(&middle_node, tree, middle_node_id == selected_node_id));
|
displayed_rows.push(NodeRow::from(&middle_node, tree, middle_node_id == selected_node_id));
|
||||||
@ -72,7 +72,7 @@ fn collect_displayed_rows(tree: &FsTree, selected_node_id: NodeId, terminal_rows
|
|||||||
let mut cursor_down_id = Some(middle_node_id);
|
let mut cursor_down_id = Some(middle_node_id);
|
||||||
|
|
||||||
while displayed_rows.len() < terminal_rows {
|
while displayed_rows.len() < terminal_rows {
|
||||||
if let Some(next_node_up) = move_cursor(tree, &mut cursor_up_id, |node| node.above_id()) {
|
if let Some(next_node_up) = move_cursor(tree, &mut cursor_up_id, FsTreeView::get_node_above) {
|
||||||
displayed_rows.insert(0, NodeRow::from(&next_node_up, tree, false));
|
displayed_rows.insert(0, NodeRow::from(&next_node_up, tree, false));
|
||||||
cursor_y = cursor_y.saturating_add(1);
|
cursor_y = cursor_y.saturating_add(1);
|
||||||
}
|
}
|
||||||
@ -81,7 +81,7 @@ fn collect_displayed_rows(tree: &FsTree, selected_node_id: NodeId, terminal_rows
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(next_node_down) = move_cursor(tree, &mut cursor_down_id, |node| node.below_id()) {
|
if let Some(next_node_down) = move_cursor(tree, &mut cursor_down_id, FsTreeView::get_node_below) {
|
||||||
displayed_rows.push(NodeRow::from(&next_node_down, tree, false));
|
displayed_rows.push(NodeRow::from(&next_node_down, tree, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,11 +94,12 @@ fn collect_displayed_rows(tree: &FsTree, selected_node_id: NodeId, terminal_rows
|
|||||||
(displayed_rows, cursor_y)
|
(displayed_rows, cursor_y)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn move_cursor<'a, F>(tree: &'a FsTree, cursor: &mut Option<NodeId>, func: F) -> Option<NodeRef<'a, FsTreeViewNode>> where F: FnOnce(NodeRef<FsTreeViewNode>) -> Option<NodeId> {
|
fn move_cursor<'a, F>(tree: &'a FsTree, cursor: &mut Option<NodeId>, func: F) -> Option<NodeRef<'a, FsTreeViewNode>> where F: FnOnce(&FsTreeView, &NodeRef<FsTreeViewNode>) -> Option<NodeId> {
|
||||||
|
let view = &tree.view;
|
||||||
let next_node = cursor
|
let next_node = cursor
|
||||||
.and_then(|id| tree.get_view_node(id))
|
.and_then(|id| view.get(id))
|
||||||
.and_then(func)
|
.and_then(|node| func(view, &node))
|
||||||
.and_then(|id| tree.get_view_node(id));
|
.and_then(|id| view.get(id));
|
||||||
|
|
||||||
*cursor = next_node.as_ref().map(NodeRef::node_id);
|
*cursor = next_node.as_ref().map(NodeRef::node_id);
|
||||||
|
|
||||||
|
@ -2,7 +2,6 @@ use std::path::Path;
|
|||||||
|
|
||||||
use slab_tree::{NodeId, NodeRef};
|
use slab_tree::{NodeId, NodeRef};
|
||||||
|
|
||||||
use crate::component::filesystem::tree::view::FsTreeViewIterator;
|
|
||||||
use crate::file::FileEntry;
|
use crate::file::FileEntry;
|
||||||
|
|
||||||
pub use self::model::FsTreeModel;
|
pub use self::model::FsTreeModel;
|
||||||
@ -15,7 +14,7 @@ mod view;
|
|||||||
|
|
||||||
pub struct FsTree {
|
pub struct FsTree {
|
||||||
model: FsTreeModel,
|
model: FsTreeModel,
|
||||||
view: FsTreeView,
|
pub view: FsTreeView,
|
||||||
pub selected_view_node_id: NodeId,
|
pub selected_view_node_id: NodeId,
|
||||||
structure_version: u32,
|
structure_version: u32,
|
||||||
}
|
}
|
||||||
@ -45,14 +44,6 @@ impl FsTree {
|
|||||||
return self.view.get(self.selected_view_node_id);
|
return self.view.get(self.selected_view_node_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn view_root_node(&self) -> Option<NodeRef<FsTreeViewNode>> {
|
|
||||||
self.view.root()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn view_iter(&self) -> FsTreeViewIterator {
|
|
||||||
self.view.into_iter()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_view_node(&self, view_node_id: NodeId) -> Option<NodeRef<FsTreeViewNode>> {
|
pub fn get_view_node(&self, view_node_id: NodeId) -> Option<NodeRef<FsTreeViewNode>> {
|
||||||
self.view.get(view_node_id)
|
self.view.get(view_node_id)
|
||||||
}
|
}
|
||||||
@ -114,7 +105,7 @@ impl FsTree {
|
|||||||
let view = &mut self.view;
|
let view = &mut self.view;
|
||||||
|
|
||||||
if self.selected_view_node_id == view_node_id {
|
if self.selected_view_node_id == view_node_id {
|
||||||
self.selected_view_node_id = view.get(view_node_id).and_then(|node| node.below_id().or_else(|| node.above_id())).unwrap_or_else(|| view.root_id());
|
self.selected_view_node_id = view.get_node_below_id(view_node_id).or_else(|| view.get_node_above_id(view_node_id)).unwrap_or_else(|| view.root_id());
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(view_node) = view.remove(view_node_id) {
|
if let Some(view_node) = view.remove(view_node_id) {
|
||||||
|
@ -5,6 +5,7 @@ use slab_tree::NodeId;
|
|||||||
|
|
||||||
use crate::component::filesystem::tree::{FsTreeModel, FsTreeModelNode};
|
use crate::component::filesystem::tree::{FsTreeModel, FsTreeModelNode};
|
||||||
use crate::file::FileEntry;
|
use crate::file::FileEntry;
|
||||||
|
use crate::util::slab_tree::{NodeMutExtensions, NodeRefExtensions};
|
||||||
|
|
||||||
impl FsTreeModel {
|
impl FsTreeModel {
|
||||||
pub fn resolve_children(&mut self, node_id: NodeId) -> Option<Vec<NodeId>> {
|
pub fn resolve_children(&mut self, node_id: NodeId) -> Option<Vec<NodeId>> {
|
||||||
|
48
src/component/filesystem/tree/view/above_below.rs
Normal file
48
src/component/filesystem/tree/view/above_below.rs
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
use slab_tree::{NodeId, NodeRef};
|
||||||
|
|
||||||
|
use crate::component::filesystem::tree::{FsTreeView, FsTreeViewNode};
|
||||||
|
use crate::util::slab_tree::NodeRefExtensions;
|
||||||
|
|
||||||
|
impl FsTreeView {
|
||||||
|
pub fn get_node_above(&self, node: &NodeRef<FsTreeViewNode>) -> Option<NodeId> {
|
||||||
|
if let Some(prev_sibling_id) = node.prev_sibling_id() {
|
||||||
|
Some(self.get_last_descendant_or_self(prev_sibling_id))
|
||||||
|
} else {
|
||||||
|
node.parent_id()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::unused_self)]
|
||||||
|
pub fn get_node_below(&self, node: &NodeRef<FsTreeViewNode>) -> Option<NodeId> {
|
||||||
|
if let Some(next_id) = node.first_child_id() {
|
||||||
|
Some(next_id)
|
||||||
|
} else if let Some(next_id) = node.next_sibling_id() {
|
||||||
|
Some(next_id)
|
||||||
|
} else {
|
||||||
|
for ancestor in node.ancestors() {
|
||||||
|
if let Some(next_id) = ancestor.next_sibling_id() {
|
||||||
|
return Some(next_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_node_above_id(&self, node_id: NodeId) -> Option<NodeId> {
|
||||||
|
self.get(node_id).and_then(|node| self.get_node_above(&node))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_node_below_id(&self, node_id: NodeId) -> Option<NodeId> {
|
||||||
|
self.get(node_id).and_then(|node| self.get_node_below(&node))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_last_descendant_or_self(&self, id: NodeId) -> NodeId {
|
||||||
|
let mut id = id;
|
||||||
|
|
||||||
|
while let Some(node_id) = self.get(id).and_then(|node| node.last_child_id()) {
|
||||||
|
id = node_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
@ -2,8 +2,7 @@ use slab_tree::{NodeId, NodeMut, NodeRef, RemoveBehavior, Tree};
|
|||||||
|
|
||||||
use crate::component::filesystem::tree::FsTreeModel;
|
use crate::component::filesystem::tree::FsTreeModel;
|
||||||
|
|
||||||
pub use self::iterator::FsTreeViewIterator;
|
mod above_below;
|
||||||
|
|
||||||
mod expand_collapse;
|
mod expand_collapse;
|
||||||
mod iterator;
|
mod iterator;
|
||||||
mod refresh;
|
mod refresh;
|
||||||
|
@ -4,13 +4,13 @@ use crate::component::filesystem::tree::{FsTreeModel, FsTreeView, FsTreeViewNode
|
|||||||
|
|
||||||
impl FsTreeView {
|
impl FsTreeView {
|
||||||
pub fn traverse_up_root(&mut self, model: &mut FsTreeModel) -> Option<NodeId> {
|
pub fn traverse_up_root(&mut self, model: &mut FsTreeModel) -> Option<NodeId> {
|
||||||
let old_model_root_id = model.root_id();
|
let old_moodel_root_id = model.root_id();
|
||||||
|
|
||||||
if let Some(new_model_root_id) = model.traverse_up_root() {
|
if let Some(new_model_root_id) = model.traverse_up_root() {
|
||||||
self.set_root(new_model_root_id);
|
self.set_root(new_model_root_id);
|
||||||
|
|
||||||
if let Some(mut new_view_root) = self.get_mut(self.root_id) {
|
if let Some(mut new_view_root) = self.get_mut(self.root_id) {
|
||||||
Self::resolve_new_root_children(&mut new_view_root, model, old_model_root_id, new_model_root_id);
|
Self::resolve_new_root_children(&mut new_view_root, model, old_moodel_root_id, new_model_root_id);
|
||||||
Some(self.root_id)
|
Some(self.root_id)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
pub use integer_length::int_len;
|
pub use integer_length::int_len;
|
||||||
|
|
||||||
mod integer_length;
|
mod integer_length;
|
||||||
|
pub mod slab_tree;
|
||||||
|
61
src/util/slab_tree.rs
Normal file
61
src/util/slab_tree.rs
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
use slab_tree::{NodeId, NodeMut, NodeRef};
|
||||||
|
|
||||||
|
pub trait NodeRefExtensions {
|
||||||
|
fn parent_id(&self) -> Option<NodeId>;
|
||||||
|
fn first_child_id(&self) -> Option<NodeId>;
|
||||||
|
fn last_child_id(&self) -> Option<NodeId>;
|
||||||
|
fn prev_sibling_id(&self) -> Option<NodeId>;
|
||||||
|
fn next_sibling_id(&self) -> Option<NodeId>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait NodeMutExtensions {
|
||||||
|
fn parent_id(&mut self) -> Option<NodeId>;
|
||||||
|
fn first_child_id(&mut self) -> Option<NodeId>;
|
||||||
|
fn last_child_id(&mut self) -> Option<NodeId>;
|
||||||
|
fn prev_sibling_id(&mut self) -> Option<NodeId>;
|
||||||
|
fn next_sibling_id(&mut self) -> Option<NodeId>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T> NodeRefExtensions for NodeRef<'a, T> {
|
||||||
|
fn parent_id(&self) -> Option<NodeId> {
|
||||||
|
self.parent().map(|node| node.node_id())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn first_child_id(&self) -> Option<NodeId> {
|
||||||
|
self.first_child().map(|node| node.node_id())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn last_child_id(&self) -> Option<NodeId> {
|
||||||
|
self.last_child().map(|node| node.node_id())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn prev_sibling_id(&self) -> Option<NodeId> {
|
||||||
|
self.prev_sibling().map(|node| node.node_id())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next_sibling_id(&self) -> Option<NodeId> {
|
||||||
|
self.next_sibling().map(|node| node.node_id())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T> NodeMutExtensions for NodeMut<'a, T> {
|
||||||
|
fn parent_id(&mut self) -> Option<NodeId> {
|
||||||
|
self.parent().map(|node| node.node_id())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn first_child_id(&mut self) -> Option<NodeId> {
|
||||||
|
self.first_child().map(|node| node.node_id())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn last_child_id(&mut self) -> Option<NodeId> {
|
||||||
|
self.last_child().map(|node| node.node_id())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn prev_sibling_id(&mut self) -> Option<NodeId> {
|
||||||
|
self.prev_sibling().map(|node| node.node_id())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next_sibling_id(&mut self) -> Option<NodeId> {
|
||||||
|
self.next_sibling().map(|node| node.node_id())
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user