diffscope 0.5.3

A composable code review engine with smart analysis, confidence scoring, and professional reporting
use anyhow::Result;
use async_trait::async_trait;
use std::collections::HashMap;
use std::sync::Arc;
use crate::core::{UnifiedDiff, LLMContextChunk, Comment};
use crate::plugins::{PreAnalyzer, PostProcessor};

#[async_trait]
pub trait Plugin: Send + Sync {
    fn id(&self) -> &str;
    fn name(&self) -> &str;
    fn version(&self) -> &str;
    
    async fn as_pre_analyzer(&self) -> Option<Box<dyn PreAnalyzer>>;
    async fn as_post_processor(&self) -> Option<Box<dyn PostProcessor>>;
}

pub struct PluginManager {
    plugins: HashMap<String, Arc<dyn Plugin>>,
    pre_analyzers: Vec<Arc<dyn PreAnalyzer>>,
    post_processors: Vec<Arc<dyn PostProcessor>>,
}

impl PluginManager {
    pub fn new() -> Self {
        Self {
            plugins: HashMap::new(),
            pre_analyzers: Vec::new(),
            post_processors: Vec::new(),
        }
    }
    
    pub async fn load_builtin_plugins(&mut self) -> Result<()> {
        self.register_pre_analyzer(Arc::new(crate::plugins::builtin::EslintAnalyzer::new()));
        self.register_pre_analyzer(Arc::new(crate::plugins::builtin::SemgrepAnalyzer::new()));
        self.register_post_processor(Arc::new(crate::plugins::builtin::DuplicateFilter::new()));
        
        Ok(())
    }
    
    pub fn register_pre_analyzer(&mut self, analyzer: Arc<dyn PreAnalyzer>) {
        self.pre_analyzers.push(analyzer);
    }
    
    pub fn register_post_processor(&mut self, processor: Arc<dyn PostProcessor>) {
        self.post_processors.push(processor);
    }
    
    pub async fn run_pre_analyzers(
        &self,
        diff: &UnifiedDiff,
        repo_path: &str,
    ) -> Result<Vec<LLMContextChunk>> {
        let mut all_chunks = Vec::new();
        
        for analyzer in &self.pre_analyzers {
            match analyzer.run(diff, repo_path).await {
                Ok(chunks) => all_chunks.extend(chunks),
                Err(e) => {
                    tracing::warn!("Pre-analyzer {} failed: {}", analyzer.id(), e);
                }
            }
        }
        
        Ok(all_chunks)
    }
    
    pub async fn run_post_processors(
        &self,
        comments: Vec<Comment>,
        repo_path: &str,
    ) -> Result<Vec<Comment>> {
        let mut processed = comments;
        
        for processor in &self.post_processors {
            match processor.run(processed.clone(), repo_path).await {
                Ok(result) => processed = result,
                Err(e) => {
                    tracing::warn!("Post-processor {} failed: {}", processor.id(), e);
                }
            }
        }
        
        Ok(processed)
    }
}