summaryrefslogtreecommitdiffstats
path: root/src/interactive/app.rs
blob: 998d337e0360e1ea1621b7211b1fcebb462c6fd4 (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
use super::widgets::{DisplayState, MainWindow};
use crate::{interactive::Traversal, sorted_entries, ByteFormat, WalkOptions, WalkResult};
use failure::Error;
use std::{io, path::PathBuf};
use termion::input::{Keys, TermReadEventsAndRaw};
use tui::widgets::Widget;
use tui::{backend::Backend, Terminal};

/// Options to configure how we display things
#[derive(Clone, Copy)]
pub struct DisplayOptions {
    pub byte_format: ByteFormat,
}

impl From<WalkOptions> for DisplayOptions {
    fn from(WalkOptions { byte_format, .. }: WalkOptions) -> Self {
        DisplayOptions { byte_format }
    }
}

/// State and methods representing the interactive disk usage analyser for the terminal
pub struct TerminalApp {
    pub traversal: Traversal,
    pub display: DisplayOptions,
    pub state: DisplayState,
}

impl TerminalApp {
    fn draw<B>(&self, terminal: &mut Terminal<B>) -> Result<(), Error>
    where
        B: Backend,
    {
        let Self {
            traversal,
            display,
            state,
        } = self;

        terminal.draw(|mut f| {
            let full_screen = f.size();
            MainWindow {
                traversal,
                display: *display,
                state: &state,
            }
            .render(&mut f, full_screen)
        })?;

        Ok(())
    }
    pub fn process_events<B, R>(
        &mut self,
        terminal: &mut Terminal<B>,
        keys: Keys<R>,
    ) -> Result<WalkResult, Error>
    where
        B: Backend,
        R: io::Read + TermReadEventsAndRaw,
    {
        use termion::event::Key::{Char, Ctrl};

        self.draw(terminal)?;
        for key in keys.filter_map(Result::ok) {
            match key {
                Char('s') => self.state.sorting.toggle_size(),
                Ctrl('c') | Char('\n') | Char('q') => break,
                _ => {}
            };
            self.draw(terminal)?;
        }
        Ok(WalkResult {
            num_errors: self.traversal.io_errors,
        })
    }

    pub fn initialize<B>(
        terminal: &mut Terminal<B>,
        options: WalkOptions,
        input: Vec<PathBuf>,
    ) -> Result<TerminalApp, Error>
    where
        B: Backend,
    {
        let display_options: DisplayOptions = options.clone().into();
        let traversal = Traversal::from_walk(options, input, move |traversal| {
            terminal.draw(|mut f| {
                let full_screen = f.size();
                let state = DisplayState {
                    root: traversal.root_index,
                    selected: None,
                    sorting: Default::default(),
                };
                MainWindow {
                    traversal,
                    display: display_options,
                    state: &state,
                }
                .render(&mut f, full_screen)
            })?;
            Ok(())
        })?;

        let sorting = Default::default();
        let root = traversal.root_index;
        let selected = sorted_entries(&traversal.tree, root, sorting)
            .next()
            .map(|(idx, _)| idx);
        Ok(TerminalApp {
            state: DisplayState {
                root,
                selected,
                sorting,
            },
            display: display_options,
            traversal: traversal,
        })
    }
}