rustic_rs/commands/
merge.rs1use 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#[derive(clap::Parser, Default, Command, Debug)]
18pub(super) struct MergeCmd {
19 #[clap(value_name = "ID")]
21 ids: Vec<String>,
22
23 #[clap(long)]
25 json: bool,
26
27 #[clap(long)]
29 delete: bool,
30
31 #[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 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 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}