summaryrefslogtreecommitdiffstats
path: root/src/interactive/app/tree_view.rs
blob: 6dd3f38907064a8020cc6d02bdd2d737fbf236a9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
use super::{sorted_entries, EntryDataBundle, SortMode};
use crate::interactive::path_of;
use dua::traverse::{EntryData, Traversal, Tree, TreeIndex};
use petgraph::{visit::Bfs, Direction};
use std::path::{Path, PathBuf};

pub struct TreeView<'a> {
    pub traversal: &'a mut Traversal,
    pub glob_tree_root: Option<TreeIndex>,
}

impl TreeView<'_> {
    pub fn tree(&self) -> &Tree {
        &self.traversal.tree
    }

    pub fn tree_mut(&mut self) -> &mut Tree {
        &mut self.traversal.tree
    }

    pub fn fs_parent_of(&self, idx: TreeIndex) -> Option<TreeIndex> {
        self.traversal
            .tree
            .neighbors_directed(idx, petgraph::Incoming)
            .find(|idx| match self.glob_tree_root {
                None => true,
                Some(glob_root) => *idx != glob_root,
            })
    }

    pub fn view_parent_of(&self, idx: TreeIndex) -> Option<TreeIndex> {
        let mut iter = self
            .traversal
            .tree
            .neighbors_directed(idx, petgraph::Incoming);
        match self.glob_tree_root {
            None => iter.next(),
            Some(glob_root) => iter
                .clone()
                .find(|idx| *idx == glob_root)
                .or_else(|| iter.next()),
        }
    }

    pub fn path_of(&self, node_idx: TreeIndex) -> PathBuf {
        path_of(&self.traversal.tree, node_idx, self.glob_tree_root)
    }

    pub fn sorted_entries(&self, view_root: TreeIndex, sorting: SortMode) -> Vec<EntryDataBundle> {
        sorted_entries(
            &self.traversal.tree,
            view_root,
            sorting,
            self.glob_tree_root,
        )
    }

    pub fn current_path(&self, view_root: TreeIndex) -> String {
        current_path(&self.traversal.tree, view_root, self.glob_tree_root)
    }

    pub fn remove_entries(&mut self, index: TreeIndex, remove_index: bool) -> usize {
        let mut entries_deleted = 0;
        let mut bfs = Bfs::new(self.tree(), index);

        while let Some(nx) = bfs.next(&self.tree()) {
            if nx == index && !remove_index {
                continue;
            }
            self.tree_mut().remove_node(nx);
            entries_deleted += 1;
        }
        entries_deleted
    }

    pub fn exists(&self, idx: TreeIndex) -> bool {
        self.tree().node_weight(idx).is_some()
    }

    pub fn total_size(&self) -> u128 {
        self.tree()
            .neighbors_directed(self.traversal.root_index, Direction::Outgoing)
            .filter_map(|idx| self.tree().node_weight(idx).map(|w| w.size))
            .sum()
    }

    pub fn recompute_sizes_recursively(&mut self, mut index: TreeIndex) {
        loop {
            let (size_of_children, item_count) = self
                .tree()
                .neighbors_directed(index, Direction::Outgoing)
                .filter_map(|idx| {
                    self.tree().node_weight(idx).map(|w| (w.size, w.entry_count.unwrap_or(1)))
                })
                .reduce(|a, b| (a.0 + b.0, a.1 + b.1))
                .unwrap_or_default();

            let node = self.traversal
                .tree
                .node_weight_mut(index)
                .expect("valid index");

            node.size = size_of_children;
            node.entry_count = Some(item_count);

            match self.fs_parent_of(index) {
                None => break,
                Some(parent) => index = parent,
            }
        }
    }
}

fn current_path(
    tree: &petgraph::stable_graph::StableGraph<EntryData, ()>,
    root: petgraph::stable_graph::NodeIndex,
    glob_root: Option<TreeIndex>,
) -> String {
    match path_of(tree, root, glob_root).to_string_lossy().to_string() {
        ref p if p.is_empty() => Path::new(".")
            .canonicalize()
            .map(|p| p.to_string_lossy().to_string())
            .unwrap_or_else(|_| String::from(".")),
        p => p,
    }
}