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