heddle_cli_render/cli/render/
fsck.rs1use anyhow::Result;
3use verbs::FsckReport;
4
5use crate::cli::{render::write_stdout, style};
6
7pub fn fsck_json(report: &FsckReport) -> Result<()> {
8 let mut text = serde_json::to_string(report)?;
9 text.push('\n');
10 write_stdout(&text)
11}
12
13pub fn fsck_text(report: &FsckReport) -> Result<()> {
14 write_stdout(&format_fsck_text(report))
15}
16
17fn format_fsck_text(report: &FsckReport) -> String {
18 let mut text = String::new();
19 if report.valid {
20 let counted = style::count(report.objects_checked, "object");
21 text.push_str(&format!(
22 "{} repository is valid ({counted} checked)\n",
23 style::ok_marker(),
24 ));
25 if report.git_projection_checked {
26 text.push_str(&format!(
27 " {}\n",
28 style::field("Git projection", "mapping, notes, and checkout checked")
29 ));
30 }
31 } else {
32 text.push_str(&format!(
33 "{} repository has {}\n",
34 style::error_marker(),
35 style::count(report.errors.len(), "integrity error")
36 ));
37 for error in &report.errors {
38 if let Some(obj) = &error.object {
39 text.push_str(&format!(
40 " {} {} {}\n",
41 style::error(&format!("[{}]", error.kind)),
42 error.message,
43 style::dim(&format!("({obj})"))
44 ));
45 } else {
46 text.push_str(&format!(
47 " {} {}\n",
48 style::error(&format!("[{}]", error.kind)),
49 error.message
50 ));
51 }
52 }
53 }
54 if let Some(target) = &report.repair_target {
55 let status = if report.repaired {
56 "repaired"
57 } else {
58 "no changes"
59 };
60 text.push_str(&format!(
61 " {}\n",
62 style::field("Repair", &format!("{target}: {status}"))
63 ));
64 for repair in &report.repairs {
65 if repair.count > 0 || repair.repaired {
66 text.push_str(&format!(
67 " {} {} ({})\n",
68 repair.name, repair.detail, repair.count
69 ));
70 }
71 }
72 }
73 if let Some(provenance) = &report.provenance {
74 text.push_str(&format!(
75 " {}\n",
76 style::field("Provenance registry", &provenance.registry_status)
77 ));
78 for state in &provenance.states {
79 let short = state
80 .state_id
81 .strip_prefix("hs-")
82 .unwrap_or(&state.state_id);
83 let short = &short[..short.len().min(12)];
84 text.push_str(&format!(
85 " {short} {:<24} {}\n",
86 state.display_status(),
87 state.detail
88 ));
89 }
90 }
91 for warning in &report.warnings {
92 text.push_str(&format!("{} {}\n", style::warn_marker(), warning));
93 }
94 text
95}
96
97#[cfg(test)]
98mod tests {
99 use super::*;
100
101 #[test]
102 fn text_renderer_consumes_the_typed_fsck_report() {
103 let report = FsckReport {
104 valid: true,
105 errors: Vec::new(),
106 warnings: vec!["legacy object retained".to_string()],
107 objects_checked: 2,
108 git_projection_checked: false,
109 provenance: None,
110 repair_target: None,
111 repaired: false,
112 repairs: Vec::new(),
113 };
114
115 let text = format_fsck_text(&report);
116 assert!(text.contains("repository is valid (2 objects checked)"));
117 assert!(text.contains("legacy object retained"));
118 }
119}