use crate::state::SessionState; use ignore::WalkBuilder; use serde::Serialize; use std::fs; use std::path::PathBuf; use tauri::State; // ----------------------------------------------------------------------------- // Helper Functions // ----------------------------------------------------------------------------- /// Helper to get the root path (cloned) without joining fn get_project_root(state: &State<'_, SessionState>) -> Result { let root_guard = state.project_root.lock().map_err(|e| e.to_string())?; let root = root_guard .as_ref() .ok_or_else(|| "No project is currently open.".to_string())?; Ok(root.clone()) } // ----------------------------------------------------------------------------- // Commands // ----------------------------------------------------------------------------- #[derive(Serialize)] pub struct SearchResult { path: String, // Relative path matches: usize, } #[tauri::command] pub async fn search_files( query: String, state: State<'_, SessionState>, ) -> Result, String> { let root = get_project_root(&state)?; let root_clone = root.clone(); // Run computationally expensive search on a blocking thread let results = tauri::async_runtime::spawn_blocking(move || { let mut matches = Vec::new(); // default to respecting .gitignore let walker = WalkBuilder::new(&root_clone).git_ignore(true).build(); for result in walker { match result { Ok(entry) => { if !entry.file_type().map(|ft| ft.is_file()).unwrap_or(false) { continue; } let path = entry.path(); // Try to read file // Note: This is a naive implementation reading whole files into memory. // For production, we should stream/buffer reads or use grep-searcher. if let Ok(content) = fs::read_to_string(path) { // Simple substring search (case-sensitive) if content.contains(&query) { // Compute relative path for display let relative = path .strip_prefix(&root_clone) .unwrap_or(path) .to_string_lossy() .to_string(); matches.push(SearchResult { path: relative, matches: 1, // Simplified count for now }); } } } Err(err) => eprintln!("Error walking dir: {}", err), } } matches }) .await .map_err(|e| format!("Search task failed: {}", e))?; Ok(results) }