mecomp_tui/ui/widgets/tree/
item.rs1use std::collections::HashSet;
2
3use ratatui::text::Text;
4
5#[derive(Debug, Clone)]
24#[allow(clippy::module_name_repetitions)]
25pub struct CheckTreeItem<'text, Identifier> {
26 pub(super) identifier: Identifier,
27 pub(super) text: Text<'text>,
28 pub(super) children: Vec<Self>,
29}
30
31impl<'text, Identifier> CheckTreeItem<'text, Identifier>
32where
33 Identifier: Clone + PartialEq + Eq + core::hash::Hash,
34{
35 #[must_use]
37 pub fn new_leaf<T>(identifier: Identifier, text: T) -> Self
38 where
39 T: Into<Text<'text>>,
40 {
41 Self {
42 identifier,
43 text: text.into(),
44 children: Vec::new(),
45 }
46 }
47
48 pub fn new<T>(identifier: Identifier, text: T, children: Vec<Self>) -> std::io::Result<Self>
54 where
55 T: Into<Text<'text>>,
56 {
57 let identifiers = children
58 .iter()
59 .map(|item| &item.identifier)
60 .collect::<HashSet<_>>();
61 if identifiers.len() != children.len() {
62 return Err(std::io::Error::new(
63 std::io::ErrorKind::AlreadyExists,
64 "The children contain duplicate identifiers",
65 ));
66 }
67
68 Ok(Self {
69 identifier,
70 text: text.into(),
71 children,
72 })
73 }
74
75 #[must_use]
77 pub const fn identifier(&self) -> &Identifier {
78 &self.identifier
79 }
80
81 #[must_use]
82 #[allow(clippy::missing_const_for_fn)] pub fn children(&self) -> &[Self] {
84 &self.children
85 }
86
87 #[must_use]
89 pub fn child(&self, index: usize) -> Option<&Self> {
90 self.children.get(index)
91 }
92
93 #[must_use]
97 pub fn child_mut(&mut self, index: usize) -> Option<&mut Self> {
98 self.children.get_mut(index)
99 }
100
101 #[must_use]
102 pub fn height(&self) -> usize {
103 self.text.height()
104 }
105
106 pub fn add_child(&mut self, child: Self) -> std::io::Result<()> {
112 let existing = self
113 .children
114 .iter()
115 .map(|item| &item.identifier)
116 .collect::<HashSet<_>>();
117 if existing.contains(&child.identifier) {
118 return Err(std::io::Error::new(
119 std::io::ErrorKind::AlreadyExists,
120 "identifier already exists in the children",
121 ));
122 }
123
124 self.children.push(child);
125 Ok(())
126 }
127}
128
129#[cfg(test)]
130impl CheckTreeItem<'static, &'static str> {
131 #[must_use]
132 pub(crate) fn example() -> Vec<Self> {
133 vec![
134 Self::new_leaf("a", "Alfa"),
135 Self::new(
136 "b",
137 "Bravo",
138 vec![
139 Self::new_leaf("c", "Charlie"),
140 Self::new(
141 "d",
142 "Delta",
143 vec![Self::new_leaf("e", "Echo"), Self::new_leaf("f", "Foxtrot")],
144 )
145 .expect("all item identifiers are unique"),
146 Self::new_leaf("g", "Golf"),
147 ],
148 )
149 .expect("all item identifiers are unique"),
150 Self::new_leaf("h", "Hotel"),
151 ]
152 }
153}
154
155#[cfg(test)]
156mod tests {
157 use super::*;
158
159 #[test]
160 #[should_panic = "duplicate identifiers"]
161 fn tree_item_new_errors_with_duplicate_identifiers() {
162 let item = CheckTreeItem::new_leaf("same", "text");
163 let another = item.clone();
164 CheckTreeItem::new("root", "Root", vec![item, another]).unwrap();
165 }
166
167 #[test]
168 #[should_panic = "identifier already exists"]
169 fn tree_item_add_child_errors_with_duplicate_identifiers() {
170 let item = CheckTreeItem::new_leaf("same", "text");
171 let another = item.clone();
172 let mut root = CheckTreeItem::new("root", "Root", vec![item]).unwrap();
173 root.add_child(another).unwrap();
174 }
175}