rustic_rs/commands/
merge.rs1use crate::{repository::CliOpenRepo, status_err, Application, RUSTIC_APP};
4use abscissa_core::{Command, Runnable, Shutdown};
5use anyhow::Result;
6use log::info;
7
8use chrono::Local;
9
10use rustic_core::{last_modified_node, repofile::SnapshotFile, SnapshotOptions};
11
12#[derive(clap::Parser, Default, Command, Debug)]
14pub(super) struct MergeCmd {
15 #[clap(value_name = "ID")]
17 ids: Vec<String>,
18
19 #[clap(long)]
21 json: bool,
22
23 #[clap(long)]
25 delete: bool,
26
27 #[clap(flatten, next_help_heading = "Snapshot options")]
29 snap_opts: SnapshotOptions,
30}
31
32impl Runnable for MergeCmd {
33 fn run(&self) {
34 if let Err(err) = RUSTIC_APP
35 .config()
36 .repository
37 .run_open(|repo| self.inner_run(repo))
38 {
39 status_err!("{}", err);
40 RUSTIC_APP.shutdown(Shutdown::Crash);
41 };
42 }
43}
44
45impl MergeCmd {
46 fn inner_run(&self, repo: CliOpenRepo) -> Result<()> {
47 let config = RUSTIC_APP.config();
48 let repo = repo.to_indexed_ids()?;
49
50 let snapshots = if self.ids.is_empty() {
51 repo.get_matching_snapshots(|sn| config.snapshot_filter.matches(sn))?
52 } else {
53 repo.get_snapshots(&self.ids)?
54 };
55
56 let snap = SnapshotFile::from_options(&self.snap_opts)?;
57
58 let snap = repo.merge_snapshots(&snapshots, &last_modified_node, snap)?;
59
60 if self.json {
61 let mut stdout = std::io::stdout();
62 serde_json::to_writer_pretty(&mut stdout, &snap)?;
63 }
64 info!("saved new snapshot as {}.", snap.id);
65
66 if self.delete {
67 let now = Local::now();
68 let snap_ids: Vec<_> = snapshots
70 .iter()
71 .filter(|sn| !sn.must_keep(now))
72 .map(|sn| sn.id)
73 .collect();
74 repo.delete_snapshots(&snap_ids)?;
75 }
76
77 Ok(())
78 }
79}