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<T: TuiResult, A: Draw + ProcessEvent<Result = Result<T>>, B: Backend>(
80 terminal: Arc<RwLock<Terminal<B>>>,
81 mut app: A,
82) -> Result<()> {
83 loop {
84 _ = terminal.write().unwrap().draw(|f| ui(f, &mut app))?;
85 let event = event::read()?;
86
87 if let Event::Key(KeyEvent {
88 code: KeyCode::Char('c'),
89 modifiers: KeyModifiers::CONTROL,
90 kind: KeyEventKind::Press,
91 ..
92 }) = event
93 {
94 return Ok(());
95 }
96 if app.input(event)?.exit() {
97 return Ok(());
98 }
99 }
100}
101
102fn ui<A: Draw>(f: &mut Frame<'_>, app: &mut A) {
103 let area = f.area();
104 app.draw(area, f);
105}