managarr_tree_widget/
flatten.rs

1use crate::tree_item::TreeItem;
2use ratatui::text::ToText;
3use std::collections::HashSet;
4use std::fmt::Display;
5use std::hash::Hash;
6
7/// A flattened item of all visible [`TreeItem`]s.
8///
9/// Generated via [`TreeState::flatten`](crate::TreeState::flatten).
10#[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    /// Zero based depth. Depth 0 means top level with 0 indentation.
24    #[must_use]
25    pub fn depth(&self) -> usize {
26        self.identifier.len() - 1
27    }
28}
29
30/// Get a flat list of all visible [`TreeItem`]s.
31///
32/// `current` starts empty: `&[]`
33#[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}