rustic_rs/commands/
repoinfo.rs

1//! `repoinfo` subcommand
2
3use crate::{
4    Application, RUSTIC_APP,
5    helpers::{bytes_size_to_string, table_right_from},
6    repository::CliRepo,
7    status_err,
8};
9
10use abscissa_core::{Command, Runnable, Shutdown};
11use serde::Serialize;
12
13use anyhow::Result;
14use rustic_core::{IndexInfos, RepoFileInfo, RepoFileInfos};
15
16/// `repoinfo` subcommand
17#[derive(clap::Parser, Command, Debug)]
18pub(crate) struct RepoInfoCmd {
19    /// Only scan repository files (doesn't need repository password)
20    #[clap(long)]
21    only_files: bool,
22
23    /// Only scan index
24    #[clap(long)]
25    only_index: bool,
26
27    /// Show infos in json format
28    #[clap(long)]
29    json: bool,
30}
31
32impl Runnable for RepoInfoCmd {
33    fn run(&self) {
34        if let Err(err) = RUSTIC_APP
35            .config()
36            .repository
37            .run(|repo| self.inner_run(repo))
38        {
39            status_err!("{}", err);
40            RUSTIC_APP.shutdown(Shutdown::Crash);
41        };
42    }
43}
44
45/// Infos about the repository
46///
47/// This struct is used to serialize infos in `json` format.
48#[serde_with::apply(Option => #[serde(default, skip_serializing_if = "Option::is_none")])]
49#[derive(Serialize)]
50struct Infos {
51    files: Option<RepoFileInfos>,
52    index: Option<IndexInfos>,
53}
54
55impl RepoInfoCmd {
56    fn inner_run(&self, repo: CliRepo) -> Result<()> {
57        let infos = Infos {
58            files: (!self.only_index)
59                .then(|| -> Result<_> { Ok(repo.infos_files()?) })
60                .transpose()?,
61            index: (!self.only_files)
62                .then(|| -> Result<_> { Ok(repo.open()?.infos_index()?) })
63                .transpose()?,
64        };
65
66        if self.json {
67            let mut stdout = std::io::stdout();
68            serde_json::to_writer_pretty(&mut stdout, &infos)?;
69            return Ok(());
70        }
71
72        if let Some(file_info) = infos.files {
73            print_file_info("repository files", file_info.repo);
74            if let Some(info) = file_info.repo_hot {
75                print_file_info("hot repository files", info);
76            }
77        }
78
79        if let Some(index_info) = infos.index {
80            print_index_info(index_info);
81        }
82        Ok(())
83    }
84}
85
86/// Print infos about repository files
87///
88/// # Arguments
89///
90/// * `text` - the text to print before the table
91/// * `info` - the [`RepoFileInfo`]s to print
92pub fn print_file_info(text: &str, info: Vec<RepoFileInfo>) {
93    let mut table = table_right_from(1, ["File type", "Count", "Total Size"]);
94    let mut total_count = 0;
95    let mut total_size = 0;
96    for row in info {
97        _ = table.add_row([
98            format!("{:?}", row.tpe),
99            row.count.to_string(),
100            bytes_size_to_string(row.size),
101        ]);
102        total_count += row.count;
103        total_size += row.size;
104    }
105    println!("{text}");
106    _ = table.add_row([
107        "Total".to_string(),
108        total_count.to_string(),
109        bytes_size_to_string(total_size),
110    ]);
111
112    println!();
113    println!("{table}");
114    println!();
115}
116
117/// Print infos about index
118///
119/// # Arguments
120///
121/// * `index_info` - the [`IndexInfos`] to print
122pub fn print_index_info(index_info: IndexInfos) {
123    let mut table = table_right_from(
124        1,
125        ["Blob type", "Count", "Total Size", "Total Size in Packs"],
126    );
127
128    let mut total_count = 0;
129    let mut total_data_size = 0;
130    let mut total_size = 0;
131
132    for blobs in &index_info.blobs {
133        _ = table.add_row([
134            format!("{:?}", blobs.blob_type),
135            blobs.count.to_string(),
136            bytes_size_to_string(blobs.data_size),
137            bytes_size_to_string(blobs.size),
138        ]);
139        total_count += blobs.count;
140        total_data_size += blobs.data_size;
141        total_size += blobs.size;
142    }
143    for blobs in &index_info.blobs_delete {
144        if blobs.count > 0 {
145            _ = table.add_row([
146                format!("{:?} to delete", blobs.blob_type),
147                blobs.count.to_string(),
148                bytes_size_to_string(blobs.data_size),
149                bytes_size_to_string(blobs.size),
150            ]);
151            total_count += blobs.count;
152            total_data_size += blobs.data_size;
153            total_size += blobs.size;
154        }
155    }
156
157    _ = table.add_row([
158        "Total".to_string(),
159        total_count.to_string(),
160        bytes_size_to_string(total_data_size),
161        bytes_size_to_string(total_size),
162    ]);
163
164    println!();
165    println!("{table}");
166
167    let mut table = table_right_from(
168        1,
169        ["Blob type", "Pack Count", "Minimum Size", "Maximum Size"],
170    );
171
172    for packs in index_info.packs {
173        _ = table.add_row([
174            format!("{:?} packs", packs.blob_type),
175            packs.count.to_string(),
176            packs
177                .min_size
178                .map_or_else(|| "-".to_string(), bytes_size_to_string),
179            packs
180                .max_size
181                .map_or_else(|| "-".to_string(), bytes_size_to_string),
182        ]);
183    }
184    for packs in index_info.packs_delete {
185        if packs.count > 0 {
186            _ = table.add_row([
187                format!("{:?} packs to delete", packs.blob_type),
188                packs.count.to_string(),
189                packs
190                    .min_size
191                    .map_or_else(|| "-".to_string(), bytes_size_to_string),
192                packs
193                    .max_size
194                    .map_or_else(|| "-".to_string(), bytes_size_to_string),
195            ]);
196        }
197    }
198    println!();
199    println!("{table}");
200}