managarr_tree_widget/
flatten.rs1use crate::tree_item::TreeItem;
2use ratatui::text::ToText;
3use std::collections::HashSet;
4use std::fmt::Display;
5use std::hash::Hash;
6
7#[must_use]
11pub struct Flattened<'a, T>
12where
13 T: ToText + Clone + Default + Display + Hash + PartialEq + Eq,
14{
15 pub identifier: Vec<u64>,
16 pub item: &'a TreeItem<T>,
17}
18
19impl<'a, T> Flattened<'a, T>
20where
21 T: ToText + Clone + Default + Display + Hash + PartialEq + Eq,
22{
23 #[must_use]
25 pub fn depth(&self) -> usize {
26 self.identifier.len() - 1
27 }
28}
29
30#[must_use]
34pub fn flatten<'a, T>(
35 open_identifiers: &HashSet<Vec<u64>>,
36 items: &'a [TreeItem<T>],
37 current: &[u64],
38) -> Vec<Flattened<'a, T>>
39where
40 T: ToText + Clone + Default + Display + Hash + PartialEq + Eq,
41{
42 let mut result = Vec::new();
43 for item in items {
44 let mut child_identifier = current.to_vec();
45 child_identifier.push(item.identifier);
46
47 let child_result = open_identifiers
48 .contains(&child_identifier)
49 .then(|| flatten(open_identifiers, &item.children, &child_identifier));
50
51 result.push(Flattened {
52 identifier: child_identifier,
53 item,
54 });
55
56 if let Some(mut child_result) = child_result {
57 result.append(&mut child_result);
58 }
59 }
60 result
61}
62
63#[test]
64fn depth_works() {
65 use std::hash::{DefaultHasher, Hash, Hasher};
66 let mut open = HashSet::new();
67 let hash = |s: &str| {
68 let mut hasher = DefaultHasher::new();
69 s.hash(&mut hasher);
70 hasher.finish()
71 };
72 open.insert(vec![hash("Bravo")]);
73 open.insert(vec![hash("Bravo"), hash("Delta")]);
74 let depths = flatten(&open, &TreeItem::example(), &[])
75 .into_iter()
76 .map(|flattened| flattened.depth())
77 .collect::<Vec<_>>();
78 assert_eq!(depths, [0, 0, 1, 1, 2, 2, 1, 0]);
79}
80
81#[cfg(test)]
82fn flatten_works(open: &HashSet<Vec<u64>>, expected: &[u64]) {
83 let items = TreeItem::example();
84 let result = flatten(open, &items, &[]);
85 let actual = result
86 .into_iter()
87 .map(|flattened| flattened.identifier.into_iter().last().unwrap())
88 .collect::<Vec<_>>();
89 assert_eq!(actual, expected);
90}
91
92#[test]
93fn flatten_nothing_open_is_top_level() {
94 use std::hash::{DefaultHasher, Hash, Hasher};
95 let open = HashSet::new();
96 let hash = |s: &str| {
97 let mut hasher = DefaultHasher::new();
98 s.hash(&mut hasher);
99 hasher.finish()
100 };
101 flatten_works(&open, &[hash("Alfa"), hash("Bravo"), hash("Hotel")]);
102}
103
104#[test]
105fn flatten_wrong_open_is_only_top_level() {
106 use std::hash::{DefaultHasher, Hash, Hasher};
107 let mut open = HashSet::new();
108 let hash = |s: &str| {
109 let mut hasher = DefaultHasher::new();
110 s.hash(&mut hasher);
111 hasher.finish()
112 };
113 open.insert(vec![hash("Alfa")]);
114 open.insert(vec![hash("Bravo"), hash("Delta")]);
115 flatten_works(&open, &[hash("Alfa"), hash("Bravo"), hash("Hotel")]);
116}
117
118#[test]
119fn flatten_one_is_open() {
120 use std::hash::{DefaultHasher, Hash, Hasher};
121 let mut open = HashSet::new();
122 let hash = |s: &str| {
123 let mut hasher = DefaultHasher::new();
124 s.hash(&mut hasher);
125 hasher.finish()
126 };
127 open.insert(vec![hash("Bravo")]);
128 flatten_works(
129 &open,
130 &[
131 hash("Alfa"),
132 hash("Bravo"),
133 hash("Charlie"),
134 hash("Delta"),
135 hash("Golf"),
136 hash("Hotel"),
137 ],
138 );
139}
140
141#[test]
142fn flatten_all_open() {
143 use std::hash::{DefaultHasher, Hash, Hasher};
144 let mut open = HashSet::new();
145 let hash = |s: &str| {
146 let mut hasher = DefaultHasher::new();
147 s.hash(&mut hasher);
148 hasher.finish()
149 };
150 open.insert(vec![hash("Bravo")]);
151 open.insert(vec![hash("Bravo"), hash("Delta")]);
152 flatten_works(
153 &open,
154 &[
155 hash("Alfa"),
156 hash("Bravo"),
157 hash("Charlie"),
158 hash("Delta"),
159 hash("Echo"),
160 hash("Foxtrot"),
161 hash("Golf"),
162 hash("Hotel"),
163 ],
164 );
165}