cl-structures: Add a simple potted tree structure, for future use in the module system
This commit is contained in:
parent
a74cd0b8ac
commit
ecde44910f
@ -4,3 +4,4 @@
|
|||||||
#![warn(clippy::all)]
|
#![warn(clippy::all)]
|
||||||
pub mod span;
|
pub mod span;
|
||||||
|
|
||||||
|
pub mod tree;
|
||||||
|
218
cl-structures/src/tree.rs
Normal file
218
cl-structures/src/tree.rs
Normal file
@ -0,0 +1,218 @@
|
|||||||
|
//! An insert-only unordered tree, backed by a [Vec]
|
||||||
|
//!
|
||||||
|
//! # Examples
|
||||||
|
//! ```
|
||||||
|
//! use cl_structures::tree::{Tree, Node};
|
||||||
|
//! // A tree can be created
|
||||||
|
//! let mut tree = Tree::new();
|
||||||
|
//! // Provided with a root node
|
||||||
|
//! let root = tree.root("This is the root node").unwrap();
|
||||||
|
//!
|
||||||
|
//! // Nodes can be accessed by indexing
|
||||||
|
//! assert_eq!(*tree[root].as_ref(), "This is the root node");
|
||||||
|
//! // Nodes' data can be accessed directly by calling `get`/`get_mut`
|
||||||
|
//! assert_eq!(tree.get(root).unwrap(), &"This is the root node")
|
||||||
|
//! ```
|
||||||
|
|
||||||
|
// #![allow(unused)]
|
||||||
|
|
||||||
|
pub use self::tree_ref::Ref;
|
||||||
|
use std::ops::{Index, IndexMut};
|
||||||
|
|
||||||
|
pub mod tree_ref;
|
||||||
|
|
||||||
|
/// An insert-only unordered tree, backed by a [Vec]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Tree<T> {
|
||||||
|
nodes: Vec<Node<T>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Default for Tree<T> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self { nodes: Default::default() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Getters
|
||||||
|
impl<T> Tree<T> {
|
||||||
|
pub fn get(&self, index: Ref<T>) -> Option<&T> {
|
||||||
|
self.get_node(index).map(|node| &node.value)
|
||||||
|
}
|
||||||
|
pub fn get_mut(&mut self, index: Ref<T>) -> Option<&mut T> {
|
||||||
|
self.get_node_mut(index).map(|node| &mut node.value)
|
||||||
|
}
|
||||||
|
pub fn get_node(&self, index: Ref<T>) -> Option<&Node<T>> {
|
||||||
|
self.nodes.get(usize::from(index))
|
||||||
|
}
|
||||||
|
pub fn get_node_mut(&mut self, index: Ref<T>) -> Option<&mut Node<T>> {
|
||||||
|
self.nodes.get_mut(usize::from(index))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tree operations
|
||||||
|
impl<T> Tree<T> {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self { nodes: Default::default() }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new root for the tree.
|
||||||
|
///
|
||||||
|
/// If the tree already has a root, the value will be returned.
|
||||||
|
pub fn root(&mut self, value: T) -> Result<Ref<T>, T> {
|
||||||
|
if self.is_empty() {
|
||||||
|
// Create an index for the new node
|
||||||
|
let node = Ref::new_unchecked(self.nodes.len());
|
||||||
|
// add child to tree
|
||||||
|
self.nodes.push(Node::from(value));
|
||||||
|
Ok(node)
|
||||||
|
} else {
|
||||||
|
Err(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_root(&mut self) -> Option<Ref<T>> {
|
||||||
|
match self.nodes.is_empty() {
|
||||||
|
true => None,
|
||||||
|
false => Some(Ref::new_unchecked(0)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Insert a value into the tree as a child of the parent node
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
/// May panic if the node [Ref] is from a different tree
|
||||||
|
pub fn insert(&mut self, value: T, parent: Ref<T>) -> Ref<T> {
|
||||||
|
let child = Ref::new_unchecked(self.nodes.len());
|
||||||
|
// add child to tree before parent
|
||||||
|
self.nodes.push(Node::with_parent(value, parent));
|
||||||
|
// add child to parent
|
||||||
|
self[parent].children.push(child);
|
||||||
|
|
||||||
|
child
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the depth of a node
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
/// May panic if the node [Ref] is from a different tree
|
||||||
|
pub fn depth(&self, node: Ref<T>) -> usize {
|
||||||
|
match self[node].parent {
|
||||||
|
Some(node) => self.depth(node) + 1,
|
||||||
|
None => 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the number of branches in the tree
|
||||||
|
pub fn branches(&self) -> usize {
|
||||||
|
self.nodes.iter().fold(0, |edges, node| edges + node.len())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Standard data structure functions
|
||||||
|
impl<T> Tree<T> {
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
self.nodes.len()
|
||||||
|
}
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.nodes.is_empty()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Index<Ref<T>> for Tree<T> {
|
||||||
|
type Output = Node<T>;
|
||||||
|
fn index(&self, index: Ref<T>) -> &Self::Output {
|
||||||
|
self.get_node(index).expect("Ref should be inside Tree")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<T> IndexMut<Ref<T>> for Tree<T> {
|
||||||
|
fn index_mut(&mut self, index: Ref<T>) -> &mut Self::Output {
|
||||||
|
self.get_node_mut(index).expect("Ref should be inside Tree")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A node in a [Tree]
|
||||||
|
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
pub struct Node<T> {
|
||||||
|
value: T,
|
||||||
|
/// The parent
|
||||||
|
parent: Option<Ref<T>>,
|
||||||
|
/// The children
|
||||||
|
children: Vec<Ref<T>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Node<T> {
|
||||||
|
pub const fn new(value: T) -> Self {
|
||||||
|
Self { value, parent: None, children: vec![] }
|
||||||
|
}
|
||||||
|
pub const fn with_parent(value: T, parent: Ref<T>) -> Self {
|
||||||
|
Self { value, parent: Some(parent), children: vec![] }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(&self) -> &T {
|
||||||
|
self.as_ref()
|
||||||
|
}
|
||||||
|
pub fn get_mut(&mut self) -> &mut T {
|
||||||
|
self.as_mut()
|
||||||
|
}
|
||||||
|
pub fn swap(&mut self, value: T) -> T {
|
||||||
|
std::mem::replace(&mut self.value, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn children(&self) -> &[Ref<T>] {
|
||||||
|
&self.children
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
self.children.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.children.is_empty()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> AsRef<T> for Node<T> {
|
||||||
|
fn as_ref(&self) -> &T {
|
||||||
|
&self.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<T> AsMut<T> for Node<T> {
|
||||||
|
fn as_mut(&mut self) -> &mut T {
|
||||||
|
&mut self.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> From<T> for Node<T> {
|
||||||
|
#[inline]
|
||||||
|
fn from(value: T) -> Self {
|
||||||
|
Self::new(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
#[allow(unused)]
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn add_children() {
|
||||||
|
let mut tree = Tree::new();
|
||||||
|
let root = tree.root(0).unwrap();
|
||||||
|
let one = tree.insert(1, root);
|
||||||
|
let two = tree.insert(2, root);
|
||||||
|
assert_eq!([one, two].as_slice(), tree[root].children());
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn nest_children() {
|
||||||
|
let mut tree = Tree::new();
|
||||||
|
let root = tree.root(0).unwrap();
|
||||||
|
let one = tree.insert(1, root);
|
||||||
|
let two = tree.insert(2, one);
|
||||||
|
assert_eq!(&[one], tree[root].children());
|
||||||
|
assert_eq!(&[two], tree[one].children());
|
||||||
|
assert_eq!(tree[two].children(), &[]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn compares_equal() {}
|
||||||
|
}
|
70
cl-structures/src/tree/tree_ref.rs
Normal file
70
cl-structures/src/tree/tree_ref.rs
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
//! An element in a [Tree](super::Tree)
|
||||||
|
///
|
||||||
|
/// Contains a niche, and as such, [`Option<TreeRef<T>>`] is free :D
|
||||||
|
use std::{marker::PhantomData, num::NonZeroUsize};
|
||||||
|
|
||||||
|
/// An element of in a [Tree](super::Tree).
|
||||||
|
//? The index of the node is stored as a [NonZeroUsize] for space savings
|
||||||
|
//? Making Refs T-specific helps the user keep track of which Refs belong to which trees.
|
||||||
|
//? This isn't bulletproof, of course, but it'll keep Ref<Foo> from being used on Tree<Bar>
|
||||||
|
pub struct Ref<T: ?Sized>(NonZeroUsize, PhantomData<T>);
|
||||||
|
|
||||||
|
impl<T: ?Sized> Ref<T> {
|
||||||
|
/// Constructs a new [TreeRef] with the given index
|
||||||
|
pub fn new_unchecked(index: usize) -> Self {
|
||||||
|
// Safety: index cannot be zero because we use saturating addition on unsigned type.
|
||||||
|
Self(
|
||||||
|
unsafe { NonZeroUsize::new_unchecked(index.saturating_add(1)) },
|
||||||
|
PhantomData,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ?Sized> From<Ref<T>> for usize {
|
||||||
|
fn from(value: Ref<T>) -> Self {
|
||||||
|
usize::from(value.0) - 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- implementations of derivable traits, because we don't need bounds here --- */
|
||||||
|
|
||||||
|
impl<T: ?Sized> std::fmt::Debug for Ref<T> {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.debug_tuple("TreeRef").field(&self.0).finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ?Sized> std::hash::Hash for Ref<T> {
|
||||||
|
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||||
|
self.0.hash(state);
|
||||||
|
self.1.hash(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ?Sized> PartialEq for Ref<T> {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.0 == other.0 && self.1 == other.1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ?Sized> Eq for Ref<T> {}
|
||||||
|
|
||||||
|
impl<T: ?Sized> PartialOrd for Ref<T> {
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||||
|
Some(self.cmp(other))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ?Sized> Ord for Ref<T> {
|
||||||
|
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||||
|
self.0.cmp(&other.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ?Sized> Clone for Ref<T> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
*self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ?Sized> Copy for Ref<T> {}
|
Loading…
Reference in New Issue
Block a user