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

@@ -10,7 +10,7 @@ pub fn check_docs_consistency(root: &str, config: &Config) -> Result<()> {
let model = analyze_project(root, config)?;
let renderer = archdoc_core::renderer::Renderer::new();
let _generated = renderer.render_architecture_md(&model)?;
let _generated = renderer.render_architecture_md(&model, None)?;
let architecture_md_path = std::path::Path::new(root).join(&config.project.entry_file);
if !architecture_md_path.exists() {

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);

View File

@@ -34,6 +34,9 @@ enum Commands {
out: String,
#[arg(short, long, default_value = "archdoc.toml")]
config: String,
/// Show what would be generated without writing files
#[arg(long)]
dry_run: bool,
},
/// Check if documentation is up to date
Check {
@@ -58,10 +61,14 @@ fn main() -> Result<()> {
Commands::Init { root, out } => {
commands::init::init_project(root, out)?;
}
Commands::Generate { root, out, config } => {
Commands::Generate { root, out, config, dry_run } => {
let config = commands::generate::load_config(config)?;
let model = commands::generate::analyze_project(root, &config)?;
commands::generate::generate_docs(&model, out, cli.verbose)?;
if *dry_run {
commands::generate::dry_run_docs(&model, out, &config)?;
} else {
commands::generate::generate_docs(&model, out, cli.verbose, &config)?;
}
output::print_generate_summary(&model);
}
Commands::Check { root, config } => {