#![allow(dead_code)]
use anyhow::Result;
use serde::{Deserialize, Serialize};
pub trait AnalyzerPlugin: Send + Sync {
fn name(&self) -> &str;
fn analyze(&self, context: &PluginContext) -> Result<PluginResult>;
fn priority(&self) -> u8 { 100 }
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PluginContext {
pub code: String,
pub language: String,
pub file_path: Option<String>,
pub metadata: std::collections::HashMap<String, String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PluginResult {
pub issues: Vec<PluginIssue>,
pub metrics: std::collections::HashMap<String, f64>,
pub suggestions: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PluginIssue {
pub severity: IssueSeverity,
pub category: String,
pub message: String,
pub line: Option<usize>,
pub suggestion: Option<String>,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
pub enum IssueSeverity {
Info,
Warning,
Error,
Critical,
}
pub struct PluginManager {
pub plugins: Vec<Box<dyn AnalyzerPlugin>>,
}
impl PluginManager {
pub fn new() -> Self {
Self {
plugins: Vec::new(),
}
}
pub fn add_plugin(&mut self, plugin: Box<dyn AnalyzerPlugin>) {
self.plugins.push(plugin);
}
pub fn register_builtin_plugins(&mut self) {
self.add_plugin(Box::new(SecurityPatternPlugin::new()));
self.add_plugin(Box::new(PerformancePatternPlugin::new()));
self.add_plugin(Box::new(DocumentationPlugin::new()));
}
pub fn analyze(&self, context: &PluginContext) -> Result<Vec<PluginResult>> {
let mut results = Vec::new();
let mut plugins: Vec<_> = self.plugins.iter().collect();
plugins.sort_by_key(|p| p.priority());
for plugin in plugins {
match plugin.analyze(context) {
Ok(result) => results.push(result),
Err(e) => {
tracing::warn!("analyzer {} failed: {}", plugin.name(), e);
}
}
}
Ok(results)
}
}
impl Default for PluginManager {
fn default() -> Self {
Self::new()
}
}
struct SecurityPatternPlugin {
patterns: Vec<(&'static str, &'static str)>,
}
impl SecurityPatternPlugin {
fn new() -> Self {
Self {
patterns: vec![
("eval(", "Avoid eval() - code injection risk"),
("exec(", "Avoid exec() - code injection risk"),
("password", "Potential hardcoded password"),
("secret", "Potential hardcoded secret"),
("api_key", "Potential hardcoded API key"),
],
}
}
}
impl AnalyzerPlugin for SecurityPatternPlugin {
fn name(&self) -> &str {
"security_patterns"
}
fn priority(&self) -> u8 {
10
}
fn analyze(&self, context: &PluginContext) -> Result<PluginResult> {
let mut issues = Vec::new();
for (line_num, line) in context.code.lines().enumerate() {
for (pattern, message) in &self.patterns {
if line.contains(pattern) {
issues.push(PluginIssue {
severity: IssueSeverity::Warning,
category: "security".to_string(),
message: message.to_string(),
line: Some(line_num + 1),
suggestion: None,
});
}
}
}
Ok(PluginResult {
issues,
metrics: std::collections::HashMap::new(),
suggestions: Vec::new(),
})
}
}
struct PerformancePatternPlugin {
patterns: Vec<(&'static str, &'static str)>,
}
impl PerformancePatternPlugin {
fn new() -> Self {
Self {
patterns: vec![
(".keys()", "Unnecessary .keys() call"),
("+ str(", "String concatenation in loop"),
("== None", "Use 'is None' for None comparison"),
],
}
}
}
impl AnalyzerPlugin for PerformancePatternPlugin {
fn name(&self) -> &str {
"performance_patterns"
}
fn priority(&self) -> u8 {
20
}
fn analyze(&self, context: &PluginContext) -> Result<PluginResult> {
let mut issues = Vec::new();
if context.language == "python" {
for (line_num, line) in context.code.lines().enumerate() {
for (pattern, message) in &self.patterns {
if line.contains(pattern) {
issues.push(PluginIssue {
severity: IssueSeverity::Info,
category: "performance".to_string(),
message: message.to_string(),
line: Some(line_num + 1),
suggestion: None,
});
}
}
}
}
Ok(PluginResult {
issues,
metrics: std::collections::HashMap::new(),
suggestions: Vec::new(),
})
}
}
struct DocumentationPlugin;
impl DocumentationPlugin {
fn new() -> Self {
Self
}
}
impl AnalyzerPlugin for DocumentationPlugin {
fn name(&self) -> &str {
"documentation"
}
fn priority(&self) -> u8 {
50
}
fn analyze(&self, context: &PluginContext) -> Result<PluginResult> {
let mut issues = Vec::new();
let has_docstring = context.code.contains("\"\"\"") || context.code.contains("'''");
let has_comments = context.code.contains("//") || context.code.contains("#");
if !has_docstring && !has_comments && context.code.lines().count() > 20 {
issues.push(PluginIssue {
severity: IssueSeverity::Info,
category: "documentation".to_string(),
message: "Consider adding documentation".to_string(),
line: None,
suggestion: Some("Add docstrings or comments to explain the code".to_string()),
});
}
Ok(PluginResult {
issues,
metrics: std::collections::HashMap::new(),
suggestions: Vec::new(),
})
}
}