feat: major improvements — layout, cycles, integrations, usage examples, tests #1

Merged
dparmeev merged 15 commits from feature/improvements-v2 into main 2026-02-15 11:21:47 +03:00
3 changed files with 57 additions and 11 deletions
Showing only changes of commit df50701764 - Show all commits

View File

@@ -2,6 +2,7 @@ use clap::{Parser, Subcommand};
use anyhow::Result; use anyhow::Result;
use archdoc_core::{Config, ProjectModel, scanner::FileScanner, python_analyzer::PythonAnalyzer}; use archdoc_core::{Config, ProjectModel, scanner::FileScanner, python_analyzer::PythonAnalyzer};
use std::path::Path; use std::path::Path;
use std::collections::HashSet;
/// CLI interface for ArchDoc /// CLI interface for ArchDoc
#[derive(Parser)] #[derive(Parser)]
@@ -96,15 +97,10 @@ fn init_project(root: &str, out: &str) -> Result<()> {
std::fs::create_dir_all(out_path) std::fs::create_dir_all(out_path)
.map_err(|e| anyhow::anyhow!("Failed to create output directory: {}", e))?; .map_err(|e| anyhow::anyhow!("Failed to create output directory: {}", e))?;
// Create docs/architecture directory structure
let docs_arch_path = out_path.join("docs").join("architecture");
std::fs::create_dir_all(&docs_arch_path)
.map_err(|e| anyhow::anyhow!("Failed to create docs/architecture directory: {}", e))?;
// Create modules and files directories // Create modules and files directories
std::fs::create_dir_all(docs_arch_path.join("modules")) std::fs::create_dir_all(out_path.join("modules"))
.map_err(|e| anyhow::anyhow!("Failed to create modules directory: {}", e))?; .map_err(|e| anyhow::anyhow!("Failed to create modules directory: {}", e))?;
std::fs::create_dir_all(docs_arch_path.join("files")) std::fs::create_dir_all(out_path.join("files"))
.map_err(|e| anyhow::anyhow!("Failed to create files directory: {}", e))?; .map_err(|e| anyhow::anyhow!("Failed to create files directory: {}", e))?;
// Create default ARCHITECTURE.md template // Create default ARCHITECTURE.md template
@@ -248,7 +244,7 @@ max_cache_age = "24h"
println!("Created:"); println!("Created:");
println!(" - {}", architecture_md_path.display()); println!(" - {}", architecture_md_path.display());
println!(" - {}", config_toml_path.display()); println!(" - {}", config_toml_path.display());
println!(" - {} (directory structure)", docs_arch_path.display()); println!(" - {} (directory structure)", out_path.display());
Ok(()) Ok(())
} }
@@ -290,10 +286,33 @@ fn analyze_project(root: &str, config: &Config) -> Result<ProjectModel> {
.map_err(|e| anyhow::anyhow!("Failed to resolve symbols: {}", e)) .map_err(|e| anyhow::anyhow!("Failed to resolve symbols: {}", e))
} }
fn sanitize_filename(filename: &str) -> String {
filename
.chars()
.map(|c| match c {
'/' | '\\' | ':' | '*' | '?' | '"' | '<' | '>' | '|' => '_',
c => c,
})
.collect()
}
fn generate_docs(model: &ProjectModel, out: &str) -> Result<()> { fn generate_docs(model: &ProjectModel, out: &str) -> Result<()> {
// TODO: Implement documentation generation // TODO: Implement documentation generation
println!("Generating docs to {}", out); println!("Generating docs to {}", out);
// Create output directory structure if needed
let out_path = std::path::Path::new(out);
std::fs::create_dir_all(out_path)
.map_err(|e| anyhow::anyhow!("Failed to create output directory: {}", e))?;
// Create modules and files directories
let modules_path = out_path.join("modules");
let files_path = out_path.join("files");
std::fs::create_dir_all(&modules_path)
.map_err(|e| anyhow::anyhow!("Failed to create modules directory: {}", e))?;
std::fs::create_dir_all(&files_path)
.map_err(|e| anyhow::anyhow!("Failed to create files directory: {}", e))?;
// Initialize renderer // Initialize renderer
let renderer = archdoc_core::renderer::Renderer::new(); let renderer = archdoc_core::renderer::Renderer::new();
@@ -304,6 +323,21 @@ fn generate_docs(model: &ProjectModel, out: &str) -> Result<()> {
// The out parameter is for the docs/architecture directory structure // The out parameter is for the docs/architecture directory structure
let output_path = std::path::Path::new(".").join("ARCHITECTURE.md"); let output_path = std::path::Path::new(".").join("ARCHITECTURE.md");
// Create individual documentation files for modules and files
for (module_id, module) in &model.modules {
let module_doc_path = modules_path.join(format!("{}.md", sanitize_filename(module_id)));
let module_content = format!("# Module: {}\n\nTODO: Add module documentation\n", module_id);
std::fs::write(&module_doc_path, module_content)
.map_err(|e| anyhow::anyhow!("Failed to create module doc {}: {}", module_doc_path.display(), e))?;
}
for (file_id, file_doc) in &model.files {
let file_doc_path = files_path.join(format!("{}.md", sanitize_filename(&file_doc.path)));
let file_content = format!("# File: {}\n\nTODO: Add file documentation\n", file_doc.path);
std::fs::write(&file_doc_path, file_content)
.map_err(|e| anyhow::anyhow!("Failed to create file doc {}: {}", file_doc_path.display(), e))?;
}
// Render and update each section individually // Render and update each section individually
// Update integrations section // Update integrations section

View File

@@ -164,7 +164,7 @@ impl PythonAnalyzer {
} }
} }
fn extract_docstring(&self, body: &[Stmt]) -> Option<String> { fn extract_docstring(&self, _body: &[Stmt]) -> Option<String> {
// For now, just return None until we figure out the correct way to extract docstrings // For now, just return None until we figure out the correct way to extract docstrings
// TODO: Implement proper docstring extraction // TODO: Implement proper docstring extraction
None None
@@ -213,11 +213,13 @@ impl PythonAnalyzer {
flags flags
} }
#[allow(dead_code)]
fn extract_function_def(&self, _func_def: &StmtFunctionDef, _symbols: &mut Vec<Symbol>, _calls: &mut Vec<Call>, _depth: usize) { fn extract_function_def(&self, _func_def: &StmtFunctionDef, _symbols: &mut Vec<Symbol>, _calls: &mut Vec<Call>, _depth: usize) {
// Extract function information // Extract function information
// This is a simplified implementation - a full implementation would extract more details // This is a simplified implementation - a full implementation would extract more details
} }
#[allow(dead_code)]
fn extract_class_def(&self, _class_def: &StmtClassDef, _symbols: &mut Vec<Symbol>, _depth: usize) { fn extract_class_def(&self, _class_def: &StmtClassDef, _symbols: &mut Vec<Symbol>, _depth: usize) {
// Extract class information // Extract class information
// This is a simplified implementation - a full implementation would extract more details // This is a simplified implementation - a full implementation would extract more details

View File

@@ -6,6 +6,16 @@
use crate::model::ProjectModel; use crate::model::ProjectModel;
use handlebars::Handlebars; use handlebars::Handlebars;
fn sanitize_for_link(filename: &str) -> String {
filename
.chars()
.map(|c| match c {
'/' | '\\' | ':' | '*' | '?' | '"' | '<' | '>' | '|' => '_',
c => c,
})
.collect()
}
pub struct Renderer { pub struct Renderer {
templates: Handlebars<'static>, templates: Handlebars<'static>,
} }
@@ -243,7 +253,7 @@ impl Renderer {
layout_items.push(serde_json::json!({ layout_items.push(serde_json::json!({
"path": file_doc.path, "path": file_doc.path,
"purpose": "Source file", "purpose": "Source file",
"link": format!("docs/architecture/files/{}.md", file_id) "link": format!("docs/architecture/files/{}.md", sanitize_for_link(&file_doc.path))
})); }));
} }
@@ -280,7 +290,7 @@ impl Renderer {
"symbol_count": module.symbols.len(), "symbol_count": module.symbols.len(),
"inbound_count": module.inbound_modules.len(), "inbound_count": module.inbound_modules.len(),
"outbound_count": module.outbound_modules.len(), "outbound_count": module.outbound_modules.len(),
"link": format!("docs/architecture/modules/{}.md", module_id) "link": format!("docs/architecture/modules/{}.md", sanitize_for_link(module_id))
})); }));
} }