Refactor directory structure creation and enhance documentation generation
- Simplified the creation of output directory structure in `init_project` and `generate_docs` functions. - Added a `sanitize_filename` function to ensure valid filenames for generated documentation files. - Implemented individual documentation file creation for modules and files in the `generate_docs` function. - Updated links in the renderer to use the new `sanitize_for_link` function for safe URL generation. - Adjusted the `extract_docstring` method in `PythonAnalyzer` to accept the body parameter without using it, preparing for future enhancements.
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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))
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user