feat: use actual project data, real usage examples, dry-run/verbose flags, skip-unchanged optimization

- renderer: render_architecture_md accepts Config, uses project name and current date
- renderer: generate real Python usage examples from analyzed symbols
- writer: skip writing files when content unchanged (optimization)
- cli: add --dry-run flag to generate command (lists files without writing)
- cli: add verbose logging for file/module/symbol generation progress
This commit is contained in:
2026-02-15 03:32:10 +03:00
parent df52f80999
commit 25fdf400fa
7 changed files with 135 additions and 28 deletions

View File

@@ -55,7 +55,46 @@ pub fn analyze_project(root: &str, config: &Config) -> Result<ProjectModel> {
Ok(model)
}
pub fn generate_docs(model: &ProjectModel, out: &str, verbose: bool) -> Result<()> {
pub fn dry_run_docs(model: &ProjectModel, out: &str, config: &Config) -> Result<()> {
println!("{}", "Dry run — no files will be written.".cyan().bold());
println!();
let out_path = std::path::Path::new(out);
let arch_path = std::path::Path::new(".").join("ARCHITECTURE.md");
// ARCHITECTURE.md
let exists = arch_path.exists();
println!(" {} {}", if exists { "UPDATE" } else { "CREATE" }, arch_path.display());
// layout.md
let layout_path = out_path.join("layout.md");
let exists = layout_path.exists();
println!(" {} {}", if exists { "UPDATE" } else { "CREATE" }, layout_path.display());
// Module docs
for module_id in model.modules.keys() {
let p = out_path.join("modules").join(format!("{}.md", sanitize_filename(module_id)));
let exists = p.exists();
println!(" {} {}", if exists { "UPDATE" } else { "CREATE" }, p.display());
}
// File docs
for file_doc in model.files.values() {
let p = out_path.join("files").join(format!("{}.md", sanitize_filename(&file_doc.path)));
let exists = p.exists();
println!(" {} {}", if exists { "UPDATE" } else { "CREATE" }, p.display());
}
let _ = config; // used for future extensions
println!();
println!("{} {} file(s) would be generated/updated",
"".green().bold(),
2 + model.modules.len() + model.files.len());
Ok(())
}
pub fn generate_docs(model: &ProjectModel, out: &str, verbose: bool, _config: &Config) -> Result<()> {
println!("{}", "Generating documentation...".cyan());
let out_path = std::path::Path::new(out);
@@ -74,6 +113,9 @@ pub fn generate_docs(model: &ProjectModel, out: &str, verbose: bool) -> Result<(
// Generate module docs
for module_id in model.modules.keys() {
let module_doc_path = modules_path.join(format!("{}.md", sanitize_filename(module_id)));
if verbose {
println!(" Generating module doc: {}", module_id);
}
match renderer.render_module_md(model, module_id) {
Ok(module_content) => {
std::fs::write(&module_doc_path, module_content)?;
@@ -88,6 +130,9 @@ pub fn generate_docs(model: &ProjectModel, out: &str, verbose: bool) -> Result<(
// Generate file docs
for file_doc in model.files.values() {
if verbose {
println!(" Generating file doc: {}", file_doc.path);
}
let file_doc_path = files_path.join(format!("{}.md", sanitize_filename(&file_doc.path)));
let mut file_content = format!("# File: {}\n\n", file_doc.path);