Skip to main content

ass_editor/utils/search/
engine.rs

1//! `DocumentSearchImpl` state and cache management
2//!
3//! Holds the search implementation struct along with construction, cache
4//! sizing, and cache-key helpers shared by the sibling search modules.
5
6use super::options::{SearchOptions, SearchResult, SearchStats};
7
8#[cfg(feature = "std")]
9use std::collections::HashMap;
10
11#[cfg(not(feature = "std"))]
12use hashbrown::HashMap;
13
14#[cfg(not(feature = "std"))]
15use alloc::{format, string::String, vec::Vec};
16
17#[cfg(feature = "search-index")]
18use crate::core::Position;
19
20#[cfg(feature = "search-index")]
21use fst::Set;
22
23/// Document search implementation with optional FST indexing
24#[derive(Debug)]
25pub struct DocumentSearchImpl {
26    /// FST set for fast prefix/substring searches (when feature enabled)
27    #[cfg(feature = "search-index")]
28    pub(super) word_index: Option<Set<Vec<u8>>>,
29
30    /// Mapping from words to their positions in the document
31    #[cfg(feature = "search-index")]
32    pub(super) word_positions: HashMap<String, Vec<Position>>,
33
34    /// Last known document version for incremental updates
35    pub(super) document_version: u64,
36
37    /// Cache of recent search results (owned to persist across calls)
38    pub(super) result_cache: HashMap<String, Vec<SearchResult<'static>>>,
39
40    /// Maximum cache size
41    pub(super) max_cache_entries: usize,
42
43    /// Search statistics
44    pub(super) stats: SearchStats,
45
46    /// Cached document text for regex and basic searches
47    pub(super) cached_text: String,
48}
49
50impl DocumentSearchImpl {
51    /// Create a new unified search instance
52    ///
53    /// # Examples
54    ///
55    /// ```
56    /// use ass_editor::utils::search::DocumentSearchImpl;
57    ///
58    /// let mut search = DocumentSearchImpl::new();
59    /// // Use search with documents...
60    /// ```
61    pub fn new() -> Self {
62        Self {
63            #[cfg(feature = "search-index")]
64            word_index: None,
65            #[cfg(feature = "search-index")]
66            word_positions: HashMap::new(),
67            document_version: 0,
68            result_cache: HashMap::new(),
69            max_cache_entries: 100,
70            stats: SearchStats {
71                match_count: 0,
72                search_time_us: 0,
73                hit_limit: false,
74                index_size: 0,
75            },
76            cached_text: String::new(),
77        }
78    }
79
80    /// Set maximum number of cached search results
81    pub fn set_cache_size(&mut self, size: usize) {
82        self.max_cache_entries = size;
83        self.trim_cache();
84    }
85
86    /// Trim cache to maximum size
87    fn trim_cache(&mut self) {
88        if self.result_cache.len() > self.max_cache_entries {
89            // Simple LRU-like eviction: remove half of the entries
90            let keys_to_remove: Vec<String> = self
91                .result_cache
92                .keys()
93                .take(self.result_cache.len() / 2)
94                .cloned()
95                .collect();
96
97            for key in keys_to_remove {
98                self.result_cache.remove(&key);
99            }
100        }
101    }
102
103    /// Generate cache key for search parameters
104    pub(super) fn cache_key(&self, pattern: &str, options: &SearchOptions) -> String {
105        format!(
106            "{}|{}|{}|{}|{:?}",
107            pattern,
108            options.case_sensitive,
109            options.whole_words,
110            options.max_results,
111            options.scope
112        )
113    }
114}
115
116impl Default for DocumentSearchImpl {
117    fn default() -> Self {
118        Self::new()
119    }
120}