Files
log/src/doc_generator.rs
syui eb5aa0a2be
Some checks failed
Deploy ailog / build-and-deploy (push) Failing after 14m42s
fix cargo
2025-06-11 18:27:58 +09:00

237 lines
7.5 KiB
Rust

use anyhow::Result;
use std::path::PathBuf;
use crate::analyzer::{ProjectInfo, ApiInfo, ProjectStructure};
use crate::ai::gpt_client::GptClient;
#[allow(dead_code)]
pub struct DocGenerator {
base_path: PathBuf,
ai_enabled: bool,
templates: DocTemplates,
}
pub struct DocTemplates {
readme_template: String,
api_template: String,
structure_template: String,
changelog_template: String,
}
impl DocGenerator {
pub fn new(base_path: PathBuf, ai_enabled: bool) -> Self {
let templates = DocTemplates::default();
Self {
base_path,
ai_enabled,
templates,
}
}
pub async fn generate_readme(&self, project_info: &ProjectInfo) -> Result<String> {
let mut content = self.templates.readme_template.clone();
// Simple template substitution
content = content.replace("{{name}}", &project_info.name);
content = content.replace("{{description}}",
&project_info.description.as_ref().unwrap_or(&"A Rust project".to_string()));
content = content.replace("{{module_count}}", &project_info.modules.len().to_string());
content = content.replace("{{total_lines}}", &project_info.metrics.total_lines.to_string());
let deps = project_info.dependencies.iter()
.map(|(name, version)| format!("- {}: {}", name, version))
.collect::<Vec<_>>()
.join("\n");
content = content.replace("{{dependencies}}", &deps);
content = content.replace("{{license}}",
&project_info.license.as_ref().unwrap_or(&"MIT".to_string()));
if self.ai_enabled {
content = self.enhance_with_ai(&content, "readme").await?;
}
Ok(content)
}
pub async fn generate_api_markdown(&self, api_info: &ApiInfo) -> Result<Vec<(String, String)>> {
let mut files = Vec::new();
// Generate main API documentation
let main_content = self.templates.api_template.replace("{{content}}", "Generated API Documentation");
files.push(("api.md".to_string(), main_content));
// Generate individual module docs
for module in &api_info.modules {
if !module.functions.is_empty() || !module.structs.is_empty() {
let module_content = self.generate_module_doc(module).await?;
files.push((format!("{}.md", module.name), module_content));
}
}
Ok(files)
}
pub async fn generate_structure_doc(&self, structure: &ProjectStructure) -> Result<String> {
let content = self.templates.structure_template.replace("{{content}}",
&format!("Found {} directories and {} files",
structure.directories.len(),
structure.files.len()));
Ok(content)
}
pub async fn generate_changelog(&self, from: Option<String>, to: Option<String>) -> Result<String> {
let commits = self.get_git_commits(from, to)?;
let mut content = self.templates.changelog_template.replace("{{content}}",
&format!("Found {} commits", commits.len()));
if self.ai_enabled {
content = self.enhance_changelog_with_ai(&content, &commits).await?;
}
Ok(content)
}
async fn enhance_with_ai(&self, content: &str, doc_type: &str) -> Result<String> {
if !self.ai_enabled {
return Ok(content.to_string());
}
let gpt_client = GptClient::new(
std::env::var("OPENAI_API_KEY").unwrap_or_default(),
None,
);
let prompt = format!(
"Enhance this {} documentation with additional insights and improve readability:\n\n{}",
doc_type, content
);
match gpt_client.chat("You are a technical writer helping to improve documentation.", &prompt).await {
Ok(enhanced) => Ok(enhanced),
Err(_) => Ok(content.to_string()), // Fallback to original content
}
}
async fn generate_module_doc(&self, module: &crate::analyzer::ModuleInfo) -> Result<String> {
let mut content = format!("# Module: {}\n\n", module.name);
if let Some(docs) = &module.docs {
content.push_str(&format!("{}\n\n", docs));
}
// Add functions
if !module.functions.is_empty() {
content.push_str("## Functions\n\n");
for func in &module.functions {
content.push_str(&self.format_function_doc(func));
}
}
// Add structs
if !module.structs.is_empty() {
content.push_str("## Structs\n\n");
for struct_info in &module.structs {
content.push_str(&self.format_struct_doc(struct_info));
}
}
Ok(content)
}
fn format_function_doc(&self, func: &crate::analyzer::FunctionInfo) -> String {
let mut doc = format!("### `{}`\n\n", func.name);
if let Some(docs) = &func.docs {
doc.push_str(&format!("{}\n\n", docs));
}
doc.push_str(&format!("**Visibility:** `{}`\n", func.visibility));
if func.is_async {
doc.push_str("**Async:** Yes\n");
}
if !func.parameters.is_empty() {
doc.push_str("\n**Parameters:**\n");
for param in &func.parameters {
doc.push_str(&format!("- `{}`: `{}`\n", param.name, param.param_type));
}
}
if let Some(return_type) = &func.return_type {
doc.push_str(&format!("\n**Returns:** `{}`\n", return_type));
}
doc.push_str("\n---\n\n");
doc
}
fn format_struct_doc(&self, struct_info: &crate::analyzer::StructInfo) -> String {
let mut doc = format!("### `{}`\n\n", struct_info.name);
if let Some(docs) = &struct_info.docs {
doc.push_str(&format!("{}\n\n", docs));
}
doc.push_str(&format!("**Visibility:** `{}`\n\n", struct_info.visibility));
if !struct_info.fields.is_empty() {
doc.push_str("**Fields:**\n");
for field in &struct_info.fields {
doc.push_str(&format!("- `{}`: `{}` ({})\n", field.name, field.field_type, field.visibility));
if let Some(field_docs) = &field.docs {
doc.push_str(&format!(" - {}\n", field_docs));
}
}
}
doc.push_str("\n---\n\n");
doc
}
async fn enhance_changelog_with_ai(&self, content: &str, _commits: &[GitCommit]) -> Result<String> {
// TODO: Implement AI-enhanced changelog generation
Ok(content.to_string())
}
fn get_git_commits(&self, _from: Option<String>, _to: Option<String>) -> Result<Vec<GitCommit>> {
// TODO: Implement git history parsing
Ok(vec![])
}
}
#[derive(Debug)]
#[allow(dead_code)]
pub struct GitCommit {
pub hash: String,
pub message: String,
pub author: String,
pub date: String,
}
impl DocTemplates {
fn default() -> Self {
Self {
readme_template: r#"# {{name}}
{{description}}
## Overview
This project contains {{module_count}} modules with {{total_lines}} lines of code.
## Dependencies
{{dependencies}}
## License
{{license}}
"#.to_string(),
api_template: "# API Documentation\n\n{{content}}".to_string(),
structure_template: "# Project Structure\n\n{{content}}".to_string(),
changelog_template: "# Changelog\n\n{{content}}".to_string(),
}
}
}