codesearch 0.1.9

A fast, intelligent CLI tool with multiple search modes (regex, fuzzy, semantic), code analysis, and dead code detection for popular programming languages
Documentation
//! Export Module
//!
//! Provides functionality to export search results to various formats.

use crate::types::SearchResult;
use std::fs::File;
use std::io::Write;

/// Export search results to a file (CSV or Markdown)
pub fn export_results(
    results: &[SearchResult],
    path: &str,
    query: &str,
) -> Result<(), Box<dyn std::error::Error>> {
    let format = if path.ends_with(".csv") {
        ExportFormat::Csv
    } else if path.ends_with(".md") || path.ends_with(".markdown") {
        ExportFormat::Markdown
    } else {
        ExportFormat::Text
    };

    match format {
        ExportFormat::Csv => export_csv(results, path),
        ExportFormat::Markdown => export_markdown(results, path, query),
        ExportFormat::Text => export_text(results, path, query),
    }
}

enum ExportFormat {
    Csv,
    Markdown,
    Text,
}

fn export_csv(results: &[SearchResult], path: &str) -> Result<(), Box<dyn std::error::Error>> {
    let file = File::create(path)?;
    let mut wtr = csv::Writer::from_writer(file);

    // Write header
    wtr.write_record(["File", "Line", "Content", "Score", "Relevance"])?;

    // Write results
    for result in results {
        wtr.write_record([
            &result.file,
            &result.line_number.to_string(),
            &result.content,
            &format!("{:.2}", result.score),
            &result.relevance,
        ])?;
    }

    wtr.flush()?;
    Ok(())
}

fn export_markdown(
    results: &[SearchResult],
    path: &str,
    query: &str,
) -> Result<(), Box<dyn std::error::Error>> {
    let mut file = File::create(path)?;

    // Write header
    writeln!(file, "# Search Results")?;
    writeln!(file)?;
    writeln!(file, "**Query:** `{}`", query)?;
    writeln!(file, "**Total Results:** {}", results.len())?;
    writeln!(file)?;

    // Group by file
    let mut current_file = "";
    for result in results {
        if result.file != current_file {
            writeln!(file)?;
            writeln!(file, "## {}", result.file)?;
            writeln!(file)?;
            current_file = &result.file;
        }

        writeln!(
            file,
            "- **Line {}** (Score: {:.0}): `{}`",
            result.line_number,
            result.score,
            result.content.trim()
        )?;
    }

    writeln!(file)?;
    writeln!(file, "---")?;
    writeln!(file, "*Generated by codesearch*")?;

    Ok(())
}

fn export_text(
    results: &[SearchResult],
    path: &str,
    query: &str,
) -> Result<(), Box<dyn std::error::Error>> {
    let mut file = File::create(path)?;

    writeln!(file, "Search Results for: {}", query)?;
    writeln!(file, "Total: {} results", results.len())?;
    writeln!(file, "{}", "=".repeat(60))?;
    writeln!(file)?;

    for result in results {
        writeln!(
            file,
            "{}:{} {}",
            result.file,
            result.line_number,
            result.content.trim()
        )?;
    }

    Ok(())
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::types::Match;
    use tempfile::tempdir;

    fn create_test_results() -> Vec<SearchResult> {
        vec![SearchResult {
            file: "test.rs".to_string(),
            line_number: 10,
            content: "fn main() {}".to_string(),
            matches: vec![Match {
                start: 0,
                end: 2,
                text: "fn".to_string(),
            }],
            score: 85.0,
            relevance: "High".to_string(),
        }]
    }

    #[test]
    fn test_export_csv() {
        let dir = tempdir().unwrap();
        let path = dir.path().join("results.csv");
        let results = create_test_results();

        export_csv(&results, path.to_str().unwrap()).unwrap();

        let content = std::fs::read_to_string(&path).unwrap();
        assert!(content.contains("File"));
        assert!(content.contains("test.rs"));
    }

    #[test]
    fn test_export_markdown() {
        let dir = tempdir().unwrap();
        let path = dir.path().join("results.md");
        let results = create_test_results();

        export_markdown(&results, path.to_str().unwrap(), "fn").unwrap();

        let content = std::fs::read_to_string(&path).unwrap();
        assert!(content.contains("# Search Results"));
        assert!(content.contains("test.rs"));
    }

    #[test]
    fn test_export_text() {
        let dir = tempdir().unwrap();
        let path = dir.path().join("results.txt");
        let results = create_test_results();

        export_text(&results, path.to_str().unwrap(), "fn").unwrap();

        let content = std::fs::read_to_string(&path).unwrap();
        assert!(content.contains("Search Results"));
        assert!(content.contains("test.rs"));
    }
}