-
Notifications
You must be signed in to change notification settings - Fork 83
/
Copy pathrestore.rs
107 lines (88 loc) · 3.03 KB
/
restore.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
//! `restore` subcommand
use crate::{
commands::open_repository, helpers::bytes_size_to_string, status_err, Application, RUSTIC_APP,
};
use abscissa_core::{Command, Runnable, Shutdown};
use anyhow::Result;
use log::info;
use rustic_core::{LocalDestination, LsOptions, RestoreOptions};
use crate::filtering::SnapshotFilter;
/// `restore` subcommand
#[allow(clippy::struct_excessive_bools)]
#[derive(clap::Parser, Command, Debug)]
pub(crate) struct RestoreCmd {
/// Snapshot/path to restore
#[clap(value_name = "SNAPSHOT[:PATH]")]
snap: String,
/// Restore destination
#[clap(value_name = "DESTINATION")]
dest: String,
/// Restore options
#[clap(flatten)]
opts: RestoreOptions,
/// List options
#[clap(flatten)]
ls_opts: LsOptions,
/// Snapshot filter options (when using latest)
#[clap(
flatten,
next_help_heading = "Snapshot filter options (when using latest)"
)]
filter: SnapshotFilter,
}
impl Runnable for RestoreCmd {
fn run(&self) {
if let Err(err) = self.inner_run() {
status_err!("{}", err);
RUSTIC_APP.shutdown(Shutdown::Crash);
};
}
}
impl RestoreCmd {
fn inner_run(&self) -> Result<()> {
let config = RUSTIC_APP.config();
let dry_run = config.global.dry_run;
let repo = open_repository(&config.repository)?.to_indexed()?;
let node =
repo.node_from_snapshot_path(&self.snap, |sn| config.snapshot_filter.matches(sn))?;
// for restore, always recurse into tree
let mut ls_opts = self.ls_opts.clone();
ls_opts.recursive = true;
let ls = repo.ls(&node, &ls_opts)?;
let dest = LocalDestination::new(&self.dest, true, !node.is_dir())?;
let restore_infos = repo.prepare_restore(&self.opts, ls, &dest, dry_run)?;
let fs = restore_infos.stats.files;
println!(
"Files: {} to restore, {} unchanged, {} verified, {} to modify, {} additional",
fs.restore, fs.unchanged, fs.verified, fs.modify, fs.additional
);
let ds = restore_infos.stats.dirs;
println!(
"Dirs: {} to restore, {} to modify, {} additional",
ds.restore, ds.modify, ds.additional
);
info!(
"total restore size: {}",
bytes_size_to_string(restore_infos.restore_size)
);
if restore_infos.matched_size > 0 {
info!(
"using {} of existing file contents.",
bytes_size_to_string(restore_infos.matched_size)
);
}
if restore_infos.restore_size == 0 {
info!("all file contents are fine.");
}
if dry_run {
repo.warm_up(restore_infos.to_packs().into_iter())?;
} else {
// save some memory
let repo = repo.drop_data_from_index();
let ls = repo.ls(&node, &ls_opts)?;
repo.restore(restore_infos, &self.opts, ls, &dest)?;
println!("restore done.");
}
Ok(())
}
}