use crate::models::log_line::LogLine;
use parking_lot::{lock_api::RwLockReadGuard, RawRwLock, RwLock};
pub trait AnalysisStore {
fn add_lines(&self, lines: &[LogLine]);
fn add_search_lines(&self, lines: &[LogLine]);
fn add_search_query(&self, query: &str);
fn get_search_query(&self) -> Option<String>;
fn reset_log(&self);
fn reset_search(&self);
fn fetch_log(&self) -> RwLockReadGuard<RawRwLock, Vec<LogLine>>;
fn fetch_search(&self) -> RwLockReadGuard<RawRwLock, Vec<LogLine>>;
fn get_log_lines(&self, from: usize, to: usize) -> Vec<LogLine>;
fn get_search_lines(&self, from: usize, to: usize) -> Vec<LogLine>;
fn get_log_lines_containing(
&self,
index: usize,
elements: usize,
) -> (Vec<LogLine>, usize, usize);
fn get_search_lines_containing(
&self,
index: usize,
elements: usize,
) -> (Vec<LogLine>, usize, usize);
fn get_total_filtered_lines(&self) -> usize;
fn get_total_searched_lines(&self) -> usize;
}
pub struct InMemmoryAnalysisStore {
log: RwLock<Vec<LogLine>>,
search_query: RwLock<Option<String>>,
search_log: RwLock<Vec<LogLine>>,
}
impl InMemmoryAnalysisStore {
pub fn new() -> Self {
Self {
log: RwLock::new(Vec::new()),
search_query: RwLock::new(None),
search_log: RwLock::new(Vec::new()),
}
}
}
impl Default for InMemmoryAnalysisStore {
fn default() -> Self {
Self::new()
}
}
impl AnalysisStore for InMemmoryAnalysisStore {
fn add_lines(&self, lines: &[LogLine]) {
let mut w = self.log.write();
for line in lines {
let index = w.len();
let mut line = line.clone();
line.index = index.to_string();
w.push(line);
}
}
fn add_search_lines(&self, lines: &[LogLine]) {
let mut w = self.search_log.write();
for line in lines {
w.push(line.clone());
}
}
fn add_search_query(&self, query: &str) {
let mut w = self.search_query.write();
*w = Some(query.to_string());
}
fn get_search_query(&self) -> Option<String> {
let r = self.search_query.read();
r.clone()
}
fn fetch_log(&self) -> RwLockReadGuard<RawRwLock, Vec<LogLine>> {
self.log.read()
}
fn fetch_search(&self) -> RwLockReadGuard<RawRwLock, Vec<LogLine>> {
self.search_log.read()
}
fn get_log_lines(&self, from: usize, to: usize) -> Vec<LogLine> {
let log = self.log.read();
log[from.min(log.len())..to.min(log.len())].to_vec()
}
fn get_search_lines(&self, from: usize, to: usize) -> Vec<LogLine> {
let log = self.search_log.read();
log[from.min(log.len())..to.min(log.len())].to_vec()
}
fn get_log_lines_containing(
&self,
index: usize,
elements: usize,
) -> (Vec<LogLine>, usize, usize) {
let log = self.log.read();
InMemmoryAnalysisStore::find_rolling_window(&log, index, elements)
}
fn get_search_lines_containing(
&self,
index: usize,
elements: usize,
) -> (Vec<LogLine>, usize, usize) {
let search_log = self.search_log.read();
InMemmoryAnalysisStore::find_rolling_window(&search_log, index, elements)
}
fn reset_log(&self) {
let mut w = self.log.write();
w.clear();
}
fn reset_search(&self) {
let mut w = self.search_log.write();
w.clear();
}
fn get_total_filtered_lines(&self) -> usize {
self.log.read().len()
}
fn get_total_searched_lines(&self) -> usize {
self.search_log.read().len()
}
}
impl InMemmoryAnalysisStore {
fn find_sorted_index(source: &[LogLine], index: usize) -> usize {
match source.binary_search_by(|e| {
e.index
.parse::<usize>()
.unwrap()
.cmp(&index)
}) {
Ok(i) => i,
Err(i) => i,
}
}
fn find_rolling_window(
source: &[LogLine],
index: usize,
elements: usize,
) -> (Vec<LogLine>, usize, usize) {
let closest = InMemmoryAnalysisStore::find_sorted_index(source, index);
let from = if (elements / 2) < closest {
closest - elements / 2
} else {
0
};
let to = (closest + elements / 2).min(source.len());
let lines = source[from..to].to_vec();
let index = InMemmoryAnalysisStore::find_sorted_index(&lines, index);
(lines, from, index)
}
}
#[cfg(test)]
mod tests {
use super::*;
fn log_line_with_index(index: usize) -> LogLine {
LogLine {
index: index.to_string(),
..Default::default()
}
}
}