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 crate::core::{UnifiedDiff, LLMContextChunk, ContextType};
use crate::plugins::PreAnalyzer;
use std::path::PathBuf;
use std::process::Command;

pub struct EslintAnalyzer;

impl EslintAnalyzer {
    pub fn new() -> Self {
        Self
    }
}

#[async_trait]
impl PreAnalyzer for EslintAnalyzer {
    fn id(&self) -> &str {
        "eslint"
    }
    
    async fn run(&self, diff: &UnifiedDiff, repo_path: &str) -> Result<Vec<LLMContextChunk>> {
        if !diff.file_path.to_string_lossy().ends_with(".js") &&
           !diff.file_path.to_string_lossy().ends_with(".ts") &&
           !diff.file_path.to_string_lossy().ends_with(".jsx") &&
           !diff.file_path.to_string_lossy().ends_with(".tsx") {
            return Ok(Vec::new());
        }
        
        let file_path = PathBuf::from(repo_path).join(&diff.file_path);
        
        let output = Command::new("eslint")
            .arg("--format=json")
            .arg("--no-eslintrc")
            .arg(file_path.to_string_lossy().as_ref())
            .output();
        
        match output {
            Ok(output) => {
                let stdout = String::from_utf8_lossy(&output.stdout);
                if !stdout.trim().is_empty() {
                    Ok(vec![LLMContextChunk {
                        file_path: diff.file_path.clone(),
                        content: format!("ESLint analysis:\n{}", stdout),
                        context_type: ContextType::Documentation,
                        line_range: None,
                    }])
                } else {
                    Ok(Vec::new())
                }
            }
            Err(_) => {
                Ok(Vec::new())
            }
        }
    }
}