srusty-files 0.2.0

A high-performance, cross-platform file search engine library with REST API
Documentation
use crate::output::OutputFormatter;
use rusty_files::core::{Result, SearchEngine};
use crossterm::{
    event::{self, Event, KeyCode, KeyEvent},
    execute,
    terminal::{disable_raw_mode, enable_raw_mode, Clear, ClearType},
};
use std::io::{self, Write};
use std::sync::Arc;
use std::sync::Mutex;

pub struct InteractiveMode {
    engine: Arc<Mutex<SearchEngine>>,
    formatter: OutputFormatter,
    history: Vec<String>,
    history_index: usize,
}

impl InteractiveMode {
    pub fn new(engine: SearchEngine) -> Self {
        Self {
            engine: Arc::new(Mutex::new(engine)),
            formatter: OutputFormatter::new(true, false),
            history: Vec::new(),
            history_index: 0,
        }
    }

    pub fn run(&mut self) -> Result<()> {
        self.print_welcome();

        loop {
            print!("\n> ");
            io::stdout().flush()?;

            let input = self.read_line()?;
            let input = input.trim();

            if input.is_empty() {
                continue;
            }

            if self.handle_command(input)? {
                break;
            }

            self.history.push(input.to_string());
            self.history_index = self.history.len();
        }

        Ok(())
    }

    fn print_welcome(&self) {
        self.formatter.print_header("Rusty Files - Interactive Search");
        println!();
        self.formatter.print_info("Type a search query or use commands:");
        println!("  :help    - Show help");
        println!("  :stats   - Show index statistics");
        println!("  :quit    - Exit interactive mode");
        println!();
    }

    fn handle_command(&self, input: &str) -> Result<bool> {
        if input.starts_with(':') {
            match input {
                ":quit" | ":q" | ":exit" => return Ok(true),
                ":help" | ":h" => {
                    self.print_help();
                }
                ":stats" => {
                    self.print_stats()?;
                }
                ":clear" => {
                    self.clear_screen()?;
                }
                ":history" => {
                    self.print_history();
                }
                _ => {
                    self.formatter.print_error(&format!("Unknown command: {}", input));
                    self.formatter.print_info("Type :help for available commands");
                }
            }
            Ok(false)
        } else {
            self.execute_search(input)?;
            Ok(false)
        }
    }

    fn execute_search(&self, query: &str) -> Result<()> {
        let engine = self.engine.lock().unwrap();
        let results = engine.search(query)?;

        self.formatter.print_search_results(&results, query);

        Ok(())
    }

    fn print_help(&self) {
        self.formatter.print_header("Interactive Mode Help");
        println!();
        println!("Search Queries:");
        println!("  pattern                    - Simple search");
        println!("  pattern ext:rs             - Search with extension filter");
        println!("  pattern size:>1MB          - Search with size filter");
        println!("  pattern modified:today     - Search with date filter");
        println!("  pattern mode:fuzzy         - Use fuzzy matching");
        println!();
        println!("Commands:");
        println!("  :help, :h                  - Show this help");
        println!("  :stats                     - Show index statistics");
        println!("  :clear                     - Clear screen");
        println!("  :history                   - Show search history");
        println!("  :quit, :q, :exit           - Exit interactive mode");
        println!();
    }

    fn print_stats(&self) -> Result<()> {
        let engine = self.engine.lock().unwrap();
        let stats = engine.get_stats()?;

        self.formatter.print_index_stats(&stats);

        Ok(())
    }

    fn print_history(&self) {
        if self.history.is_empty() {
            self.formatter.print_info("No search history");
            return;
        }

        self.formatter.print_header("Search History");
        println!();

        for (i, query) in self.history.iter().enumerate() {
            println!("  {}: {}", i + 1, query);
        }

        println!();
    }

    fn clear_screen(&self) -> Result<()> {
        execute!(io::stdout(), Clear(ClearType::All))?;
        Ok(())
    }

    fn read_line(&self) -> Result<String> {
        let mut input = String::new();
        io::stdin().read_line(&mut input)?;
        Ok(input)
    }

    pub fn run_with_raw_mode(&mut self) -> Result<()> {
        enable_raw_mode()?;

        let result = self.run_raw_mode_loop();

        disable_raw_mode()?;

        result
    }

    fn run_raw_mode_loop(&mut self) -> Result<()> {
        self.print_welcome();

        let mut input = String::new();

        loop {
            print!("\r> {}", input);
            io::stdout().flush()?;

            if let Event::Key(KeyEvent { code, .. }) = event::read()? {
                match code {
                    KeyCode::Enter => {
                        println!();
                        if !input.is_empty() {
                            if self.handle_command(&input)? {
                                break;
                            }
                            self.history.push(input.clone());
                            self.history_index = self.history.len();
                            input.clear();
                        }
                    }
                    KeyCode::Char(c) => {
                        input.push(c);
                    }
                    KeyCode::Backspace => {
                        input.pop();
                    }
                    KeyCode::Esc => {
                        break;
                    }
                    KeyCode::Up => {
                        if self.history_index > 0 {
                            self.history_index -= 1;
                            input = self.history[self.history_index].clone();
                        }
                    }
                    KeyCode::Down => {
                        if self.history_index < self.history.len() - 1 {
                            self.history_index += 1;
                            input = self.history[self.history_index].clone();
                        } else {
                            self.history_index = self.history.len();
                            input.clear();
                        }
                    }
                    _ => {}
                }
            }
        }

        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use tempfile::TempDir;

    #[test]
    fn test_interactive_mode_creation() {
        let temp_dir = TempDir::new().unwrap();
        let index_path = temp_dir.path().join("index.db");
        let engine = SearchEngine::new(&index_path).unwrap();
        let _interactive = InteractiveMode::new(engine);
    }
}