opengrep 1.1.0

Advanced AST-aware code search tool with tree-sitter parsing and AI integration capabilities
Documentation
//! Output formatting and rendering
//!
//! This module provides various output formatters for search results,
//! including text, JSON, HTML, XML, and CSV formats.

use crate::config::OutputConfig;
use crate::search::SearchResult;
use anyhow::Result;
use std::io::Write;

pub mod csv;
pub mod html;
pub mod json;
pub mod text;
pub mod xml;

/// Output formatter trait
pub trait OutputFormatter {
    /// Format search results to the given writer
    fn format<W: Write>(
        &self,
        results: &[SearchResult],
        writer: &mut W,
        config: &OutputConfig,
    ) -> Result<()>;
}

/// Concrete formatter that can be used as a trait object alternative
pub enum Formatter {
    /// Text formatter.
    Text(text::TextFormatter),
    /// JSON formatter.
    Json(json::JsonFormatter),
    /// HTML formatter.
    Html(html::HtmlFormatter),
    /// XML formatter.
    Xml(xml::XmlFormatter),
    /// CSV formatter.
    Csv(csv::CsvFormatter),
}

impl Formatter {
    /// Format search results to the given writer
    pub fn format<W: Write>(
        &self,
        results: &[SearchResult],
        writer: &mut W,
        config: &OutputConfig,
    ) -> Result<()> {
        match self {
            Formatter::Text(f) => f.format(results, writer, config),
            Formatter::Json(f) => f.format(results, writer, config),
            Formatter::Html(f) => f.format(results, writer, config),
            Formatter::Xml(f) => f.format(results, writer, config),
            Formatter::Csv(f) => f.format(results, writer, config),
        }
    }
}

/// Available output formats
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum OutputFormat {
    /// Plain text output
    Text,
    /// JSON output
    Json,
    /// Pretty JSON output
    JsonPretty,
    /// HTML output
    Html,
    /// XML output
    Xml,
    /// CSV output
    Csv,
}

impl std::str::FromStr for OutputFormat {
    type Err = anyhow::Error;

    fn from_str(s: &str) -> Result<Self> {
        match s.to_lowercase().as_str() {
            "text" | "txt" => Ok(OutputFormat::Text),
            "json" => Ok(OutputFormat::Json),
            "json-pretty" | "pretty-json" => Ok(OutputFormat::JsonPretty),
            "html" => Ok(OutputFormat::Html),
            "xml" => Ok(OutputFormat::Xml),
            "csv" => Ok(OutputFormat::Csv),
            _ => Err(anyhow::anyhow!("Unknown output format: {}", s)),
        }
    }
}

/// Create formatter for the given output format
pub fn create_formatter(format: OutputFormat) -> Formatter {
    match format {
        OutputFormat::Text => Formatter::Text(text::TextFormatter::new()),
        OutputFormat::Json => Formatter::Json(json::JsonFormatter::new(false)),
        OutputFormat::JsonPretty => Formatter::Json(json::JsonFormatter::new(true)),
        OutputFormat::Html => Formatter::Html(html::HtmlFormatter::new()),
        OutputFormat::Xml => Formatter::Xml(xml::XmlFormatter::new()),
        OutputFormat::Csv => Formatter::Csv(csv::CsvFormatter::new()),
    }
}

/// Statistics about the output
#[derive(Debug, Clone, Default)]
pub struct OutputStats {
    /// Number of files processed
    pub files_processed: usize,
    /// Total number of matches
    pub total_matches: usize,
    /// Number of files with matches
    pub files_with_matches: usize,
    /// Processing time in milliseconds
    pub processing_time_ms: u64,
}

impl OutputStats {
    /// Create new output statistics
    pub fn new() -> Self {
        Self::default()
    }

    /// Update statistics with search results
    pub fn update_with_results(&mut self, results: &[SearchResult]) {
        self.files_processed = results.len();
        self.total_matches = results.iter().map(|r| r.matches.len()).sum();
        self.files_with_matches = results.iter().filter(|r| !r.matches.is_empty()).count();
    }
}