summaryrefslogtreecommitdiffstats
path: root/src/interactive/app/tree_view.rs
blob: 2afc366eac189d0298c55de20a8a3189ad02d61f (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
127
128
129
130
131
132
133
134
135
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,
        is_scanning: bool,
    ) -> Vec<EntryDataBundle> {
        sorted_entries(
            &self.traversal.tree,
            view_root,
            sorting,
            self.glob_tree_root,
            is_scanning,
        )
    }

    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, root_index: TreeIndex, remove_root_node: bool) -> usize {
        let mut entries_deleted = 0;
        let mut bfs = Bfs::new(self.tree(), root_index);

        while let Some(nx) = bfs.next(&self.tree()) {
            if nx == root_index && !remove_root_node {
                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,
    }
}