use crate::app_state_container::AppStateContainer;
use crate::buffer::BufferAPI;
use crate::data::data_view::DataView;
use crate::widgets::search_modes_widget::SearchMode;
pub struct SearchContext<'a> {
pub state_container: &'a AppStateContainer,
pub buffer: &'a mut dyn BufferAPI,
pub current_data: Option<&'a DataView>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct SearchResult {
pub matches_found: usize,
pub first_match: Option<(usize, usize)>, pub status_message: String,
}
#[derive(Debug, Clone, PartialEq)]
pub enum SearchActionResult {
Success(SearchResult),
InProgress(String),
Error(String),
}
pub fn execute_search_action(
mode: SearchMode,
pattern: String,
ctx: &mut SearchContext,
) -> SearchActionResult {
match mode {
SearchMode::Search => {
ctx.state_container.start_search(pattern.clone());
ctx.buffer.set_search_pattern(pattern);
let search_result = perform_search_with_context(ctx);
match search_result {
Ok(result) => SearchActionResult::Success(result),
Err(e) => SearchActionResult::Error(e),
}
}
SearchMode::Filter => {
SearchActionResult::InProgress(format!("Filter mode with pattern: {pattern}"))
}
SearchMode::FuzzyFilter => {
SearchActionResult::InProgress(format!("Fuzzy filter mode with pattern: {pattern}"))
}
SearchMode::ColumnSearch => {
SearchActionResult::InProgress(format!("Column search mode with pattern: {pattern}"))
}
}
}
fn perform_search_with_context(ctx: &mut SearchContext) -> Result<SearchResult, String> {
if let Some(dataview) = ctx.current_data {
let data: Vec<Vec<String>> = (0..dataview.row_count())
.filter_map(|i| dataview.get_row(i))
.map(|row| {
row.values
.iter()
.map(std::string::ToString::to_string)
.collect()
})
.collect();
let matches = ctx.state_container.perform_search(&data);
let buffer_matches: Vec<(usize, usize)> = matches
.iter()
.map(|(row, col, _, _)| (*row, *col))
.collect();
if buffer_matches.is_empty() {
ctx.buffer.set_search_matches(Vec::new());
Ok(SearchResult {
matches_found: 0,
first_match: None,
status_message: "No matches found".to_string(),
})
} else {
let first_match = buffer_matches[0];
ctx.buffer.set_search_matches(buffer_matches.clone());
ctx.buffer.set_search_match_index(0);
ctx.buffer.set_current_match(Some(first_match));
Ok(SearchResult {
matches_found: buffer_matches.len(),
first_match: Some(first_match),
status_message: format!("Found {} matches", buffer_matches.len()),
})
}
} else {
Err("No data available for search".to_string())
}
}
pub fn apply_search_result(result: &SearchResult, ctx: &mut SearchContext) {
if let Some((row, _col)) = result.first_match {
ctx.state_container.set_table_selected_row(Some(row));
}
ctx.buffer.set_status_message(result.status_message.clone());
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_search_action_result_types() {
let success = SearchActionResult::Success(SearchResult {
matches_found: 5,
first_match: Some((0, 1)),
status_message: "Found 5 matches".to_string(),
});
match success {
SearchActionResult::Success(result) => {
assert_eq!(result.matches_found, 5);
assert_eq!(result.first_match, Some((0, 1)));
assert_eq!(result.status_message, "Found 5 matches");
}
_ => panic!("Expected Success result"),
}
let in_progress = SearchActionResult::InProgress("Filtering...".to_string());
match in_progress {
SearchActionResult::InProgress(msg) => {
assert_eq!(msg, "Filtering...");
}
_ => panic!("Expected InProgress result"),
}
let error = SearchActionResult::Error("No data".to_string());
match error {
SearchActionResult::Error(msg) => {
assert_eq!(msg, "No data");
}
_ => panic!("Expected Error result"),
}
}
#[test]
fn test_search_result_creation() {
let result = SearchResult {
matches_found: 0,
first_match: None,
status_message: "No matches found".to_string(),
};
assert_eq!(result.matches_found, 0);
assert_eq!(result.first_match, None);
assert_eq!(result.status_message, "No matches found");
}
}