use anyhow::Result;
use crossterm::event::{Event, KeyCode, KeyEventKind};
use ratatui::prelude::*;
use rustic_core::{
repofile::Node, IndexedFull, LocalDestination, LsOptions, ProgressBars, Repository,
RestoreOptions, RestorePlan,
};
use crate::{
commands::tui::widgets::{
popup_input, popup_prompt, Draw, PopUpInput, PopUpPrompt, PopUpText, ProcessEvent,
PromptResult, TextInputResult,
},
helpers::bytes_size_to_string,
};
use super::widgets::popup_text;
enum CurrentScreen {
GetDestination(PopUpInput),
PromptRestore(PopUpPrompt, Option<RestorePlan>),
RestoreDone(PopUpText),
}
pub(crate) struct Restore<'a, P, S> {
current_screen: CurrentScreen,
repo: &'a Repository<P, S>,
opts: RestoreOptions,
node: Node,
source: String,
dest: String,
}
impl<'a, P: ProgressBars, S: IndexedFull> Restore<'a, P, S> {
pub fn new(repo: &'a Repository<P, S>, node: Node, source: String, path: &str) -> Self {
let opts = RestoreOptions::default();
let title = format!("restore {} to:", source);
let popup = popup_input(title, "enter restore destination", path, 1);
Self {
current_screen: CurrentScreen::GetDestination(popup),
node,
repo,
opts,
source,
dest: String::new(),
}
}
pub fn compute_plan(&mut self, mut dest: String, dry_run: bool) -> Result<RestorePlan> {
if dest.is_empty() {
dest = ".".to_string();
}
self.dest = dest;
let dest = LocalDestination::new(&self.dest, true, !self.node.is_dir())?;
let ls_opts = LsOptions {
recursive: true,
..Default::default()
};
let ls = self.repo.ls(&self.node, &ls_opts)?;
let plan = self.repo.prepare_restore(&self.opts, ls, &dest, dry_run)?;
Ok(plan)
}
fn restore(&self, _plan: RestorePlan) -> Result<()> {
let dest = LocalDestination::new(&self.dest, true, !self.node.is_dir())?;
let ls_opts = LsOptions {
recursive: true,
..Default::default()
};
let ls = self.repo.ls(&self.node, &ls_opts)?;
let plan = self
.repo
.prepare_restore(&self.opts, ls.clone(), &dest, false)?;
self.repo.restore(plan, &self.opts, ls, &dest)?;
Ok(())
}
pub fn input(&mut self, event: Event) -> Result<bool> {
use KeyCode::*;
match &mut self.current_screen {
CurrentScreen::GetDestination(prompt) => match prompt.input(event) {
TextInputResult::Cancel => return Ok(true),
TextInputResult::Input(input) => {
let plan = self.compute_plan(input, true)?;
let fs = plan.stats.files;
let ds = plan.stats.dirs;
let popup = popup_prompt(
"restore information",
Text::from(format!(
r#"
restoring from: {}
restoring to: {}
Files: {} to restore, {} unchanged, {} verified, {} to modify, {} additional
Dirs: {} to restore, {} to modify, {} additional
Total restore size: {}
Do you want to proceed (y/n)?
"#,
self.source,
self.dest,
fs.restore,
fs.unchanged,
fs.verified,
fs.modify,
fs.additional,
ds.restore,
ds.modify,
ds.additional,
bytes_size_to_string(plan.restore_size)
)),
);
self.current_screen = CurrentScreen::PromptRestore(popup, Some(plan));
}
TextInputResult::None => {}
},
CurrentScreen::PromptRestore(prompt, plan) => match prompt.input(event) {
PromptResult::Ok => {
let plan = plan.take().unwrap();
self.restore(plan)?;
self.current_screen = CurrentScreen::RestoreDone(popup_text(
"restore done",
format!("restored {} successfully to {}", self.source, self.dest).into(),
));
}
PromptResult::Cancel => return Ok(true),
PromptResult::None => {}
},
CurrentScreen::RestoreDone(_) => match event {
Event::Key(key) if key.kind == KeyEventKind::Press => {
if matches!(key.code, Char('q') | Esc | Enter | Char(' ')) {
return Ok(true);
}
}
_ => {}
},
}
Ok(false)
}
pub fn draw(&mut self, area: Rect, f: &mut Frame<'_>) {
match &mut self.current_screen {
CurrentScreen::GetDestination(popup) => popup.draw(area, f),
CurrentScreen::PromptRestore(popup, _) => popup.draw(area, f),
CurrentScreen::RestoreDone(popup) => popup.draw(area, f),
}
}
}