Files
rlogg/src/search.rs

147 lines
3.7 KiB
Rust

use regex::Regex;
use std::fs::File;
use std::io::BufReader;
use std::sync::{Arc, Mutex};
use std::thread;
use crate::line_index::LineIndex;
use crate::types::FilteredLine;
pub const MAX_SEARCH_HISTORY: usize = 50;
pub struct SearchState {
pub searching: Arc<Mutex<bool>>,
pub progress: Arc<Mutex<f32>>,
pub results: Arc<Mutex<Option<Vec<FilteredLine>>>>,
}
impl SearchState {
pub fn new() -> Self {
Self {
searching: Arc::new(Mutex::new(false)),
progress: Arc::new(Mutex::new(0.0)),
results: Arc::new(Mutex::new(None)),
}
}
pub fn is_searching(&self) -> bool {
*self.searching.lock().unwrap()
}
pub fn get_progress(&self) -> f32 {
*self.progress.lock().unwrap()
}
pub fn take_results(&self) -> Option<Vec<FilteredLine>> {
self.results.lock().unwrap().take()
}
}
pub struct SearchParams {
pub query: String,
pub case_sensitive: bool,
pub use_regex: bool,
}
impl SearchParams {
fn build_regex_matcher(&self) -> Option<Regex> {
if !self.use_regex {
return None;
}
let pattern = if self.case_sensitive {
self.query.clone()
} else {
format!("(?i){}", self.query)
};
Regex::new(&pattern).ok()
}
fn matches_line(&self, content: &str, regex_matcher: &Option<Regex>) -> bool {
if let Some(regex) = regex_matcher {
regex.is_match(content)
} else if self.use_regex {
false
} else if self.case_sensitive {
content.contains(&self.query)
} else {
content.to_lowercase().contains(&self.query.to_lowercase())
}
}
}
pub fn start_search(
search_state: &SearchState,
params: SearchParams,
line_index: Arc<LineIndex>,
file_path: std::path::PathBuf,
) {
if search_state.is_searching() {
return;
}
let searching = Arc::clone(&search_state.searching);
let progress = Arc::clone(&search_state.progress);
let results = Arc::clone(&search_state.results);
*searching.lock().unwrap() = true;
*progress.lock().unwrap() = 0.0;
*results.lock().unwrap() = None;
thread::spawn(move || {
let filtered = search_lines(&params, &line_index, &file_path, &progress);
*results.lock().unwrap() = Some(filtered);
*progress.lock().unwrap() = 1.0;
*searching.lock().unwrap() = false;
});
}
fn search_lines(
params: &SearchParams,
line_index: &LineIndex,
file_path: &std::path::Path,
progress: &Arc<Mutex<f32>>,
) -> Vec<FilteredLine> {
let mut filtered = Vec::new();
let regex_matcher = params.build_regex_matcher();
if let Ok(file) = File::open(file_path) {
let mut file_handle = BufReader::new(file);
let total_lines = line_index.total_lines;
for line_num in 0..total_lines {
if let Some(content) = line_index.read_line(&mut file_handle, line_num) {
if params.matches_line(&content, &regex_matcher) {
filtered.push(FilteredLine {
line_number: line_num,
content,
});
}
}
if line_num % 1000 == 0 {
*progress.lock().unwrap() = line_num as f32 / total_lines as f32;
}
}
}
filtered
}
pub fn add_to_history(history: &mut Vec<String>, query: &str) {
if query.is_empty() {
return;
}
if let Some(pos) = history.iter().position(|x| x == query) {
history.remove(pos);
}
history.insert(0, query.to_string());
if history.len() > MAX_SEARCH_HISTORY {
history.truncate(MAX_SEARCH_HISTORY);
}
}