use anyhow::Result;
use serde::{Deserialize, Serialize};
use std::process::Command;
use tracing::{debug, info, warn};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AIChatConfig {
pub model: String,
pub api_key: Option<String>,
pub temperature: f32,
pub max_tokens: usize,
}
impl Default for AIChatConfig {
fn default() -> Self {
Self {
model: "gpt-4".to_string(),
api_key: None,
temperature: 0.7,
max_tokens: 2048,
}
}
}
pub struct AIChatIntegration {
config: AIChatConfig,
}
impl AIChatIntegration {
pub fn new(config: AIChatConfig) -> Self {
Self { config }
}
pub async fn generate_code(&self, prompt: &str) -> Result<String> {
info!("Generating code using AIChat");
debug!("Prompt: {}", prompt);
let mut cmd = Command::new("aichat");
cmd.arg("--model").arg(&self.config.model);
if self.config.temperature != 0.7 {
cmd.arg("--temperature").arg(self.config.temperature.to_string());
}
if self.config.max_tokens != 2048 {
cmd.arg("--max-tokens").arg(self.config.max_tokens.to_string());
}
let enhanced_prompt = format!(
"Generate Rust code for the following request. \
Provide clean, idiomatic Rust code with proper error handling, \
documentation, and tests where appropriate:\n\n{}",
prompt
);
cmd.arg(&enhanced_prompt);
match cmd.output() {
Ok(output) => {
if output.status.success() {
let generated_code = String::from_utf8(output.stdout)?;
info!("Successfully generated {} bytes of code", generated_code.len());
Ok(generated_code)
} else {
let error_msg = String::from_utf8_lossy(&output.stderr);
warn!("AIChat code generation failed: {}", error_msg);
Err(anyhow::anyhow!("AIChat failed: {}", error_msg))
}
}
Err(e) => {
warn!("Failed to execute aichat command: {}", e);
self.fallback_code_generation(prompt).await
}
}
}
pub async fn analyze_code(&self, code: &str) -> Result<String> {
info!("Analyzing code using AIChat");
debug!("Code length: {} characters", code.len());
let mut cmd = Command::new("aichat");
cmd.arg("--model").arg(&self.config.model);
cmd.arg("--temperature").arg("0.3");
let analysis_prompt = format!(
"Analyze the following Rust code and provide:\n\
1. Code quality assessment\n\
2. Potential issues or bugs\n\
3. Performance optimization suggestions\n\
4. Security considerations\n\
5. Best practices recommendations\n\n\
Code to analyze:\n```rust\n{}\n```",
code
);
cmd.arg(&analysis_prompt);
match cmd.output() {
Ok(output) => {
if output.status.success() {
let analysis_result = String::from_utf8(output.stdout)?;
info!("Successfully analyzed code");
Ok(analysis_result)
} else {
let error_msg = String::from_utf8_lossy(&output.stderr);
warn!("AIChat code analysis failed: {}", error_msg);
Err(anyhow::anyhow!("AIChat analysis failed: {}", error_msg))
}
}
Err(e) => {
warn!("Failed to execute aichat command for analysis: {}", e);
self.fallback_code_analysis(code).await
}
}
}
pub async fn interactive_chat(&self, question: &str) -> Result<String> {
info!("Starting interactive chat session");
debug!("Question: {}", question);
let mut cmd = Command::new("aichat");
cmd.arg("--model").arg(&self.config.model);
cmd.arg("--temperature").arg(self.config.temperature.to_string());
let chat_prompt = format!(
"You are an expert Rust developer assistant helping with OpenCrates development. \
Provide helpful, accurate, and practical advice for Rust programming, \
crate development, and software architecture.\n\n\
Question: {}",
question
);
cmd.arg(&chat_prompt);
match cmd.output() {
Ok(output) => {
if output.status.success() {
let response = String::from_utf8(output.stdout)?;
info!("Interactive chat completed successfully");
Ok(response)
} else {
let error_msg = String::from_utf8_lossy(&output.stderr);
warn!("AIChat interactive session failed: {}", error_msg);
Err(anyhow::anyhow!("Chat failed: {}", error_msg))
}
}
Err(e) => {
warn!("Failed to start interactive chat: {}", e);
Ok("I'm sorry, but I'm unable to connect to AIChat at the moment. Please ensure AIChat is installed and properly configured.".to_string())
}
}
}
pub fn is_available(&self) -> bool {
match Command::new("aichat").arg("--version").output() {
Ok(output) => {
let available = output.status.success();
if available {
debug!("AIChat is available");
} else {
debug!("AIChat command failed");
}
available
}
Err(e) => {
debug!("AIChat not found: {}", e);
false
}
}
}
pub fn config(&self) -> &AIChatConfig {
&self.config
}
pub fn update_config(&mut self, config: AIChatConfig) {
info!("Updating AIChat configuration");
self.config = config;
}
pub async fn generate_documentation(&self, code: &str, doc_type: &str) -> Result<String> {
info!("Generating {} documentation using AIChat", doc_type);
let mut cmd = Command::new("aichat");
cmd.arg("--model").arg(&self.config.model);
cmd.arg("--temperature").arg("0.4");
let doc_prompt = match doc_type {
"api" => format!(
"Generate comprehensive API documentation for the following Rust code. \
Include function descriptions, parameters, return values, examples, and error conditions:\n\n```rust\n{}\n```",
code
),
"tutorial" => format!(
"Create a tutorial explaining how to use the following Rust code. \
Include step-by-step instructions and practical examples:\n\n```rust\n{}\n```",
code
),
"readme" => format!(
"Generate a README.md file for a Rust project containing this code. \
Include project description, installation, usage, and examples:\n\n```rust\n{}\n```",
code
),
_ => format!(
"Generate documentation for the following Rust code:\n\n```rust\n{}\n```",
code
),
};
cmd.arg(&doc_prompt);
match cmd.output() {
Ok(output) => {
if output.status.success() {
let documentation = String::from_utf8(output.stdout)?;
info!("Successfully generated {} documentation", doc_type);
Ok(documentation)
} else {
let error_msg = String::from_utf8_lossy(&output.stderr);
warn!("Documentation generation failed: {}", error_msg);
Err(anyhow::anyhow!("Documentation generation failed: {}", error_msg))
}
}
Err(e) => {
warn!("Failed to execute documentation generation: {}", e);
self.fallback_documentation(code, doc_type).await
}
}
}
async fn fallback_code_generation(&self, prompt: &str) -> Result<String> {
warn!("Using fallback code generation");
let code_template = format!(
r#"// Generated code for: {}
//
// This is a basic template. Please customize as needed.
use anyhow::Result;
/// Main function generated for the request
pub fn generated_function() -> Result<()> {{
// TODO: Implement the requested functionality
println!("Generated code for: {}", "{}");
Ok(())
}}
#[cfg(test)]
mod tests {{
use super::*;
#[test]
fn test_generated_function() {{
assert!(generated_function().is_ok());
}}
}}
"#,
prompt, prompt
);
Ok(code_template)
}
async fn fallback_code_analysis(&self, code: &str) -> Result<String> {
warn!("Using fallback code analysis");
let mut analysis = Vec::new();
if code.contains("unsafe") {
analysis.push("⚠️ Contains unsafe code - review carefully for memory safety");
}
if code.contains("unwrap()") {
analysis.push("⚠️ Uses unwrap() - consider proper error handling");
}
if code.contains("panic!") {
analysis.push("⚠️ Contains panic! - consider returning errors instead");
}
if !code.contains("#[cfg(test)]") && !code.contains("#[test]") {
analysis.push("📝 Consider adding unit tests");
}
if !code.contains("//!") && !code.contains("///") {
analysis.push("📝 Consider adding documentation comments");
}
let line_count = code.lines().count();
if line_count > 100 {
analysis.push("📏 Function/file is quite long - consider breaking it down");
}
if analysis.is_empty() {
analysis.push("✅ No obvious issues found in basic analysis");
}
Ok(format!(
"# Basic Code Analysis\n\n{}\n\n_Note: This is a basic analysis. For comprehensive analysis, install AIChat._",
analysis.join("\n")
))
}
async fn fallback_documentation(&self, code: &str, doc_type: &str) -> Result<String> {
warn!("Using fallback documentation generation");
match doc_type {
"readme" => Ok(format!(
"# Rust Project\n\n## Description\n\nThis project contains Rust code.\n\n## Usage\n\n```rust\n{}\n```\n\n## Installation\n\n```bash\ncargo add your-crate-name\n```",
code.lines().take(10).collect::<Vec<_>>().join("\n")
)),
"api" => Ok(format!(
"# API Documentation\n\n## Code\n\n```rust\n{}\n```\n\n_Note: For detailed API documentation, install AIChat._",
code
)),
_ => Ok(format!(
"# Documentation\n\n```rust\n{}\n```\n\n_Note: For detailed documentation, install AIChat._",
code
)),
}
}
}