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)]
|
||||
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