Skip to main content

rustic_rs/commands/
merge.rs

1//! `merge` subcommand
2
3use crate::{
4    Application, RUSTIC_APP,
5    repository::{OpenRepo, get_snapots_from_ids},
6    status_err,
7};
8use abscissa_core::{Command, Runnable, Shutdown};
9use anyhow::Result;
10use jiff::Zoned;
11use log::info;
12
13use rustic_core::{SnapshotOptions, last_modified_node, repofile::SnapshotFile};
14
15/// `merge` subcommand
16#[derive(clap::Parser, Default, Command, Debug)]
17pub(super) struct MergeCmd {
18    /// Snapshots to merge. If none is given, use filter options to filter from all snapshots.
19    #[clap(value_name = "ID")]
20    ids: Vec<String>,
21
22    /// Output generated snapshot in json format
23    #[clap(long)]
24    json: bool,
25
26    /// Remove input snapshots after merging
27    #[clap(long)]
28    delete: bool,
29
30    /// Snapshot options
31    #[clap(flatten, next_help_heading = "Snapshot options")]
32    snap_opts: SnapshotOptions,
33}
34
35impl Runnable for MergeCmd {
36    fn run(&self) {
37        if let Err(err) = RUSTIC_APP
38            .config()
39            .repository
40            .run_open(|repo| self.inner_run(repo))
41        {
42            status_err!("{}", err);
43            RUSTIC_APP.shutdown(Shutdown::Crash);
44        };
45    }
46}
47
48impl MergeCmd {
49    fn inner_run(&self, repo: OpenRepo) -> Result<()> {
50        let config = RUSTIC_APP.config();
51        let repo = repo.to_indexed_ids()?;
52
53        let snapshots = get_snapots_from_ids(&repo, &self.ids)?;
54
55        // Handle dry-run mode
56        if config.global.dry_run {
57            println!("would have modified the following snapshots:\n {snapshots:?}");
58            return Ok(());
59        }
60
61        let snap = SnapshotFile::from_options(&self.snap_opts)?;
62        let snap = repo.merge_snapshots(&snapshots, &last_modified_node, snap)?;
63
64        if self.json {
65            let mut stdout = std::io::stdout();
66            serde_json::to_writer_pretty(&mut stdout, &snap)?;
67        }
68        info!("saved new snapshot as {}.", snap.id);
69
70        if self.delete {
71            let now = Zoned::now();
72            // TODO: Maybe use this check in repo.delete_snapshots?
73            let snap_ids: Vec<_> = snapshots
74                .iter()
75                .filter(|sn| !sn.must_keep(&now))
76                .map(|sn| sn.id)
77                .collect();
78            repo.delete_snapshots(&snap_ids)?;
79        }
80
81        Ok(())
82    }
83}