1use std::borrow::Cow;
20use std::io::{BufWriter, Write};
21use std::sync::Arc;
22
23use time::format_description::well_known::Rfc3339;
24use time::UtcOffset;
25use tracing::error;
26
27use crate::misc::duration_to_hms;
28use crate::termui::TermUiMonitor;
29use crate::*;
30
31#[derive(Default, Clone, Eq, PartialEq)]
33pub struct ShowVersionsOptions {
34 pub newest_first: bool,
36 pub tree_size: bool,
39 pub start_time: bool,
41 pub backup_duration: bool,
43 pub timezone: Option<UtcOffset>,
45}
46
47pub fn show_versions(
49 archive: &Archive,
50 options: &ShowVersionsOptions,
51 monitor: Arc<TermUiMonitor>,
52) -> Result<()> {
53 let mut band_ids = archive.list_band_ids()?;
54 if options.newest_first {
55 band_ids.reverse();
56 }
57 for band_id in band_ids {
58 if !(options.tree_size || options.start_time || options.backup_duration) {
59 println!("{}", band_id);
60 continue;
61 }
62 let mut l: Vec<String> = Vec::new();
63 l.push(format!("{band_id:<20}"));
64 let band = match Band::open(archive, band_id) {
65 Ok(band) => band,
66 Err(err) => {
67 error!("Failed to open band {band_id:?}: {err}");
68 continue;
69 }
70 };
71 let info = match band.get_info() {
72 Ok(info) => info,
73 Err(err) => {
74 error!("Failed to read band tail {band_id:?}: {err}");
75 continue;
76 }
77 };
78
79 if options.start_time {
80 let mut start_time = info.start_time;
81 if let Some(timezone) = options.timezone {
82 start_time = start_time.to_offset(timezone);
83 }
84 l.push(format!(
85 "{date:<25}", date = start_time.format(&Rfc3339).unwrap(),
87 ));
88 }
89
90 if options.backup_duration {
91 let duration_str: Cow<str> = if info.is_closed {
92 if let Some(end_time) = info.end_time {
93 let duration = end_time - info.start_time;
94 if let Ok(duration) = duration.try_into() {
95 duration_to_hms(duration).into()
96 } else {
97 Cow::Borrowed("negative")
98 }
99 } else {
100 Cow::Borrowed("unknown")
101 }
102 } else {
103 Cow::Borrowed("incomplete")
104 };
105 l.push(format!("{duration_str:>10}"));
106 }
107
108 if options.tree_size {
109 let tree_mb_str = crate::misc::bytes_to_human_mb(
110 archive
111 .open_stored_tree(BandSelectionPolicy::Specified(band_id))?
112 .size(Exclude::nothing(), monitor.clone())?
113 .file_bytes,
114 );
115 l.push(format!("{tree_mb_str:>14}",));
116 }
117 monitor.clear_progress_bars(); println!("{}", l.join(" "));
119 }
120 Ok(())
121}
122
123pub fn show_index_json(band: &Band, w: &mut dyn Write) -> Result<()> {
124 let bw = BufWriter::new(w);
126 let index_entries: Vec<IndexEntry> = band.index().iter_entries().collect();
127 serde_json::ser::to_writer_pretty(bw, &index_entries)
128 .map_err(|source| Error::SerializeJson { source })
129}
130
131pub fn show_entry_names<E: EntryTrait, I: Iterator<Item = E>>(
132 it: I,
133 w: &mut dyn Write,
134 long_listing: bool,
135) -> Result<()> {
136 let mut bw = BufWriter::new(w);
137 for entry in it {
138 if long_listing {
139 writeln!(
140 bw,
141 "{} {} {}",
142 entry.unix_mode(),
143 entry.owner(),
144 entry.apath()
145 )?;
146 } else {
147 writeln!(bw, "{}", entry.apath())?;
148 }
149 }
150 Ok(())
151}