1use std::fs;
2use std::path::Path;
3
4use archidoc_types::{DriftReport, DriftedFile, ModuleDoc};
5
6pub fn check_drift(docs: &[ModuleDoc], architecture_file: &Path, root: &Path) -> DriftReport {
11 let expected = crate::architecture::generate(docs, root);
12
13 let mut report = DriftReport::default();
14
15 if !architecture_file.exists() {
16 report
17 .missing_files
18 .push("ARCHITECTURE.md".to_string());
19 return report;
20 }
21
22 let actual = fs::read_to_string(architecture_file).unwrap_or_default();
23
24 if expected != actual {
25 report.drifted_files.push(DriftedFile {
26 path: "ARCHITECTURE.md".to_string(),
27 expected_lines: expected.lines().count(),
28 actual_lines: actual.lines().count(),
29 });
30 }
31
32 report
33}
34
35pub fn format_drift_report(report: &DriftReport) -> String {
37 let mut out = String::new();
38
39 if !report.has_drift() {
40 out.push_str("Documentation is up to date.\n");
41 return out;
42 }
43
44 out.push_str("Documentation drift detected!\n\n");
45
46 if !report.drifted_files.is_empty() {
47 out.push_str(&format!(
48 "Changed files ({}):\n",
49 report.drifted_files.len()
50 ));
51 for file in &report.drifted_files {
52 out.push_str(&format!(" {} (expected {} lines, got {})\n",
53 file.path, file.expected_lines, file.actual_lines));
54 }
55 }
56
57 if !report.missing_files.is_empty() {
58 out.push_str(&format!(
59 "Missing files ({}):\n",
60 report.missing_files.len()
61 ));
62 for file in &report.missing_files {
63 out.push_str(&format!(" {}\n", file));
64 }
65 }
66
67 if !report.extra_files.is_empty() {
68 out.push_str(&format!(
69 "Extra files ({}):\n",
70 report.extra_files.len()
71 ));
72 for file in &report.extra_files {
73 out.push_str(&format!(" {}\n", file));
74 }
75 }
76
77 out.push_str("\nRun `archidoc` to regenerate.\n");
78 out
79}