Skip to content

Commit 91fd69a

Browse files
committed
feat(interactive): Add --interactive option to ls
1 parent f6890c1 commit 91fd69a

File tree

5 files changed

+110
-54
lines changed

5 files changed

+110
-54
lines changed

src/commands/diff.rs

Lines changed: 12 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -92,24 +92,18 @@ impl DiffCmd {
9292
if self.interactive {
9393
use tui::summary::SummaryMap;
9494
return tui::run(|progress| {
95-
let config = RUSTIC_APP.config();
96-
config
97-
.repository
98-
.run_indexed_with_progress(progress.clone(), |repo| {
99-
let p = progress
100-
.progress_spinner("starting rustic in interactive mode...");
101-
p.finish();
102-
// create app and run it
103-
let diff = tui::Diff::new(
104-
&repo,
105-
snap1.clone(),
106-
snap2.clone(),
107-
path1,
108-
path2,
109-
SummaryMap::default(),
110-
)?;
111-
tui::run_app(progress.terminal, diff)
112-
})
95+
let p = progress.progress_spinner("starting rustic in interactive mode...");
96+
p.finish();
97+
// create app and run it
98+
let diff = tui::Diff::new(
99+
&repo,
100+
snap1.clone(),
101+
snap2.clone(),
102+
path1,
103+
path2,
104+
SummaryMap::default(),
105+
)?;
106+
tui::run_app(progress.terminal, diff)
113107
});
114108
}
115109

src/commands/ls.rs

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ use std::{
55
path::Path,
66
};
77

8+
#[cfg(feature = "tui")]
9+
use crate::commands::tui;
810
use crate::{Application, RUSTIC_APP, repository::CliIndexedRepo, status_err};
911

1012
use abscissa_core::{Command, Runnable, Shutdown};
@@ -57,6 +59,11 @@ pub(crate) struct LsCmd {
5759
/// Listing options
5860
#[clap(flatten)]
5961
ls_opts: LsOptions,
62+
63+
#[cfg(feature = "tui")]
64+
/// Run in interactive UI mode
65+
#[clap(long, short)]
66+
interactive: bool,
6067
}
6168

6269
impl Runnable for LsCmd {
@@ -152,8 +159,23 @@ impl LsCmd {
152159
fn inner_run(&self, repo: CliIndexedRepo) -> Result<()> {
153160
let config = RUSTIC_APP.config();
154161

155-
let node =
156-
repo.node_from_snapshot_path(&self.snap, |sn| config.snapshot_filter.matches(sn))?;
162+
let (snap_id, path) = self.snap.split_once(':').unwrap_or((&self.snap, ""));
163+
let snap = repo.get_snapshot_from_str(snap_id, |sn| config.snapshot_filter.matches(sn))?;
164+
165+
#[cfg(feature = "tui")]
166+
if self.interactive {
167+
use rustic_core::{Progress, ProgressBars};
168+
use tui::summary::SummaryMap;
169+
170+
return tui::run(|progress| {
171+
let p = progress.progress_spinner("starting rustic in interactive mode...");
172+
p.finish();
173+
// create app and run it
174+
let ls = tui::Ls::new(&repo, snap, path, SummaryMap::default())?;
175+
tui::run_app(progress.terminal, ls)
176+
});
177+
}
178+
let node = repo.node_from_snapshot_and_path(&snap, path)?;
157179

158180
// recursive if standard if we specify a snapshot without dirs. In other cases, use the parameter `recursive`
159181
let mut ls_opts = self.ls_opts.clone();

src/commands/tui.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ mod snapshots;
77
pub mod summary;
88
mod tree;
99
mod widgets;
10+
1011
pub use diff::Diff;
12+
pub use ls::Ls;
1113
pub use snapshots::Snapshots;
1214

1315
use std::io;

src/commands/tui/ls.rs

Lines changed: 64 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use crate::{
1616
commands::{
1717
ls::{NodeLs, Summary},
1818
tui::{
19+
TuiResult,
1920
restore::Restore,
2021
widgets::{
2122
Draw, PopUpPrompt, PopUpText, ProcessEvent, PromptResult, SelectTable,
@@ -30,10 +31,11 @@ use super::{summary::SummaryMap, widgets::PopUpInput};
3031

3132
// the states this screen can be in
3233
enum CurrentScreen<'a, P, S> {
33-
Snapshot,
34+
Ls,
3435
ShowHelp(PopUpText),
3536
Restore(Box<Restore<'a, P, S>>),
3637
PromptExit(PopUpPrompt),
38+
PromptLeave(PopUpPrompt),
3739
ShowFile(Box<PopUpInput>),
3840
}
3941

@@ -57,7 +59,7 @@ General Commands:
5759
5860
";
5961

60-
pub(crate) struct Snapshot<'a, P, S> {
62+
pub struct Ls<'a, P, S> {
6163
current_screen: CurrentScreen<'a, P, S>,
6264
numeric: bool,
6365
table: WithBlock<SelectTable>,
@@ -70,32 +72,49 @@ pub(crate) struct Snapshot<'a, P, S> {
7072
summary_map: SummaryMap,
7173
}
7274

73-
pub enum SnapshotResult {
75+
pub enum LsResult {
7476
Exit,
7577
Return(SummaryMap),
7678
None,
7779
}
7880

79-
impl<'a, P: ProgressBars, S: IndexedFull> Snapshot<'a, P, S> {
81+
impl TuiResult for LsResult {
82+
fn exit(&self) -> bool {
83+
!matches!(self, Self::None)
84+
}
85+
}
86+
87+
impl<'a, P: ProgressBars, S: IndexedFull> Ls<'a, P, S> {
8088
pub fn new(
8189
repo: &'a Repository<P, S>,
8290
snapshot: SnapshotFile,
91+
path: &str,
8392
summary_map: SummaryMap,
8493
) -> Result<Self> {
8594
let header = ["Name", "Size", "Mode", "User", "Group", "Time"]
8695
.into_iter()
8796
.map(Text::from)
8897
.collect();
8998

90-
let tree_id = snapshot.tree;
91-
let tree = repo.get_tree(&tree_id)?;
99+
let node = repo.node_from_snapshot_and_path(&snapshot, path)?;
100+
let (tree_id, tree) = node.subtree.map_or_else(
101+
|| -> Result<_> {
102+
Ok((
103+
TreeId::default(),
104+
Tree {
105+
nodes: vec![node.clone()],
106+
},
107+
))
108+
},
109+
|id| Ok((id, repo.get_tree(&id)?)),
110+
)?;
92111
let mut app = Self {
93-
current_screen: CurrentScreen::Snapshot,
112+
current_screen: CurrentScreen::Ls,
94113
numeric: false,
95114
table: WithBlock::new(SelectTable::new(header), Block::new()),
96115
repo,
97116
snapshot,
98-
path: PathBuf::new(),
117+
path: PathBuf::from(path),
99118
trees: Vec::new(),
100119
tree,
101120
tree_id,
@@ -200,19 +219,20 @@ impl<'a, P: ProgressBars, S: IndexedFull> Snapshot<'a, P, S> {
200219
Ok(())
201220
}
202221

203-
pub fn goback(&mut self) -> bool {
222+
pub fn goback(&mut self) {
204223
_ = self.path.pop();
205224
if let Some((tree, tree_id, idx)) = self.trees.pop() {
206225
self.tree = tree;
207226
self.tree_id = tree_id;
208227
self.table.widget.set_to(idx);
209228
self.update_table();
210-
false
211-
} else {
212-
true
213229
}
214230
}
215231

232+
pub fn in_root(&self) -> bool {
233+
self.trees.is_empty()
234+
}
235+
216236
pub fn toggle_numeric(&mut self) {
217237
self.numeric = !self.numeric;
218238
self.update_table();
@@ -226,18 +246,24 @@ impl<'a, P: ProgressBars, S: IndexedFull> Snapshot<'a, P, S> {
226246
self.update_table();
227247
Ok(())
228248
}
249+
}
229250

230-
pub fn input(&mut self, event: Event) -> Result<SnapshotResult> {
251+
impl<'a, P: ProgressBars, S: IndexedFull> ProcessEvent for Ls<'a, P, S> {
252+
type Result = Result<LsResult>;
253+
fn input(&mut self, event: Event) -> Result<LsResult> {
231254
use KeyCode::{Backspace, Char, Enter, Esc, Left, Right};
232255
match &mut self.current_screen {
233-
CurrentScreen::Snapshot => match event {
256+
CurrentScreen::Ls => match event {
234257
Event::Key(key) if key.kind == KeyEventKind::Press => match key.code {
235258
Enter | Right => self.enter()?,
236259
Backspace | Left => {
237-
if self.goback() {
238-
return Ok(SnapshotResult::Return(std::mem::take(
239-
&mut self.summary_map,
240-
)));
260+
if self.in_root() {
261+
self.current_screen = CurrentScreen::PromptLeave(popup_prompt(
262+
"leave ls",
263+
"do you want to leave the ls view? (y/n)".into(),
264+
));
265+
} else {
266+
self.goback();
241267
}
242268
}
243269
Esc | Char('q') => {
@@ -309,33 +335,42 @@ impl<'a, P: ProgressBars, S: IndexedFull> Snapshot<'a, P, S> {
309335
},
310336
CurrentScreen::ShowFile(prompt) => match prompt.input(event) {
311337
TextInputResult::Cancel | TextInputResult::Input(_) => {
312-
self.current_screen = CurrentScreen::Snapshot;
338+
self.current_screen = CurrentScreen::Ls;
313339
}
314340
TextInputResult::None => {}
315341
},
316342
CurrentScreen::ShowHelp(_) => match event {
317343
Event::Key(key) if key.kind == KeyEventKind::Press => {
318344
if matches!(key.code, Char('q' | ' ' | '?') | Esc | Enter) {
319-
self.current_screen = CurrentScreen::Snapshot;
345+
self.current_screen = CurrentScreen::Ls;
320346
}
321347
}
322348
_ => {}
323349
},
324350
CurrentScreen::Restore(restore) => {
325351
if restore.input(event)? {
326-
self.current_screen = CurrentScreen::Snapshot;
352+
self.current_screen = CurrentScreen::Ls;
327353
}
328354
}
329355
CurrentScreen::PromptExit(prompt) => match prompt.input(event) {
330-
PromptResult::Ok => return Ok(SnapshotResult::Exit),
331-
PromptResult::Cancel => self.current_screen = CurrentScreen::Snapshot,
356+
PromptResult::Ok => return Ok(LsResult::Exit),
357+
PromptResult::Cancel => self.current_screen = CurrentScreen::Ls,
358+
PromptResult::None => {}
359+
},
360+
CurrentScreen::PromptLeave(prompt) => match prompt.input(event) {
361+
PromptResult::Ok => {
362+
return Ok(LsResult::Return(std::mem::take(&mut self.summary_map)));
363+
}
364+
PromptResult::Cancel => self.current_screen = CurrentScreen::Ls,
332365
PromptResult::None => {}
333366
},
334367
}
335-
Ok(SnapshotResult::None)
368+
Ok(LsResult::None)
336369
}
370+
}
337371

338-
pub fn draw(&mut self, area: Rect, f: &mut Frame<'_>) {
372+
impl<'a, P: ProgressBars, S: IndexedFull> Draw for Ls<'a, P, S> {
373+
fn draw(&mut self, area: Rect, f: &mut Frame<'_>) {
339374
let rects = Layout::vertical([Constraint::Min(0), Constraint::Length(1)]).split(area);
340375

341376
if let CurrentScreen::Restore(restore) = &mut self.current_screen {
@@ -355,9 +390,11 @@ impl<'a, P: ProgressBars, S: IndexedFull> Snapshot<'a, P, S> {
355390

356391
// draw popups
357392
match &mut self.current_screen {
358-
CurrentScreen::Snapshot | CurrentScreen::Restore(_) => {}
393+
CurrentScreen::Ls | CurrentScreen::Restore(_) => {}
359394
CurrentScreen::ShowHelp(popup) => popup.draw(area, f),
360-
CurrentScreen::PromptExit(popup) => popup.draw(area, f),
395+
CurrentScreen::PromptExit(popup) | CurrentScreen::PromptLeave(popup) => {
396+
popup.draw(area, f);
397+
}
361398
CurrentScreen::ShowFile(popup) => popup.draw(area, f),
362399
}
363400
}

src/commands/tui/snapshots.rs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use crate::{
1919
snapshots::{fill_table, snap_to_table},
2020
tui::{
2121
diff::{Diff, DiffResult},
22-
ls::{Snapshot, SnapshotResult},
22+
ls::{Ls, LsResult},
2323
tree::{Tree, TreeIterItem, TreeNode},
2424
widgets::{
2525
Draw, PopUpInput, PopUpPrompt, PopUpTable, PopUpText, ProcessEvent, PromptResult,
@@ -42,7 +42,7 @@ enum CurrentScreen<'a, P, S> {
4242
EnterFilter(PopUpInput),
4343
PromptWrite(PopUpPrompt),
4444
PromptExit(PopUpPrompt),
45-
Dir(Box<Snapshot<'a, P, S>>),
45+
Dir(Box<Ls<'a, P, S>>),
4646
Diff(Box<Diff<'a, P, S>>),
4747
}
4848

@@ -529,11 +529,12 @@ impl<'a, P: ProgressBars, S: IndexedFull> Snapshots<'a, P, S> {
529529
popup_table("snapshot details", rows)
530530
}
531531

532-
pub fn dir(&mut self) -> Result<Option<Snapshot<'a, P, S>>> {
532+
pub fn dir(&mut self) -> Result<Option<Ls<'a, P, S>>> {
533533
self.selected_snapshot().cloned().map_or(Ok(None), |snap| {
534-
Some(Snapshot::new(
534+
Some(Ls::new(
535535
self.repo,
536536
snap,
537+
"",
537538
mem::take(&mut self.summary_map),
538539
))
539540
.transpose()
@@ -962,12 +963,12 @@ impl<'a, P: ProgressBars, S: IndexedFull> ProcessEvent for Snapshots<'a, P, S> {
962963
PromptResult::None => {}
963964
},
964965
CurrentScreen::Dir(dir) => match dir.input(event)? {
965-
SnapshotResult::Exit => return Ok(true),
966-
SnapshotResult::Return(summary_map) => {
966+
LsResult::Exit => return Ok(true),
967+
LsResult::Return(summary_map) => {
967968
self.current_screen = CurrentScreen::Snapshots;
968969
self.summary_map = summary_map;
969970
}
970-
SnapshotResult::None => {}
971+
LsResult::None => {}
971972
},
972973
CurrentScreen::Diff(diff) => match diff.input(event)? {
973974
DiffResult::Exit => return Ok(true),

0 commit comments

Comments
 (0)