1use anyhow::Result;
2use crossterm::event::{Event, KeyCode, KeyEventKind};
3use ratatui::prelude::*;
4use rustic_core::{LocalDestination, LsOptions, RestoreOptions, RestorePlan, repofile::Node};
5
6use crate::{
7 commands::tui::widgets::{
8 Draw, PopUpInput, PopUpPrompt, PopUpText, ProcessEvent, PromptResult, TextInputResult,
9 popup_input, popup_prompt,
10 },
11 helpers::bytes_size_to_string,
12 repository::IndexedRepo,
13};
14
15use super::widgets::popup_text;
16
17enum CurrentScreen {
19 GetDestination(PopUpInput),
20 PromptRestore(PopUpPrompt, Option<RestorePlan>),
21 RestoreDone(PopUpText),
22}
23
24pub(crate) struct Restore<'a> {
25 current_screen: CurrentScreen,
26 repo: &'a IndexedRepo,
27 opts: RestoreOptions,
28 node: Node,
29 source: String,
30 dest: String,
31}
32
33impl<'a> Restore<'a> {
34 pub fn new(repo: &'a IndexedRepo, node: Node, source: String, path: &str) -> Self {
35 let opts = RestoreOptions::default();
36 let title = format!("restore {source} to:");
37 let popup = popup_input(title, "enter restore destination", path, 1);
38 Self {
39 current_screen: CurrentScreen::GetDestination(popup),
40 node,
41 repo,
42 opts,
43 source,
44 dest: String::new(),
45 }
46 }
47
48 pub fn compute_plan(&mut self, mut dest: String, dry_run: bool) -> Result<RestorePlan> {
49 if dest.is_empty() {
50 dest = ".".to_string();
51 }
52 self.dest = dest;
53 let dest = LocalDestination::new(&self.dest, true, !self.node.is_dir())?;
54
55 let mut ls_opts = LsOptions::default();
57 ls_opts.recursive = true;
58
59 let ls = self.repo.ls(&self.node, &ls_opts)?;
60
61 let plan = self.repo.prepare_restore(&self.opts, ls, &dest, dry_run)?;
62
63 Ok(plan)
64 }
65
66 fn restore(&self, _plan: RestorePlan) -> Result<()> {
71 let dest = LocalDestination::new(&self.dest, true, !self.node.is_dir())?;
72
73 let mut ls_opts = LsOptions::default();
75 ls_opts.recursive = true;
76
77 let ls = self.repo.ls(&self.node, &ls_opts)?;
78 let plan = self
79 .repo
80 .prepare_restore(&self.opts, ls.clone(), &dest, false)?;
81
82 self.repo.restore(plan, &self.opts, ls, &dest)?;
84 Ok(())
85 }
86
87 pub fn input(&mut self, event: Event) -> Result<bool> {
88 use KeyCode::{Char, Enter, Esc};
89 match &mut self.current_screen {
90 CurrentScreen::GetDestination(prompt) => match prompt.input(event) {
91 TextInputResult::Cancel => return Ok(true),
92 TextInputResult::Input(input) => {
93 let plan = self.compute_plan(input, true)?;
94 let fs = plan.stats.files;
95 let ds = plan.stats.dirs;
96 let popup = popup_prompt(
97 "restore information",
98 Text::from(format!(
99 r#"
100restoring from: {}
101restoring to: {}
102
103Files: {} to restore, {} unchanged, {} verified, {} to modify, {} additional
104Dirs: {} to restore, {} to modify, {} additional
105Total restore size: {}
106
107Do you want to proceed (y/n)?
108 "#,
109 self.source,
110 self.dest,
111 fs.restore,
112 fs.unchanged,
113 fs.verified,
114 fs.modify,
115 fs.additional,
116 ds.restore,
117 ds.modify,
118 ds.additional,
119 bytes_size_to_string(plan.restore_size)
120 )),
121 );
122 self.current_screen = CurrentScreen::PromptRestore(popup, Some(plan));
123 }
124 TextInputResult::None => {}
125 },
126 CurrentScreen::PromptRestore(prompt, plan) => match prompt.input(event) {
127 PromptResult::Ok => {
128 let plan = plan.take().unwrap();
129 self.restore(plan)?;
130 self.current_screen = CurrentScreen::RestoreDone(popup_text(
131 "restore done",
132 format!("restored {} successfully to {}", self.source, self.dest).into(),
133 ));
134 }
135 PromptResult::Cancel => return Ok(true),
136 PromptResult::None => {}
137 },
138 CurrentScreen::RestoreDone(_) => match event {
139 Event::Key(key) if key.kind == KeyEventKind::Press => {
140 if matches!(key.code, Char('q' | ' ') | Esc | Enter) {
141 return Ok(true);
142 }
143 }
144 _ => {}
145 },
146 }
147 Ok(false)
148 }
149
150 pub fn draw(&mut self, area: Rect, f: &mut Frame<'_>) {
151 match &mut self.current_screen {
153 CurrentScreen::GetDestination(popup) => popup.draw(area, f),
154 CurrentScreen::PromptRestore(popup, _) => popup.draw(area, f),
155 CurrentScreen::RestoreDone(popup) => popup.draw(area, f),
156 }
157 }
158}