The bead description mentioned compile errors in hash.rs from API drift, but those errors were either already fixed or misattributed. The API usage was already correct: - compute_fingerprint already takes 3 arguments with source - len() already propagates Result with ? - read_at method already used correctly - Catalog fields accessed via trailer correctly Only cleanup: removed unused std::fs::File and std::io imports. Verification: notes/bf-4mkhv.md
120 lines
3.7 KiB
Rust
Executable file
120 lines
3.7 KiB
Rust
Executable file
#!/usr/bin/env rust-script
|
|
//! Measure rustdoc coverage for pdftract-core public API.
|
|
|
|
use std::fs;
|
|
use std::path::Path;
|
|
|
|
#[derive(Default)]
|
|
struct DocStats {
|
|
total_items: usize,
|
|
with_docs: usize,
|
|
with_examples: usize,
|
|
modules: usize,
|
|
structs: usize,
|
|
enums: usize,
|
|
traits: usize,
|
|
functions: usize,
|
|
types: usize,
|
|
}
|
|
|
|
impl DocStats {
|
|
fn coverage(&self) -> f64 {
|
|
if self.total_items == 0 {
|
|
0.0
|
|
} else {
|
|
(self.with_examples as f64 / self.total_items as f64) * 100.0
|
|
}
|
|
}
|
|
}
|
|
|
|
fn scan_file(path: &Path, stats: &mut DocStats) {
|
|
let content = match fs::read_to_string(path) {
|
|
Ok(c) => c,
|
|
Err(_) => return,
|
|
};
|
|
|
|
let lines: Vec<&str> = content.lines().collect();
|
|
|
|
for (i, line) in lines.iter().enumerate() {
|
|
let line = line.trim();
|
|
|
|
// Look for doc comments before public items
|
|
let mut has_doc = false;
|
|
let mut has_example = false;
|
|
|
|
// Scan backward for doc comments
|
|
if i > 0 {
|
|
for j in (0..i).rev() {
|
|
let prev_line = lines[j].trim();
|
|
if prev_line.starts_with("///") || prev_line.starts_with("//!") {
|
|
has_doc = true;
|
|
if prev_line.contains("```") && (prev_line.contains("rust") || prev_line.contains("no_run")) {
|
|
has_example = true;
|
|
}
|
|
} else if !prev_line.is_empty() && !prev_line.starts_with("//") && !prev_line.starts_with("#[") {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Count public items
|
|
if line.starts_with("pub ") && !line.starts_with("pub(crate)") {
|
|
if line.contains("fn ") {
|
|
stats.functions += 1;
|
|
} else if line.contains("struct ") {
|
|
stats.structs += 1;
|
|
} else if line.contains("enum ") {
|
|
stats.enums += 1;
|
|
} else if line.contains("trait ") {
|
|
stats.traits += 1;
|
|
} else if line.contains("type ") {
|
|
stats.types += 1;
|
|
} else if line.contains("mod ") {
|
|
stats.modules += 1;
|
|
}
|
|
|
|
stats.total_items += 1;
|
|
if has_doc {
|
|
stats.with_docs += 1;
|
|
}
|
|
if has_example {
|
|
stats.with_examples += 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn scan_directory(dir: &Path, stats: &mut DocStats) {
|
|
if let Ok(entries) = fs::read_dir(dir) {
|
|
for entry in entries.flatten() {
|
|
let path = entry.path();
|
|
if path.is_dir() {
|
|
scan_directory(&path, stats);
|
|
} else if path.extension().map(|e| e == "rs").unwrap_or(false) {
|
|
scan_file(&path, stats);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn main() {
|
|
let mut stats = DocStats::default();
|
|
scan_directory(Path::new("crates/pdftract-core/src"), &mut stats);
|
|
|
|
println!("\n=== Rustdoc Coverage Report ===\n");
|
|
println!("Total public items: {}", stats.total_items);
|
|
println!("With docs: {} ({:.1}%)", stats.with_docs,
|
|
(stats.with_docs as f64 / stats.total_items as f64) * 100.0);
|
|
println!("With examples: {} ({:.1}%)", stats.with_examples,
|
|
(stats.with_examples as f64 / stats.total_items as f64) * 100.0);
|
|
println!("\nBy type:");
|
|
println!(" Modules: {}", stats.modules);
|
|
println!(" Structs: {}", stats.structs);
|
|
println!(" Enums: {}", stats.enums);
|
|
println!(" Traits: {}", stats.traits);
|
|
println!(" Functions: {}", stats.functions);
|
|
println!(" Types: {}", stats.types);
|
|
println!("\nTarget: 80%+ coverage");
|
|
println!("Status: {}", if stats.coverage() >= 80.0 { "✓ PASS" } else { "✗ FAIL" });
|
|
println!("Current: {:.1}%", stats.coverage());
|
|
}
|