use log_source::source::log_source::LogSource;
use parking_lot::RwLock;
use rustc_hash::FxHashMap as HashMap;
use std::{iter::Iterator, ops::Range, sync::Arc};
pub trait LogStore {
fn add_log(
&self,
log_id: &str,
log_source: Arc<Box<dyn LogSource + Send + Sync>>,
format: Option<&String>,
enabled: bool,
);
fn add_line(&self, log_id: &str, line: &str);
fn add_lines(&self, log_id: &str, lines: &[String]) -> Range<usize>;
fn get_format(&self, log_id: &str) -> Option<String>;
fn get_logs(&self) -> Vec<(bool, String, Option<String>)>;
fn get_source(&self, id: &str) -> Option<Arc<Box<dyn LogSource + Send + Sync>>>;
fn get_lines(&self, log_id: &str) -> Vec<String>;
fn extract_lines(&self, log_id: &str) -> Vec<String>;
fn get_total_lines(&self) -> usize;
fn toggle_log(&self, log_id: &str);
}
pub struct InMemmoryLogStore {
raw_lines: RwLock<Vec<(String, Vec<String>)>>,
format: RwLock<HashMap<String, String>>,
enabled: RwLock<HashMap<String, bool>>,
source: RwLock<HashMap<String, Arc<Box<dyn LogSource + Send + Sync>>>>,
}
impl InMemmoryLogStore {
pub fn new() -> Self {
Self {
raw_lines: RwLock::new(Vec::default()),
format: RwLock::new(HashMap::default()),
enabled: RwLock::new(HashMap::default()),
source: RwLock::new(HashMap::default()),
}
}
}
impl Default for InMemmoryLogStore {
fn default() -> Self {
Self::new()
}
}
impl LogStore for InMemmoryLogStore {
fn add_log(
&self,
log_id: &str,
log_source: Arc<Box<dyn LogSource + Send + Sync>>,
format: Option<&String>,
enabled: bool,
) {
let (mut source_lock, mut format_lock, mut enabled_lock) = (
self.source.write(),
self.format.write(),
self.enabled.write(),
);
source_lock.insert(log_id.to_string(), log_source);
enabled_lock.insert(log_id.to_string(), enabled);
if let Some(format) = format {
format_lock.insert(log_id.to_string(), format.clone());
}
}
fn add_line(&self, log_id: &str, line: &str) {
let mut raw_lines_lock = self.raw_lines.write();
if !raw_lines_lock.iter().any(|(id, _)| log_id == id) {
raw_lines_lock.push((log_id.to_string(), Vec::new()));
}
let raw_lines = raw_lines_lock.iter_mut().find(|(id, _)| log_id == id).unwrap();
raw_lines.1.push(line.to_string());
}
fn add_lines(&self, log_id: &str, lines: &[String]) -> Range<usize> {
let mut raw_lines_lock = self.raw_lines.write();
if !raw_lines_lock.iter().any(|(id, _)| log_id == id) {
raw_lines_lock.push((log_id.to_string(), Vec::new()));
}
let (_, raw_lines) = raw_lines_lock.iter_mut().find(|(id, _)| log_id == id).unwrap();
let current_len = raw_lines.len();
raw_lines.append(&mut lines.to_vec());
let new_len = raw_lines.len();
current_len..new_len
}
fn get_lines(&self, log_id: &str) -> Vec<String> {
match self.raw_lines.read().iter().find(|(id, _)| log_id == id) {
Some((_, lines)) => lines.clone(),
_ => Vec::new(),
}
}
fn extract_lines(&self, log_id: &str) -> Vec<String> {
let mut w = self.raw_lines.write();
let (_, lines) = std::mem::take(w.iter_mut().find(|(id, _)| log_id == id).unwrap());
lines
}
fn get_logs(&self) -> Vec<(bool, String, Option<String>)> {
let (format_lock, enabled_lock) = (self.format.read(), self.enabled.read());
let logs: Vec<(bool, String, Option<String>)> = enabled_lock
.iter()
.map(|(path, enabled)| (*enabled, path.clone(), format_lock.get(path).cloned()))
.collect();
logs
}
fn get_format(&self, log_id: &str) -> Option<String> {
let format_lock = self.format.read();
format_lock.get(log_id).cloned()
}
fn get_total_lines(&self) -> usize {
self.raw_lines
.read()
.iter()
.fold(0, |acc, (_, v)| acc + v.len())
}
fn get_source(&self, id: &str) -> Option<Arc<Box<dyn LogSource + Send + Sync>>> {
if let Some((_id, source)) = self
.source
.read()
.iter()
.find(|(log_id, _source)| *id == **log_id)
{
Some(source.clone())
} else {
None
}
}
fn toggle_log(&self, log_id: &str) {
if let Some(e) = self.enabled.write().get_mut(log_id) {
*e = !*e;
}
}
}