use super::types::JsonNode;
use std::collections::HashSet;
pub trait Search {
fn search(&mut self, query: &str);
fn clear_search(&mut self);
fn match_count(&self) -> usize;
fn is_searching(&self) -> bool;
fn next_match(&mut self);
fn prev_match(&mut self);
}
#[derive(Clone, Debug)]
pub struct SearchState {
pub search_query: String,
pub search_matches: Vec<String>,
pub current_match: usize,
pub collapsed: HashSet<String>,
}
impl SearchState {
pub fn new() -> Self {
Self {
search_query: String::new(),
search_matches: Vec::new(),
current_match: 0,
collapsed: HashSet::new(),
}
}
pub fn search_recursive(&mut self, node: &JsonNode) {
if node.key.to_lowercase().contains(&self.search_query) {
self.search_matches.push(node.path.clone());
}
else if let Some(value) = &node.value {
if value.to_lowercase().contains(&self.search_query) {
self.search_matches.push(node.path.clone());
}
}
for child in &node.children {
self.search_recursive(child);
}
}
pub fn go_to_match(
&mut self,
selected: &mut usize,
get_visible_nodes: impl Fn(&Self) -> Vec<JsonNode>,
) {
if let Some(path) = self.search_matches.get(self.current_match) {
let parts: Vec<&str> = path.split('.').collect();
let mut current_path = String::new();
for (i, part) in parts.iter().enumerate() {
if i > 0 {
current_path.push('.');
}
current_path.push_str(part);
self.collapsed.remove(¤t_path);
}
let visible = get_visible_nodes(self);
for (idx, node) in visible.iter().enumerate() {
if &node.path == path {
*selected = idx;
break;
}
}
}
}
}
impl Default for SearchState {
fn default() -> Self {
Self::new()
}
}