rustic_rs/commands/
tui.rs1mod diff;
3mod ls;
4mod progress;
5mod restore;
6mod snapshots;
7pub mod summary;
8mod tree;
9mod widgets;
10
11pub use diff::Diff;
12pub use ls::Ls;
13pub use snapshots::Snapshots;
14
15use std::io;
16use std::sync::{Arc, RwLock};
17
18use anyhow::Result;
19use crossterm::event::{KeyEvent, KeyModifiers};
20use crossterm::{
21 event::{self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode, KeyEventKind},
22 execute,
23 terminal::{EnterAlternateScreen, LeaveAlternateScreen, disable_raw_mode, enable_raw_mode},
24};
25use progress::TuiProgressBars;
26use ratatui::prelude::*;
27use scopeguard::defer;
28use widgets::{Draw, ProcessEvent};
29
30pub trait TuiResult {
31 fn exit(&self) -> bool;
32}
33
34impl TuiResult for bool {
35 fn exit(&self) -> bool {
36 *self
37 }
38}
39
40pub fn run(f: impl FnOnce(TuiProgressBars) -> Result<()>) -> Result<()> {
41 let terminal = init_terminal()?;
43 let terminal = Arc::new(RwLock::new(terminal));
44
45 defer! {
47 reset_terminal().unwrap();
48 }
49
50 let progress = TuiProgressBars { terminal };
51
52 if let Err(err) = f(progress) {
53 println!("{err:?}");
54 }
55
56 Ok(())
57}
58
59fn init_terminal() -> Result<Terminal<CrosstermBackend<io::Stdout>>> {
61 execute!(io::stdout(), EnterAlternateScreen, EnableMouseCapture)?;
62 enable_raw_mode()?;
63
64 let backend = CrosstermBackend::new(io::stdout());
65
66 let mut terminal = Terminal::new(backend)?;
67 terminal.hide_cursor()?;
68
69 Ok(terminal)
70}
71
72fn reset_terminal() -> Result<()> {
74 disable_raw_mode()?;
75 execute!(io::stdout(), LeaveAlternateScreen, DisableMouseCapture)?;
76 Ok(())
77}
78
79pub fn run_app<B: Backend, A, T>(terminal: Arc<RwLock<Terminal<B>>>, mut app: A) -> Result<()>
80where
81 A: Draw + ProcessEvent<Result = Result<T>>,
82 T: TuiResult,
83 B::Error: Send + Sync + 'static,
84{
85 loop {
86 _ = terminal.write().unwrap().draw(|f| ui(f, &mut app))?;
87 let event = event::read()?;
88
89 if let Event::Key(KeyEvent {
90 code: KeyCode::Char('c'),
91 modifiers: KeyModifiers::CONTROL,
92 kind: KeyEventKind::Press,
93 ..
94 }) = event
95 {
96 return Ok(());
97 }
98 if app.input(event)?.exit() {
99 return Ok(());
100 }
101 }
102}
103
104fn ui<A: Draw>(f: &mut Frame<'_>, app: &mut A) {
105 let area = f.area();
106 app.draw(area, f);
107}