rustic_rs/commands/
merge.rs

1//! `merge` subcommand
2
3use 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/// `merge` subcommand
13#[derive(clap::Parser, Default, Command, Debug)]
14pub(super) struct MergeCmd {
15    /// Snapshots to merge. If none is given, use filter options to filter from all snapshots.
16    #[clap(value_name = "ID")]
17    ids: Vec<String>,
18
19    /// Output generated snapshot in json format
20    #[clap(long)]
21    json: bool,
22
23    /// Remove input snapshots after merging
24    #[clap(long)]
25    delete: bool,
26
27    /// Snapshot options
28    #[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            // TODO: Maybe use this check in repo.delete_snapshots?
69            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}