Skip to main content

rustic_rs/commands/
merge.rs

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