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| row.values.iter().map(|v| v.to_string()).collect())
81 .collect();
82
83 let matches = ctx.state_container.perform_search(&data);
85
86 let buffer_matches: Vec<(usize, usize)> = matches
88 .iter()
89 .map(|(row, col, _, _)| (*row, *col))
90 .collect();
91
92 if !buffer_matches.is_empty() {
93 let first_match = buffer_matches[0];
94
95 ctx.buffer.set_search_matches(buffer_matches.clone());
97 ctx.buffer.set_search_match_index(0);
98 ctx.buffer.set_current_match(Some(first_match));
99
100 Ok(SearchResult {
101 matches_found: buffer_matches.len(),
102 first_match: Some(first_match),
103 status_message: format!("Found {} matches", buffer_matches.len()),
104 })
105 } else {
106 ctx.buffer.set_search_matches(Vec::new());
107
108 Ok(SearchResult {
109 matches_found: 0,
110 first_match: None,
111 status_message: "No matches found".to_string(),
112 })
113 }
114 } else {
115 Err("No data available for search".to_string())
116 }
117}
118
119pub fn apply_search_result(result: &SearchResult, ctx: &mut SearchContext) {
121 if let Some((row, _col)) = result.first_match {
122 ctx.state_container.set_table_selected_row(Some(row));
123 }
124
125 ctx.buffer.set_status_message(result.status_message.clone());
126}
127
128#[cfg(test)]
129mod tests {
130 use super::*;
131
132 #[test]
133 fn test_search_action_result_types() {
134 let success = SearchActionResult::Success(SearchResult {
136 matches_found: 5,
137 first_match: Some((0, 1)),
138 status_message: "Found 5 matches".to_string(),
139 });
140
141 match success {
142 SearchActionResult::Success(result) => {
143 assert_eq!(result.matches_found, 5);
144 assert_eq!(result.first_match, Some((0, 1)));
145 assert_eq!(result.status_message, "Found 5 matches");
146 }
147 _ => panic!("Expected Success result"),
148 }
149
150 let in_progress = SearchActionResult::InProgress("Filtering...".to_string());
151 match in_progress {
152 SearchActionResult::InProgress(msg) => {
153 assert_eq!(msg, "Filtering...");
154 }
155 _ => panic!("Expected InProgress result"),
156 }
157
158 let error = SearchActionResult::Error("No data".to_string());
159 match error {
160 SearchActionResult::Error(msg) => {
161 assert_eq!(msg, "No data");
162 }
163 _ => panic!("Expected Error result"),
164 }
165 }
166
167 #[test]
168 fn test_search_result_creation() {
169 let result = SearchResult {
170 matches_found: 0,
171 first_match: None,
172 status_message: "No matches found".to_string(),
173 };
174
175 assert_eq!(result.matches_found, 0);
176 assert_eq!(result.first_match, None);
177 assert_eq!(result.status_message, "No matches found");
178 }
179}