ferrous_forge/safety/checks/
doc.rs1use crate::Result;
4use std::path::Path;
5use std::time::Instant;
6
7use super::SafetyCheck;
8use crate::safety::{CheckType, report::CheckResult};
9
10pub struct DocCheck;
12
13impl SafetyCheck for DocCheck {
14 async fn run(project_path: &Path) -> Result<CheckResult> {
15 run(project_path).await
16 }
17
18 fn name() -> &'static str {
19 "doc"
20 }
21
22 fn description() -> &'static str {
23 "Builds project documentation"
24 }
25}
26
27pub async fn run(project_path: &Path) -> Result<CheckResult> {
33 let start = Instant::now();
34 let mut result = CheckResult::new(CheckType::Doc);
35
36 match tokio::process::Command::new("cargo")
37 .args(["doc", "--no-deps"])
38 .current_dir(project_path)
39 .output()
40 .await
41 {
42 Ok(output) => {
43 if !output.status.success() {
44 let stderr = String::from_utf8_lossy(&output.stderr);
45 result.add_error(&format!("cargo doc failed: {stderr}"));
46 result.fail();
47 } else {
48 result.add_context("Documentation built successfully");
49 }
50 }
51 Err(e) => {
52 result.add_error(&format!("Failed to execute cargo doc: {e}"));
53 result.fail();
54 }
55 }
56
57 result.set_duration(start.elapsed());
58 Ok(result)
59}
60
61pub async fn coverage_check(project_path: &Path) -> Result<CheckResult> {
67 let start = Instant::now();
68 let mut result = CheckResult::new(CheckType::DocCoverage);
69
70 let coverage = crate::doc_coverage::check_documentation_coverage(project_path).await?;
71 let threshold = 50.0;
72
73 if !coverage.meets_threshold(threshold) {
74 result.add_error(&format!(
75 "Documentation coverage {:.1}% is below the {:.0}% threshold",
76 coverage.coverage_percent, threshold
77 ));
78 result.fail();
79 }
80
81 result.add_context(&format!(
82 "Documentation coverage: {:.1}% ({}/{} items documented)",
83 coverage.coverage_percent, coverage.documented_items, coverage.total_items
84 ));
85
86 result.set_duration(start.elapsed());
87 Ok(result)
88}