rustic_rs/commands/
merge.rs

1//! `merge` subcommand
2
3use crate::{
4    Application, RUSTIC_APP,
5    repository::{CliOpenRepo, get_filtered_snapshots},
6    status_err,
7};
8use abscissa_core::{Command, Runnable, Shutdown};
9use anyhow::Result;
10use log::info;
11
12use chrono::Local;
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: CliOpenRepo) -> Result<()> {
51        let config = RUSTIC_APP.config();
52        let repo = repo.to_indexed_ids()?;
53
54        let snapshots = if self.ids.is_empty() {
55            get_filtered_snapshots(&repo)?
56        } else {
57            repo.get_snapshots(&self.ids)?
58        };
59
60        // Handle dry-run mode
61        if config.global.dry_run {
62            println!("would have modified the following snapshots:\n {snapshots:?}");
63            return Ok(());
64        }
65
66        let snap = SnapshotFile::from_options(&self.snap_opts)?;
67        let snap = repo.merge_snapshots(&snapshots, &last_modified_node, snap)?;
68
69        if self.json {
70            let mut stdout = std::io::stdout();
71            serde_json::to_writer_pretty(&mut stdout, &snap)?;
72        }
73        info!("saved new snapshot as {}.", snap.id);
74
75        if self.delete {
76            let now = Local::now();
77            // TODO: Maybe use this check in repo.delete_snapshots?
78            let snap_ids: Vec<_> = snapshots
79                .iter()
80                .filter(|sn| !sn.must_keep(now))
81                .map(|sn| sn.id)
82                .collect();
83            repo.delete_snapshots(&snap_ids)?;
84        }
85
86        Ok(())
87    }
88}