fix: Changed the implementation to not require the direct use of identifiers, and to instead generate them using the hashes of the contents of each tree item

This commit is contained in:
2024-11-14 17:35:40 -07:00
parent 8a91f662dd
commit 6c10db760d
6 changed files with 198 additions and 183 deletions
+42 -39
View File
@@ -1,14 +1,15 @@
use std::collections::HashSet;
use std::fmt::Display;
use std::hash::{DefaultHasher, Hash, Hasher};
use ratatui::text::ToText;
/// One item inside a [`Tree`](crate::Tree).
///
/// Can have zero or more `children`.
///
/// # Identifier
/// # identifier
///
/// The generic argument `Identifier` is used to keep the state like the currently selected or opened [`TreeItem`]s in the [`TreeState`](crate::TreeState).
/// The `identifier` is used to keep the state like the currently selected or opened [`TreeItem`]s in the [`TreeState`](crate::TreeState).
///
/// It needs to be unique among its siblings but can be used again on parent or child [`TreeItem`]s.
/// A common example would be a filename which has to be unique in its directory while it can exist in another.
@@ -29,42 +30,30 @@ use ratatui::text::ToText;
///
/// ```
/// # use managarr_tree_widget::TreeItem;
/// let a = TreeItem::new_leaf("l", "Leaf");
/// let b = TreeItem::new("r", "Root", vec![a])?;
/// let a = TreeItem::new_leaf("Leaf");
/// let b = TreeItem::new("Root", vec![a])?;
/// # Ok::<(), std::io::Error>(())
/// ```
#[derive(Debug, Clone)]
pub struct TreeItem<Identifier, T>
pub struct TreeItem<T>
where
Identifier: Clone + PartialEq + Eq + core::hash::Hash,
T: ToText + Clone + Default,
T: ToText + Clone + Default + Display + Hash,
{
pub(super) identifier: Identifier,
pub(super) identifier: u64,
pub(super) content: T,
pub(super) children: Vec<Self>,
}
impl<Identifier, T> TreeItem<Identifier, T>
impl<T> TreeItem<T>
where
Identifier: Clone + PartialEq + Eq + core::hash::Hash,
T: ToText + Clone + Default,
T: ToText + Clone + Default + Display + Hash,
{
/// Create a new `TreeItem` without children.
#[must_use]
pub const fn new_leaf(identifier: Identifier, content: T) -> Self {
Self {
identifier,
content,
children: Vec::new(),
}
}
/// Create a new `TreeItem` with children.
///
/// # Errors
///
/// Errors when there are duplicate identifiers in the children.
pub fn new(identifier: Identifier, content: T, children: Vec<Self>) -> std::io::Result<Self> {
pub fn new(content: T, children: Vec<Self>) -> std::io::Result<Self> {
let identifiers = children
.iter()
.map(|item| &item.identifier)
@@ -76,17 +65,33 @@ where
));
}
let mut hasher = DefaultHasher::new();
content.hash(&mut hasher);
Ok(Self {
identifier,
identifier: hasher.finish(),
content,
children,
})
}
/// Create a new `TreeItem` without children.
#[must_use]
pub fn new_leaf(content: T) -> Self {
let mut hasher = DefaultHasher::new();
content.hash(&mut hasher);
Self {
identifier: hasher.finish(),
content,
children: Vec::new(),
}
}
/// Get a reference to the identifier.
#[must_use]
pub const fn identifier(&self) -> &Identifier {
&self.identifier
pub const fn identifier(&self) -> u64 {
self.identifier
}
/// Get a reference to the text.
@@ -142,31 +147,29 @@ where
}
}
impl TreeItem<&'static str, &'static str> {
impl TreeItem<&'static str> {
#[cfg(test)]
#[must_use]
pub(crate) fn example() -> Vec<Self> {
vec![
Self::new_leaf("a", "Alfa"),
Self::new_leaf("Alfa"),
Self::new(
"b",
"Bravo",
vec![
Self::new_leaf("c", "Charlie"),
Self::new_leaf("Charlie"),
Self::new(
"d",
"Delta",
vec![
Self::new_leaf("e", "Echo"),
Self::new_leaf("f", "Foxtrot"),
Self::new_leaf("Echo"),
Self::new_leaf( "Foxtrot"),
],
)
.expect("all item identifiers are unique"),
Self::new_leaf("g", "Golf"),
Self::new_leaf("Golf"),
],
)
.expect("all item identifiers are unique"),
Self::new_leaf("h", "Hotel"),
Self::new_leaf( "Hotel"),
]
}
}
@@ -174,16 +177,16 @@ impl TreeItem<&'static str, &'static str> {
#[test]
#[should_panic = "duplicate identifiers"]
fn tree_item_new_errors_with_duplicate_identifiers() {
let item = TreeItem::new_leaf("same", "text");
let item = TreeItem::new_leaf( "text");
let another = item.clone();
TreeItem::new("root", "Root", vec![item, another]).unwrap();
TreeItem::new("Root", vec![item, another]).unwrap();
}
#[test]
#[should_panic = "identifier already exists"]
fn tree_item_add_child_errors_with_duplicate_identifiers() {
let item = TreeItem::new_leaf("same", "text");
let item = TreeItem::new_leaf("text");
let another = item.clone();
let mut root = TreeItem::new("root", "Root", vec![item]).unwrap();
let mut root = TreeItem::new( "Root", vec![item]).unwrap();
root.add_child(another).unwrap();
}