sql_cli/ui/search/
search_operations.rs1use crate::app_state_container::AppStateContainer;
8use crate::buffer::BufferAPI;
9use crate::data::data_view::DataView;
10use crate::widgets::search_modes_widget::SearchMode;
11pub struct SearchContext<'a> {
16 pub state_container: &'a AppStateContainer,
17 pub buffer: &'a mut dyn BufferAPI,
18 pub current_data: Option<&'a DataView>,
19}
20
21#[derive(Debug, Clone, PartialEq)]
23pub struct SearchResult {
24 pub matches_found: usize,
25 pub first_match: Option<(usize, usize)>, pub status_message: String,
27}
28
29#[derive(Debug, Clone, PartialEq)]
31pub enum SearchActionResult {
32 Success(SearchResult),
34 InProgress(String),
36 Error(String),
38}
39
40pub fn execute_search_action(
43 mode: SearchMode,
44 pattern: String,
45 ctx: &mut SearchContext,
46) -> SearchActionResult {
47 match mode {
48 SearchMode::Search => {
49 ctx.state_container.start_search(pattern.clone());
51 ctx.buffer.set_search_pattern(pattern);
52
53 let search_result = perform_search_with_context(ctx);
55
56 match search_result {
57 Ok(result) => SearchActionResult::Success(result),
58 Err(e) => SearchActionResult::Error(e),
59 }
60 }
61 SearchMode::Filter => {
62 SearchActionResult::InProgress(format!("Filter mode with pattern: {pattern}"))
63 }
64 SearchMode::FuzzyFilter => {
65 SearchActionResult::InProgress(format!("Fuzzy filter mode with pattern: {pattern}"))
66 }
67 SearchMode::ColumnSearch => {
68 SearchActionResult::InProgress(format!("Column search mode with pattern: {pattern}"))
69 }
70 }
71}
72
73fn perform_search_with_context(ctx: &mut SearchContext) -> Result<SearchResult, String> {
76 if let Some(dataview) = ctx.current_data {
77 let data: Vec<Vec<String>> = (0..dataview.row_count())
79 .filter_map(|i| dataview.get_row(i))
80 .map(|row| {
81 row.values
82 .iter()
83 .map(std::string::ToString::to_string)
84 .collect()
85 })
86 .collect();
87
88 let matches = ctx.state_container.perform_search(&data);
90
91 let buffer_matches: Vec<(usize, usize)> = matches
93 .iter()
94 .map(|(row, col, _, _)| (*row, *col))
95 .collect();
96
97 if buffer_matches.is_empty() {
98 ctx.buffer.set_search_matches(Vec::new());
99
100 Ok(SearchResult {
101 matches_found: 0,
102 first_match: None,
103 status_message: "No matches found".to_string(),
104 })
105 } else {
106 let first_match = buffer_matches[0];
107
108 ctx.buffer.set_search_matches(buffer_matches.clone());
110 ctx.buffer.set_search_match_index(0);
111 ctx.buffer.set_current_match(Some(first_match));
112
113 Ok(SearchResult {
114 matches_found: buffer_matches.len(),
115 first_match: Some(first_match),
116 status_message: format!("Found {} matches", buffer_matches.len()),
117 })
118 }
119 } else {
120 Err("No data available for search".to_string())
121 }
122}
123
124pub fn apply_search_result(result: &SearchResult, ctx: &mut SearchContext) {
126 if let Some((row, _col)) = result.first_match {
127 ctx.state_container.set_table_selected_row(Some(row));
128 }
129
130 ctx.buffer.set_status_message(result.status_message.clone());
131}
132
133#[cfg(test)]
134mod tests {
135 use super::*;
136
137 #[test]
138 fn test_search_action_result_types() {
139 let success = SearchActionResult::Success(SearchResult {
141 matches_found: 5,
142 first_match: Some((0, 1)),
143 status_message: "Found 5 matches".to_string(),
144 });
145
146 match success {
147 SearchActionResult::Success(result) => {
148 assert_eq!(result.matches_found, 5);
149 assert_eq!(result.first_match, Some((0, 1)));
150 assert_eq!(result.status_message, "Found 5 matches");
151 }
152 _ => panic!("Expected Success result"),
153 }
154
155 let in_progress = SearchActionResult::InProgress("Filtering...".to_string());
156 match in_progress {
157 SearchActionResult::InProgress(msg) => {
158 assert_eq!(msg, "Filtering...");
159 }
160 _ => panic!("Expected InProgress result"),
161 }
162
163 let error = SearchActionResult::Error("No data".to_string());
164 match error {
165 SearchActionResult::Error(msg) => {
166 assert_eq!(msg, "No data");
167 }
168 _ => panic!("Expected Error result"),
169 }
170 }
171
172 #[test]
173 fn test_search_result_creation() {
174 let result = SearchResult {
175 matches_found: 0,
176 first_match: None,
177 status_message: "No matches found".to_string(),
178 };
179
180 assert_eq!(result.matches_found, 0);
181 assert_eq!(result.first_match, None);
182 assert_eq!(result.status_message, "No matches found");
183 }
184}