use crate::atuin_client::{ database::{ClientSqlite, Context, OptFilters}, history::{History, HistoryId}, settings::{FilterMode, SearchMode, Settings}, }; use async_trait::async_trait; use eyre::Result; use super::cursor::Cursor; #[cfg(feature = "daemon")] pub(crate) mod daemon; pub(crate) mod db; pub(crate) mod skim; #[expect(unused)] // settings is only used if daemon feature is enabled pub(crate) fn engine(search_mode: SearchMode, settings: &Settings) -> Box { match search_mode { SearchMode::Skim => Box::new(skim::Search::new()) as Box<_>, #[cfg(feature = "daemon")] SearchMode::DaemonFuzzy => Box::new(daemon::Search::new(settings)) as Box<_>, #[cfg(not(feature = "daemon"))] SearchMode::DaemonFuzzy => { // Fall back to fuzzy mode if daemon feature is not enabled Box::new(db::Search(SearchMode::Fuzzy)) as Box<_> } mode => Box::new(db::Search(mode)) as Box<_>, } } pub(crate) struct SearchState { pub(crate) input: Cursor, pub(crate) filter_mode: FilterMode, pub(crate) context: Context, pub(crate) custom_context: Option, } impl SearchState { pub(crate) fn rotate_filter_mode(&mut self, settings: &Settings, offset: isize) { let mut i = settings .search .filters .iter() .position(|&m| m == self.filter_mode) .unwrap_or_default(); for _ in 0..settings.search.filters.len() { i = (i.wrapping_add_signed(offset)) % settings.search.filters.len(); let mode = settings.search.filters[i]; if self.filter_mode_available(mode, settings) { self.filter_mode = mode; break; } } } fn filter_mode_available(&self, mode: FilterMode, settings: &Settings) -> bool { match mode { FilterMode::Global | FilterMode::SessionPreload => self.custom_context.is_none(), FilterMode::Workspace => settings.workspaces && self.context.git_root.is_some(), _ => true, } } } #[async_trait] pub(crate) trait SearchEngine: Send + Sync + 'static { async fn full_query( &mut self, state: &SearchState, db: &mut ClientSqlite, ) -> Result>; async fn query(&mut self, state: &SearchState, db: &mut ClientSqlite) -> Result> { if state.input.as_str().is_empty() { Ok(db .search( SearchMode::FullText, state.filter_mode, &state.context, "", OptFilters { limit: Some(200), ..Default::default() }, ) .await? .into_iter() .collect::>()) } else { self.full_query(state, db).await } } fn get_highlight_indices(&self, command: &str, search_input: &str) -> Vec; }