tui_tree_widget/
flatten.rs

1use std::collections::HashSet;
2
3use crate::tree_item::TreeItem;
4
5/// A flattened item of all visible [`TreeItem`]s.
6///
7/// Generated via [`TreeState::flatten`](crate::TreeState::flatten).
8#[must_use]
9pub struct Flattened<'text, Identifier> {
10    pub identifier: Vec<Identifier>,
11    pub item: &'text TreeItem<'text, Identifier>,
12}
13
14impl<Identifier> Flattened<'_, Identifier> {
15    /// Zero based depth. Depth 0 means top level with 0 indentation.
16    #[must_use]
17    pub fn depth(&self) -> usize {
18        self.identifier.len() - 1
19    }
20}
21
22/// Get a flat list of all visible [`TreeItem`]s.
23///
24/// `current` starts empty: `&[]`
25#[must_use]
26pub fn flatten<'text, Identifier>(
27    open_identifiers: &HashSet<Vec<Identifier>>,
28    items: &'text [TreeItem<'text, Identifier>],
29    current: &[Identifier],
30) -> Vec<Flattened<'text, Identifier>>
31where
32    Identifier: Clone + PartialEq + Eq + core::hash::Hash,
33{
34    let mut result = Vec::new();
35    for item in items {
36        let mut child_identifier = current.to_vec();
37        child_identifier.push(item.identifier.clone());
38
39        let child_result = open_identifiers
40            .contains(&child_identifier)
41            .then(|| flatten(open_identifiers, &item.children, &child_identifier));
42
43        result.push(Flattened {
44            identifier: child_identifier,
45            item,
46        });
47
48        if let Some(mut child_result) = child_result {
49            result.append(&mut child_result);
50        }
51    }
52    result
53}
54
55#[test]
56fn depth_works() {
57    let mut open = HashSet::new();
58    open.insert(vec!["b"]);
59    open.insert(vec!["b", "d"]);
60    let depths = flatten(&open, &TreeItem::example(), &[])
61        .into_iter()
62        .map(|flattened| flattened.depth())
63        .collect::<Vec<_>>();
64    assert_eq!(depths, [0, 0, 1, 1, 2, 2, 1, 0]);
65}
66
67#[cfg(test)]
68fn flatten_works(open: &HashSet<Vec<&'static str>>, expected: &[&str]) {
69    let items = TreeItem::example();
70    let result = flatten(open, &items, &[]);
71    let actual = result
72        .into_iter()
73        .map(|flattened| flattened.identifier.into_iter().next_back().unwrap())
74        .collect::<Vec<_>>();
75    assert_eq!(actual, expected);
76}
77
78#[test]
79fn flatten_nothing_open_is_top_level() {
80    let open = HashSet::new();
81    flatten_works(&open, &["a", "b", "h"]);
82}
83
84#[test]
85fn flatten_wrong_open_is_only_top_level() {
86    let mut open = HashSet::new();
87    open.insert(vec!["a"]);
88    open.insert(vec!["b", "d"]);
89    flatten_works(&open, &["a", "b", "h"]);
90}
91
92#[test]
93fn flatten_one_is_open() {
94    let mut open = HashSet::new();
95    open.insert(vec!["b"]);
96    flatten_works(&open, &["a", "b", "c", "d", "g", "h"]);
97}
98
99#[test]
100fn flatten_all_open() {
101    let mut open = HashSet::new();
102    open.insert(vec!["b"]);
103    open.insert(vec!["b", "d"]);
104    flatten_works(&open, &["a", "b", "c", "d", "e", "f", "g", "h"]);
105}