srusty-files 0.2.0

A high-performance, cross-platform file search engine library with REST API
Documentation
use rusty_files::core::types::{IndexStats, SearchResult};
use rusty_files::filters::{format_date, format_relative_date, format_size};
use rusty_files::indexer::{UpdateStats, VerificationStats};
use colored::*;

pub struct OutputFormatter {
    use_colors: bool,
    verbose: bool,
}

impl OutputFormatter {
    pub fn new(use_colors: bool, verbose: bool) -> Self {
        Self { use_colors, verbose }
    }

    pub fn print_search_results(&self, results: &[SearchResult], query: &str) {
        if results.is_empty() {
            self.print_info(&format!("No results found for query: {}", query));
            return;
        }

        self.print_header(&format!("Found {} results for: {}", results.len(), query));
        println!();

        for (idx, result) in results.iter().enumerate() {
            self.print_search_result(idx + 1, result);
        }

        println!();
        self.print_summary(&format!("Total: {} results", results.len()));
    }

    pub fn print_search_result(&self, index: usize, result: &SearchResult) {
        let file = &result.file;

        let index_str = format!("[{}]", index);
        let name = &file.name;
        let path = file.path.display().to_string();

        if self.use_colors {
            print!("{} ", index_str.bright_black());
            print!("{} ", name.bright_white().bold());
            println!("{}", path.bright_black());
        } else {
            println!("[{}] {} ({})", index, name, path);
        }

        if self.verbose {
            let mut details = Vec::new();

            if let Some(ref ext) = file.extension {
                details.push(format!("ext: {}", ext));
            }

            details.push(format!("size: {}", format_size(file.size)));

            if let Some(modified) = file.modified_at {
                details.push(format!("modified: {}", format_relative_date(modified)));
            }

            if result.score > 0.0 {
                details.push(format!("score: {:.2}", result.score));
            }

            let details_str = details.join(" | ");
            if self.use_colors {
                println!("  {}", details_str.bright_black());
            } else {
                println!("  {}", details_str);
            }
        }

        if let Some(ref snippet) = result.snippet {
            if self.use_colors {
                println!("  {}", snippet.as_str().bright_yellow());
            } else {
                println!("  {}", snippet);
            }
        }

        println!();
    }

    pub fn print_index_stats(&self, stats: &IndexStats) {
        self.print_header("Index Statistics");
        println!();

        self.print_stat("Total Files", &stats.total_files.to_string());
        self.print_stat("Total Directories", &stats.total_directories.to_string());
        self.print_stat("Total Size", &format_size(stats.total_size));
        self.print_stat(
            "Indexed Files (Content)",
            &stats.indexed_files.to_string(),
        );
        self.print_stat("Last Update", &format_date(stats.last_update));
        self.print_stat("Index Size", &format_size(stats.index_size));

        println!();
    }

    pub fn print_update_stats(&self, stats: &UpdateStats) {
        self.print_header("Index Update Summary");
        println!();

        self.print_stat("Files Added", &stats.added.to_string());
        self.print_stat("Files Updated", &stats.updated.to_string());
        self.print_stat("Files Removed", &stats.removed.to_string());
        self.print_stat("Total Changes", &stats.total().to_string());

        println!();
    }

    pub fn print_verification_stats(&self, stats: &VerificationStats) {
        self.print_header("Index Verification Results");
        println!();

        self.print_stat("Total Indexed", &stats.total_indexed.to_string());
        self.print_stat("Valid", &stats.valid.to_string());
        self.print_stat("Outdated", &stats.outdated.to_string());
        self.print_stat("Missing", &stats.missing.to_string());
        self.print_stat(
            "Health",
            &format!("{:.1}%", stats.health_percentage()),
        );

        println!();
    }

    fn print_stat(&self, label: &str, value: &str) {
        if self.use_colors {
            println!("  {}: {}", label.cyan(), value.white());
        } else {
            println!("  {}: {}", label, value);
        }
    }

    pub fn print_header(&self, text: &str) {
        if self.use_colors {
            println!("{}", text.bright_green().bold());
        } else {
            println!("{}", text);
            println!("{}", "=".repeat(text.len()));
        }
    }

    pub fn print_info(&self, text: &str) {
        if self.use_colors {
            println!("{}", text.bright_blue());
        } else {
            println!("{}", text);
        }
    }

    pub fn print_success(&self, text: &str) {
        if self.use_colors {
            println!("{} {}", "".green(), text.green());
        } else {
            println!("[SUCCESS] {}", text);
        }
    }

    pub fn print_error(&self, text: &str) {
        if self.use_colors {
            eprintln!("{} {}", "".red(), text.red());
        } else {
            eprintln!("[ERROR] {}", text);
        }
    }

    pub fn print_warning(&self, text: &str) {
        if self.use_colors {
            println!("{} {}", "".yellow(), text.yellow());
        } else {
            println!("[WARNING] {}", text);
        }
    }

    pub fn print_summary(&self, text: &str) {
        if self.use_colors {
            println!("{}", text.bright_white().bold());
        } else {
            println!("{}", text);
        }
    }

    pub fn print_progress(&self, message: &str) {
        if self.use_colors {
            print!("\r{}", message.bright_black());
        } else {
            print!("\r{}", message);
        }
        use std::io::Write;
        std::io::stdout().flush().ok();
    }
}

impl Default for OutputFormatter {
    fn default() -> Self {
        Self::new(true, false)
    }
}

pub fn print_table(headers: &[&str], rows: &[Vec<String>], use_colors: bool) {
    let mut col_widths = vec![0; headers.len()];

    for (i, header) in headers.iter().enumerate() {
        col_widths[i] = header.len();
    }

    for row in rows {
        for (i, cell) in row.iter().enumerate() {
            if i < col_widths.len() {
                col_widths[i] = col_widths[i].max(cell.len());
            }
        }
    }

    let separator = col_widths
        .iter()
        .map(|w| "-".repeat(w + 2))
        .collect::<Vec<_>>()
        .join("+");

    if use_colors {
        for (i, header) in headers.iter().enumerate() {
            print!("| {:<width$} ", header.cyan(), width = col_widths[i]);
        }
    } else {
        for (i, header) in headers.iter().enumerate() {
            print!("| {:<width$} ", header, width = col_widths[i]);
        }
    }
    println!("|");

    println!("+{}+", separator);

    for row in rows {
        for (i, cell) in row.iter().enumerate() {
            if i < col_widths.len() {
                print!("| {:<width$} ", cell, width = col_widths[i]);
            }
        }
        println!("|");
    }
}