Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 12 additions & 18 deletions src/commands/diff.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,24 +92,18 @@ impl DiffCmd {
if self.interactive {
use tui::summary::SummaryMap;
return tui::run(|progress| {
let config = RUSTIC_APP.config();
config
.repository
.run_indexed_with_progress(progress.clone(), |repo| {
let p = progress
.progress_spinner("starting rustic in interactive mode...");
p.finish();
// create app and run it
let diff = tui::Diff::new(
&repo,
snap1.clone(),
snap2.clone(),
path1,
path2,
SummaryMap::default(),
)?;
tui::run_app(progress.terminal, diff)
})
let p = progress.progress_spinner("starting rustic in interactive mode...");
p.finish();
// create app and run it
let diff = tui::Diff::new(
&repo,
snap1.clone(),
snap2.clone(),
path1,
path2,
SummaryMap::default(),
)?;
tui::run_app(progress.terminal, diff)
});
}

Expand Down
26 changes: 24 additions & 2 deletions src/commands/ls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ use std::{
path::Path,
};

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

use abscissa_core::{Command, Runnable, Shutdown};
Expand Down Expand Up @@ -57,6 +59,11 @@ pub(crate) struct LsCmd {
/// Listing options
#[clap(flatten)]
ls_opts: LsOptions,

#[cfg(feature = "tui")]
/// Run in interactive UI mode
#[clap(long, short)]
interactive: bool,
}

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

let node =
repo.node_from_snapshot_path(&self.snap, |sn| config.snapshot_filter.matches(sn))?;
let (snap_id, path) = self.snap.split_once(':').unwrap_or((&self.snap, ""));
let snap = repo.get_snapshot_from_str(snap_id, |sn| config.snapshot_filter.matches(sn))?;

#[cfg(feature = "tui")]
if self.interactive {
use rustic_core::{Progress, ProgressBars};
use tui::summary::SummaryMap;

return tui::run(|progress| {
let p = progress.progress_spinner("starting rustic in interactive mode...");
p.finish();
// create app and run it
let ls = tui::Ls::new(&repo, snap, path, SummaryMap::default())?;
tui::run_app(progress.terminal, ls)
});
}
let node = repo.node_from_snapshot_and_path(&snap, path)?;

// recursive if standard if we specify a snapshot without dirs. In other cases, use the parameter `recursive`
let mut ls_opts = self.ls_opts.clone();
Expand Down
2 changes: 2 additions & 0 deletions src/commands/tui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ mod snapshots;
pub mod summary;
mod tree;
mod widgets;

pub use diff::Diff;
pub use ls::Ls;
pub use snapshots::Snapshots;

use std::io;
Expand Down
91 changes: 64 additions & 27 deletions src/commands/tui/ls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use crate::{
commands::{
ls::{NodeLs, Summary},
tui::{
TuiResult,
restore::Restore,
widgets::{
Draw, PopUpPrompt, PopUpText, ProcessEvent, PromptResult, SelectTable,
Expand All @@ -30,10 +31,11 @@ use super::{summary::SummaryMap, widgets::PopUpInput};

// the states this screen can be in
enum CurrentScreen<'a, P, S> {
Snapshot,
Ls,
ShowHelp(PopUpText),
Restore(Box<Restore<'a, P, S>>),
PromptExit(PopUpPrompt),
PromptLeave(PopUpPrompt),
ShowFile(Box<PopUpInput>),
}

Expand All @@ -57,7 +59,7 @@ General Commands:

";

pub(crate) struct Snapshot<'a, P, S> {
pub struct Ls<'a, P, S> {
current_screen: CurrentScreen<'a, P, S>,
numeric: bool,
table: WithBlock<SelectTable>,
Expand All @@ -70,32 +72,49 @@ pub(crate) struct Snapshot<'a, P, S> {
summary_map: SummaryMap,
}

pub enum SnapshotResult {
pub enum LsResult {
Exit,
Return(SummaryMap),
None,
}

impl<'a, P: ProgressBars, S: IndexedFull> Snapshot<'a, P, S> {
impl TuiResult for LsResult {
fn exit(&self) -> bool {
!matches!(self, Self::None)
}
}

impl<'a, P: ProgressBars, S: IndexedFull> Ls<'a, P, S> {
pub fn new(
repo: &'a Repository<P, S>,
snapshot: SnapshotFile,
path: &str,
summary_map: SummaryMap,
) -> Result<Self> {
let header = ["Name", "Size", "Mode", "User", "Group", "Time"]
.into_iter()
.map(Text::from)
.collect();

let tree_id = snapshot.tree;
let tree = repo.get_tree(&tree_id)?;
let node = repo.node_from_snapshot_and_path(&snapshot, path)?;
let (tree_id, tree) = node.subtree.map_or_else(
|| -> Result<_> {
Ok((
TreeId::default(),
Tree {
nodes: vec![node.clone()],
},
))
},
|id| Ok((id, repo.get_tree(&id)?)),
)?;
let mut app = Self {
current_screen: CurrentScreen::Snapshot,
current_screen: CurrentScreen::Ls,
numeric: false,
table: WithBlock::new(SelectTable::new(header), Block::new()),
repo,
snapshot,
path: PathBuf::new(),
path: PathBuf::from(path),
trees: Vec::new(),
tree,
tree_id,
Expand Down Expand Up @@ -200,19 +219,20 @@ impl<'a, P: ProgressBars, S: IndexedFull> Snapshot<'a, P, S> {
Ok(())
}

pub fn goback(&mut self) -> bool {
pub fn goback(&mut self) {
_ = self.path.pop();
if let Some((tree, tree_id, idx)) = self.trees.pop() {
self.tree = tree;
self.tree_id = tree_id;
self.table.widget.set_to(idx);
self.update_table();
false
} else {
true
}
}

pub fn in_root(&self) -> bool {
self.trees.is_empty()
}

pub fn toggle_numeric(&mut self) {
self.numeric = !self.numeric;
self.update_table();
Expand All @@ -226,18 +246,24 @@ impl<'a, P: ProgressBars, S: IndexedFull> Snapshot<'a, P, S> {
self.update_table();
Ok(())
}
}

pub fn input(&mut self, event: Event) -> Result<SnapshotResult> {
impl<'a, P: ProgressBars, S: IndexedFull> ProcessEvent for Ls<'a, P, S> {
type Result = Result<LsResult>;
fn input(&mut self, event: Event) -> Result<LsResult> {
use KeyCode::{Backspace, Char, Enter, Esc, Left, Right};
match &mut self.current_screen {
CurrentScreen::Snapshot => match event {
CurrentScreen::Ls => match event {
Event::Key(key) if key.kind == KeyEventKind::Press => match key.code {
Enter | Right => self.enter()?,
Backspace | Left => {
if self.goback() {
return Ok(SnapshotResult::Return(std::mem::take(
&mut self.summary_map,
)));
if self.in_root() {
self.current_screen = CurrentScreen::PromptLeave(popup_prompt(
"leave ls",
"do you want to leave the ls view? (y/n)".into(),
));
} else {
self.goback();
}
}
Esc | Char('q') => {
Expand Down Expand Up @@ -309,33 +335,42 @@ impl<'a, P: ProgressBars, S: IndexedFull> Snapshot<'a, P, S> {
},
CurrentScreen::ShowFile(prompt) => match prompt.input(event) {
TextInputResult::Cancel | TextInputResult::Input(_) => {
self.current_screen = CurrentScreen::Snapshot;
self.current_screen = CurrentScreen::Ls;
}
TextInputResult::None => {}
},
CurrentScreen::ShowHelp(_) => match event {
Event::Key(key) if key.kind == KeyEventKind::Press => {
if matches!(key.code, Char('q' | ' ' | '?') | Esc | Enter) {
self.current_screen = CurrentScreen::Snapshot;
self.current_screen = CurrentScreen::Ls;
}
}
_ => {}
},
CurrentScreen::Restore(restore) => {
if restore.input(event)? {
self.current_screen = CurrentScreen::Snapshot;
self.current_screen = CurrentScreen::Ls;
}
}
CurrentScreen::PromptExit(prompt) => match prompt.input(event) {
PromptResult::Ok => return Ok(SnapshotResult::Exit),
PromptResult::Cancel => self.current_screen = CurrentScreen::Snapshot,
PromptResult::Ok => return Ok(LsResult::Exit),
PromptResult::Cancel => self.current_screen = CurrentScreen::Ls,
PromptResult::None => {}
},
CurrentScreen::PromptLeave(prompt) => match prompt.input(event) {
PromptResult::Ok => {
return Ok(LsResult::Return(std::mem::take(&mut self.summary_map)));
}
PromptResult::Cancel => self.current_screen = CurrentScreen::Ls,
PromptResult::None => {}
},
}
Ok(SnapshotResult::None)
Ok(LsResult::None)
}
}

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

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

// draw popups
match &mut self.current_screen {
CurrentScreen::Snapshot | CurrentScreen::Restore(_) => {}
CurrentScreen::Ls | CurrentScreen::Restore(_) => {}
CurrentScreen::ShowHelp(popup) => popup.draw(area, f),
CurrentScreen::PromptExit(popup) => popup.draw(area, f),
CurrentScreen::PromptExit(popup) | CurrentScreen::PromptLeave(popup) => {
popup.draw(area, f);
}
CurrentScreen::ShowFile(popup) => popup.draw(area, f),
}
}
Expand Down
15 changes: 8 additions & 7 deletions src/commands/tui/snapshots.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use crate::{
snapshots::{fill_table, snap_to_table},
tui::{
diff::{Diff, DiffResult},
ls::{Snapshot, SnapshotResult},
ls::{Ls, LsResult},
tree::{Tree, TreeIterItem, TreeNode},
widgets::{
Draw, PopUpInput, PopUpPrompt, PopUpTable, PopUpText, ProcessEvent, PromptResult,
Expand All @@ -42,7 +42,7 @@ enum CurrentScreen<'a, P, S> {
EnterFilter(PopUpInput),
PromptWrite(PopUpPrompt),
PromptExit(PopUpPrompt),
Dir(Box<Snapshot<'a, P, S>>),
Dir(Box<Ls<'a, P, S>>),
Diff(Box<Diff<'a, P, S>>),
}

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

pub fn dir(&mut self) -> Result<Option<Snapshot<'a, P, S>>> {
pub fn dir(&mut self) -> Result<Option<Ls<'a, P, S>>> {
self.selected_snapshot().cloned().map_or(Ok(None), |snap| {
Some(Snapshot::new(
Some(Ls::new(
self.repo,
snap,
"",
mem::take(&mut self.summary_map),
))
.transpose()
Expand Down Expand Up @@ -962,12 +963,12 @@ impl<'a, P: ProgressBars, S: IndexedFull> ProcessEvent for Snapshots<'a, P, S> {
PromptResult::None => {}
},
CurrentScreen::Dir(dir) => match dir.input(event)? {
SnapshotResult::Exit => return Ok(true),
SnapshotResult::Return(summary_map) => {
LsResult::Exit => return Ok(true),
LsResult::Return(summary_map) => {
self.current_screen = CurrentScreen::Snapshots;
self.summary_map = summary_map;
}
SnapshotResult::None => {}
LsResult::None => {}
},
CurrentScreen::Diff(diff) => match diff.input(event)? {
DiffResult::Exit => return Ok(true),
Expand Down
Loading