rustic_rs/commands/
prune.rs1use crate::{
4 Application, RUSTIC_APP, helpers::bytes_size_to_string, repository::CliOpenRepo, status_err,
5};
6use abscissa_core::{Command, Runnable, Shutdown};
7use log::{debug, info};
8
9use anyhow::Result;
10
11use rustic_core::{PruneOptions, PruneStats};
12
13#[allow(clippy::struct_excessive_bools)]
15#[derive(clap::Parser, Command, Debug, Clone)]
16pub(crate) struct PruneCmd {
17 #[clap(flatten)]
19 pub(crate) opts: PruneOptions,
20}
21
22impl Runnable for PruneCmd {
23 fn run(&self) {
24 if let Err(err) = RUSTIC_APP
25 .config()
26 .repository
27 .run_open(|repo| self.inner_run(repo))
28 {
29 status_err!("{}", err);
30 RUSTIC_APP.shutdown(Shutdown::Crash);
31 };
32 }
33}
34
35impl PruneCmd {
36 fn inner_run(&self, repo: CliOpenRepo) -> Result<()> {
37 let config = RUSTIC_APP.config();
38
39 let prune_plan = repo.prune_plan(&self.opts)?;
40
41 print_stats(&prune_plan.stats);
42
43 let dry_run = config.global.dry_run;
44 if dry_run && config.global.dry_run_warmup {
45 repo.warm_up(prune_plan.repack_packs().into_iter())?;
46 } else if !config.global.dry_run_warmup {
47 debug!("Ignoring --dry-run-warmup works only in combination with --dry-run");
48 }
49 if !dry_run {
50 repo.prune(&self.opts, prune_plan)?;
51 }
52
53 Ok(())
54 }
55}
56
57#[allow(clippy::cast_precision_loss)]
63fn print_stats(stats: &PruneStats) {
64 let pack_stat = &stats.packs;
65 let blob_stat = stats.blobs_sum();
66 let size_stat = stats.size_sum();
67
68 debug!("statistics:");
69 debug!("{:#?}", stats.debug);
70
71 debug!(
72 "used: {:>10} blobs, {:>10}",
73 blob_stat.used,
74 bytes_size_to_string(size_stat.used)
75 );
76
77 debug!(
78 "unused: {:>10} blobs, {:>10}",
79 blob_stat.unused,
80 bytes_size_to_string(size_stat.unused)
81 );
82 debug!(
83 "total: {:>10} blobs, {:>10}",
84 blob_stat.total(),
85 bytes_size_to_string(size_stat.total())
86 );
87
88 info!(
89 "to repack: {:>10} packs, {:>10} blobs, {:>10}",
90 pack_stat.repack,
91 blob_stat.repack,
92 bytes_size_to_string(size_stat.repack)
93 );
94 info!(
95 "this removes: {:>10} blobs, {:>10}",
96 blob_stat.repackrm,
97 bytes_size_to_string(size_stat.repackrm)
98 );
99 info!(
100 "to delete: {:>10} packs, {:>10} blobs, {:>10}",
101 pack_stat.unused,
102 blob_stat.remove,
103 bytes_size_to_string(size_stat.remove)
104 );
105 if stats.packs_unref > 0 {
106 info!(
107 "unindexed: {:>10} packs, ?? blobs, {:>10}",
108 stats.packs_unref,
109 bytes_size_to_string(stats.size_unref)
110 );
111 }
112
113 info!(
114 "total prune: {:>10} blobs, {:>10}",
115 blob_stat.repackrm + blob_stat.remove,
116 bytes_size_to_string(size_stat.repackrm + size_stat.remove + stats.size_unref)
117 );
118 info!(
119 "remaining: {:>10} blobs, {:>10}",
120 blob_stat.total_after_prune(),
121 bytes_size_to_string(size_stat.total_after_prune())
122 );
123 info!(
124 "unused size after prune: {:>10} ({:.2}% of remaining size)",
125 bytes_size_to_string(size_stat.unused_after_prune()),
126 size_stat.unused_after_prune() as f64 / size_stat.total_after_prune() as f64 * 100.0
127 );
128
129 info!(
130 "packs marked for deletion: {:>10}, {:>10}",
131 stats.packs_to_delete.total(),
132 bytes_size_to_string(stats.size_to_delete.total()),
133 );
134 info!(
135 " - complete deletion: {:>10}, {:>10}",
136 stats.packs_to_delete.remove,
137 bytes_size_to_string(stats.size_to_delete.remove),
138 );
139 info!(
140 " - keep marked: {:>10}, {:>10}",
141 stats.packs_to_delete.keep,
142 bytes_size_to_string(stats.size_to_delete.keep),
143 );
144 info!(
145 " - recover: {:>10}, {:>10}",
146 stats.packs_to_delete.recover,
147 bytes_size_to_string(stats.size_to_delete.recover),
148 );
149
150 debug!(
151 "index files to rebuild: {} / {}",
152 stats.index_files_rebuild, stats.index_files
153 );
154}