feat(renderer): implement module-level documentation generation
- Added module_md template to renderer for generating detailed module documentation - Updated CLI to use renderer for module docs with fallback to simple template - Generated module documentation for test project files with symbols, dependencies, and integrations - Added proper error handling when module rendering fails This implements the core functionality for generating detailed architectural documentation at the module level, including symbols, dependencies, and integration points.
This commit is contained in:
0
archdoc-cli/ARCHITECTURE.md
Normal file
0
archdoc-cli/ARCHITECTURE.md
Normal file
@@ -325,10 +325,20 @@ fn generate_docs(model: &ProjectModel, out: &str) -> Result<()> {
|
||||
// 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)));
|
||||
match renderer.render_module_md(model, module_id) {
|
||||
Ok(module_content) => {
|
||||
std::fs::write(&module_doc_path, module_content)
|
||||
.map_err(|e| anyhow::anyhow!("Failed to create module doc {}: {}", module_doc_path.display(), e))?;
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("Warning: Failed to render module doc for {}: {}", module_id, e);
|
||||
// Fallback to simple template
|
||||
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)));
|
||||
|
||||
@@ -28,7 +28,9 @@ impl Renderer {
|
||||
handlebars.register_template_string("architecture_md", Self::architecture_md_template())
|
||||
.expect("Failed to register architecture_md template");
|
||||
|
||||
// TODO: Register other templates
|
||||
// Register module documentation template
|
||||
handlebars.register_template_string("module_md", Self::module_md_template())
|
||||
.expect("Failed to register module_md template");
|
||||
|
||||
Self {
|
||||
templates: handlebars,
|
||||
@@ -151,6 +153,82 @@ impl Renderer {
|
||||
"#
|
||||
}
|
||||
|
||||
fn module_md_template() -> &'static str {
|
||||
r#"# Module: {{{module_name}}}
|
||||
|
||||
{{{module_summary}}}
|
||||
|
||||
## Symbols
|
||||
|
||||
{{#each symbols}}
|
||||
### {{{name}}}
|
||||
|
||||
{{{signature}}}
|
||||
|
||||
{{{docstring}}}
|
||||
|
||||
**Type:** {{{kind}}}
|
||||
|
||||
**Metrics:**
|
||||
- Fan-in: {{{fan_in}}}
|
||||
- Fan-out: {{{fan_out}}}
|
||||
{{#if is_critical}}
|
||||
- Critical: Yes
|
||||
{{/if}}
|
||||
|
||||
{{/each}}
|
||||
|
||||
## Dependencies
|
||||
|
||||
### Imports
|
||||
{{#each imports}}
|
||||
- {{{this}}}
|
||||
{{/each}}
|
||||
|
||||
### Outbound Modules
|
||||
{{#each outbound_modules}}
|
||||
- {{{this}}}
|
||||
{{/each}}
|
||||
|
||||
### Inbound Modules
|
||||
{{#each inbound_modules}}
|
||||
- {{{this}}}
|
||||
{{/each}}
|
||||
|
||||
## Integrations
|
||||
|
||||
{{#if has_db_integrations}}
|
||||
### Database Integrations
|
||||
{{#each db_symbols}}
|
||||
- {{{this}}}
|
||||
{{/each}}
|
||||
{{/if}}
|
||||
|
||||
{{#if has_http_integrations}}
|
||||
### HTTP/API Integrations
|
||||
{{#each http_symbols}}
|
||||
- {{{this}}}
|
||||
{{/each}}
|
||||
{{/if}}
|
||||
|
||||
{{#if has_queue_integrations}}
|
||||
### Queue Integrations
|
||||
{{#each queue_symbols}}
|
||||
- {{{this}}}
|
||||
{{/each}}
|
||||
{{/if}}
|
||||
|
||||
## Usage Examples
|
||||
|
||||
{{#each usage_examples}}
|
||||
```python
|
||||
{{{this}}}
|
||||
```
|
||||
|
||||
{{/each}}
|
||||
"#
|
||||
}
|
||||
|
||||
pub fn render_architecture_md(&self, model: &ProjectModel) -> Result<String, anyhow::Error> {
|
||||
// Collect integration information
|
||||
let mut db_integrations = Vec::new();
|
||||
@@ -188,6 +266,72 @@ impl Renderer {
|
||||
.map_err(|e| anyhow::anyhow!("Failed to render architecture.md: {}", e))
|
||||
}
|
||||
|
||||
pub fn render_module_md(&self, model: &ProjectModel, module_id: &str) -> Result<String, anyhow::Error> {
|
||||
// Find the module in the project model
|
||||
let module = model.modules.get(module_id)
|
||||
.ok_or_else(|| anyhow::anyhow!("Module {} not found", module_id))?;
|
||||
|
||||
// Collect symbols for this module
|
||||
let mut symbols = Vec::new();
|
||||
for symbol_id in &module.symbols {
|
||||
if let Some(symbol) = model.symbols.get(symbol_id) {
|
||||
symbols.push(serde_json::json!({
|
||||
"name": symbol.qualname,
|
||||
"signature": symbol.signature,
|
||||
"docstring": symbol.docstring_first_line.as_deref().unwrap_or("No documentation available"),
|
||||
"kind": format!("{:?}", symbol.kind),
|
||||
"fan_in": symbol.metrics.fan_in,
|
||||
"fan_out": symbol.metrics.fan_out,
|
||||
"is_critical": symbol.metrics.is_critical,
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
// Collect integration information for this module
|
||||
let mut db_symbols = Vec::new();
|
||||
let mut http_symbols = Vec::new();
|
||||
let mut queue_symbols = Vec::new();
|
||||
|
||||
for symbol_id in &module.symbols {
|
||||
if let Some(symbol) = model.symbols.get(symbol_id) {
|
||||
if symbol.integrations_flags.db {
|
||||
db_symbols.push(symbol.qualname.clone());
|
||||
}
|
||||
if symbol.integrations_flags.http {
|
||||
http_symbols.push(symbol.qualname.clone());
|
||||
}
|
||||
if symbol.integrations_flags.queue {
|
||||
queue_symbols.push(symbol.qualname.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Prepare usage examples (for now, just placeholders)
|
||||
let usage_examples = vec![
|
||||
"// Example usage of module functions\n// TODO: Add real usage examples based on module analysis".to_string()
|
||||
];
|
||||
|
||||
// Prepare data for template
|
||||
let data = serde_json::json!({
|
||||
"module_name": module_id,
|
||||
"module_summary": module.doc_summary.as_deref().unwrap_or("No summary available"),
|
||||
"symbols": symbols,
|
||||
"imports": model.files.get(&module.files[0]).map(|f| f.imports.clone()).unwrap_or_default(),
|
||||
"outbound_modules": module.outbound_modules,
|
||||
"inbound_modules": module.inbound_modules,
|
||||
"has_db_integrations": !db_symbols.is_empty(),
|
||||
"has_http_integrations": !http_symbols.is_empty(),
|
||||
"has_queue_integrations": !queue_symbols.is_empty(),
|
||||
"db_symbols": db_symbols,
|
||||
"http_symbols": http_symbols,
|
||||
"queue_symbols": queue_symbols,
|
||||
"usage_examples": usage_examples,
|
||||
});
|
||||
|
||||
self.templates.render("module_md", &data)
|
||||
.map_err(|e| anyhow::anyhow!("Failed to render module.md: {}", e))
|
||||
}
|
||||
|
||||
pub fn render_integrations_section(&self, model: &ProjectModel) -> Result<String, anyhow::Error> {
|
||||
// Collect integration information
|
||||
let mut db_integrations = Vec::new();
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
# File: ../test-project/src/__init__.py
|
||||
|
||||
TODO: Add file documentation
|
||||
@@ -0,0 +1,3 @@
|
||||
# File: ../test-project/src/core.py
|
||||
|
||||
TODO: Add file documentation
|
||||
@@ -0,0 +1,3 @@
|
||||
# File: ../test-project/src/utils.py
|
||||
|
||||
TODO: Add file documentation
|
||||
@@ -0,0 +1,27 @@
|
||||
# Module: ../test-project/src/__init__.py
|
||||
|
||||
No summary available
|
||||
|
||||
## Symbols
|
||||
|
||||
|
||||
## Dependencies
|
||||
|
||||
### Imports
|
||||
|
||||
### Outbound Modules
|
||||
|
||||
### Inbound Modules
|
||||
|
||||
## Integrations
|
||||
|
||||
|
||||
|
||||
|
||||
## Usage Examples
|
||||
|
||||
```python
|
||||
// Example usage of module functions
|
||||
// TODO: Add real usage examples based on module analysis
|
||||
```
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
# Module: ../test-project/src/core.py
|
||||
|
||||
No summary available
|
||||
|
||||
## Symbols
|
||||
|
||||
### DatabaseManager
|
||||
|
||||
class DatabaseManager
|
||||
|
||||
No documentation available
|
||||
|
||||
**Type:** Class
|
||||
|
||||
**Metrics:**
|
||||
- Fan-in: 0
|
||||
- Fan-out: 0
|
||||
|
||||
### __init__
|
||||
|
||||
def __init__(...)
|
||||
|
||||
No documentation available
|
||||
|
||||
**Type:** Function
|
||||
|
||||
**Metrics:**
|
||||
- Fan-in: 0
|
||||
- Fan-out: 0
|
||||
|
||||
### connect
|
||||
|
||||
def connect(...)
|
||||
|
||||
No documentation available
|
||||
|
||||
**Type:** Function
|
||||
|
||||
**Metrics:**
|
||||
- Fan-in: 0
|
||||
- Fan-out: 0
|
||||
|
||||
### execute_query
|
||||
|
||||
def execute_query(...)
|
||||
|
||||
No documentation available
|
||||
|
||||
**Type:** Function
|
||||
|
||||
**Metrics:**
|
||||
- Fan-in: 0
|
||||
- Fan-out: 0
|
||||
|
||||
### fetch_external_data
|
||||
|
||||
def fetch_external_data(...)
|
||||
|
||||
No documentation available
|
||||
|
||||
**Type:** Function
|
||||
|
||||
**Metrics:**
|
||||
- Fan-in: 0
|
||||
- Fan-out: 0
|
||||
|
||||
### process_user_data
|
||||
|
||||
def process_user_data(...)
|
||||
|
||||
No documentation available
|
||||
|
||||
**Type:** Function
|
||||
|
||||
**Metrics:**
|
||||
- Fan-in: 0
|
||||
- Fan-out: 1
|
||||
|
||||
|
||||
## Dependencies
|
||||
|
||||
### Imports
|
||||
- sqlite3
|
||||
- requests
|
||||
|
||||
### Outbound Modules
|
||||
|
||||
### Inbound Modules
|
||||
|
||||
## Integrations
|
||||
|
||||
### Database Integrations
|
||||
- DatabaseManager
|
||||
- connect
|
||||
|
||||
### HTTP/API Integrations
|
||||
- fetch_external_data
|
||||
|
||||
|
||||
## Usage Examples
|
||||
|
||||
```python
|
||||
// Example usage of module functions
|
||||
// TODO: Add real usage examples based on module analysis
|
||||
```
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
# Module: ../test-project/src/utils.py
|
||||
|
||||
No summary available
|
||||
|
||||
## Symbols
|
||||
|
||||
### load_config
|
||||
|
||||
def load_config(...)
|
||||
|
||||
No documentation available
|
||||
|
||||
**Type:** Function
|
||||
|
||||
**Metrics:**
|
||||
- Fan-in: 0
|
||||
- Fan-out: 0
|
||||
|
||||
### save_config
|
||||
|
||||
def save_config(...)
|
||||
|
||||
No documentation available
|
||||
|
||||
**Type:** Function
|
||||
|
||||
**Metrics:**
|
||||
- Fan-in: 0
|
||||
- Fan-out: 0
|
||||
|
||||
### get_file_size
|
||||
|
||||
def get_file_size(...)
|
||||
|
||||
No documentation available
|
||||
|
||||
**Type:** Function
|
||||
|
||||
**Metrics:**
|
||||
- Fan-in: 0
|
||||
- Fan-out: 0
|
||||
|
||||
### format_bytes
|
||||
|
||||
def format_bytes(...)
|
||||
|
||||
No documentation available
|
||||
|
||||
**Type:** Function
|
||||
|
||||
**Metrics:**
|
||||
- Fan-in: 0
|
||||
- Fan-out: 0
|
||||
|
||||
|
||||
## Dependencies
|
||||
|
||||
### Imports
|
||||
- json
|
||||
- os
|
||||
|
||||
### Outbound Modules
|
||||
|
||||
### Inbound Modules
|
||||
|
||||
## Integrations
|
||||
|
||||
|
||||
|
||||
|
||||
## Usage Examples
|
||||
|
||||
```python
|
||||
// Example usage of module functions
|
||||
// TODO: Add real usage examples based on module analysis
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user