log_analyzer/stores/
analysis_store.rs1use crate::models::log_line::LogLine;
2use parking_lot::{lock_api::RwLockReadGuard, RawRwLock, RwLock};
3
4pub trait AnalysisStore {
8 fn add_lines(&self, lines: &[LogLine]);
10 fn add_search_lines(&self, lines: &[LogLine]);
12 fn add_search_query(&self, query: &str);
14 fn get_search_query(&self) -> Option<String>;
16 fn reset_log(&self);
18 fn reset_search(&self);
20 fn fetch_log(&self) -> RwLockReadGuard<RawRwLock, Vec<LogLine>>;
22 fn fetch_search(&self) -> RwLockReadGuard<RawRwLock, Vec<LogLine>>;
24 fn get_log_lines(&self, from: usize, to: usize) -> Vec<LogLine>;
26 fn get_search_lines(&self, from: usize, to: usize) -> Vec<LogLine>;
28 fn get_log_lines_containing(
32 &self,
33 index: usize,
34 elements: usize,
35 ) -> (Vec<LogLine>, usize, usize);
36 fn get_search_lines_containing(
40 &self,
41 index: usize,
42 elements: usize,
43 ) -> (Vec<LogLine>, usize, usize);
44 fn get_total_filtered_lines(&self) -> usize;
46 fn get_total_searched_lines(&self) -> usize;
48}
49pub struct InMemmoryAnalysisStore {
50 log: RwLock<Vec<LogLine>>,
51 search_query: RwLock<Option<String>>,
52 search_log: RwLock<Vec<LogLine>>,
53}
54
55impl InMemmoryAnalysisStore {
56 pub fn new() -> Self {
57 Self {
58 log: RwLock::new(Vec::new()),
59 search_query: RwLock::new(None),
60 search_log: RwLock::new(Vec::new()),
61 }
62 }
63}
64
65impl Default for InMemmoryAnalysisStore {
66 fn default() -> Self {
67 Self::new()
68 }
69}
70
71impl AnalysisStore for InMemmoryAnalysisStore {
72 fn add_lines(&self, lines: &[LogLine]) {
73 let mut w = self.log.write();
74 for line in lines {
75 let index = w.len();
76
77 let mut line = line.clone();
78 line.index = index.to_string();
79
80 w.push(line);
81 }
82 }
83
84 fn add_search_lines(&self, lines: &[LogLine]) {
85 let mut w = self.search_log.write();
86 for line in lines {
87 w.push(line.clone());
88 }
89 }
90
91 fn add_search_query(&self, query: &str) {
92 let mut w = self.search_query.write();
93 *w = Some(query.to_string());
94 }
95
96 fn get_search_query(&self) -> Option<String> {
97 let r = self.search_query.read();
98 r.clone()
99 }
100
101 fn fetch_log(&self) -> RwLockReadGuard<RawRwLock, Vec<LogLine>> {
102 self.log.read()
103 }
104
105 fn fetch_search(&self) -> RwLockReadGuard<RawRwLock, Vec<LogLine>> {
106 self.search_log.read()
107 }
108
109 fn get_log_lines(&self, from: usize, to: usize) -> Vec<LogLine> {
110 let log = self.log.read();
111 log[from.min(log.len())..to.min(log.len())].to_vec()
112 }
113
114 fn get_search_lines(&self, from: usize, to: usize) -> Vec<LogLine> {
115 let log = self.search_log.read();
116 log[from.min(log.len())..to.min(log.len())].to_vec()
117 }
118
119 fn get_log_lines_containing(
120 &self,
121 index: usize,
122 elements: usize,
123 ) -> (Vec<LogLine>, usize, usize) {
124 let log = self.log.read();
125 InMemmoryAnalysisStore::find_rolling_window(&log, index, elements)
126 }
127
128 fn get_search_lines_containing(
129 &self,
130 index: usize,
131 elements: usize,
132 ) -> (Vec<LogLine>, usize, usize) {
133 let search_log = self.search_log.read();
134 InMemmoryAnalysisStore::find_rolling_window(&search_log, index, elements)
135 }
136
137 fn reset_log(&self) {
138 let mut w = self.log.write();
139 w.clear();
140 }
141
142 fn reset_search(&self) {
143 let mut w = self.search_log.write();
144 w.clear();
145 }
146
147 fn get_total_filtered_lines(&self) -> usize {
148 self.log.read().len()
149 }
150
151 fn get_total_searched_lines(&self) -> usize {
152 self.search_log.read().len()
153 }
154}
155
156impl InMemmoryAnalysisStore {
157 fn find_sorted_index(source: &[LogLine], index: usize) -> usize {
158 match source.binary_search_by(|e| {
159 e.index
160 .parse::<usize>()
161 .unwrap()
162 .cmp(&index)
163 }) {
164 Ok(i) => i,
165 Err(i) => i,
166 }
167 }
168
169 fn find_rolling_window(
172 source: &[LogLine],
173 index: usize,
174 elements: usize,
175 ) -> (Vec<LogLine>, usize, usize) {
176 let closest = InMemmoryAnalysisStore::find_sorted_index(source, index);
177 let from = if (elements / 2) < closest {
178 closest - elements / 2
179 } else {
180 0
181 };
182 let to = (closest + elements / 2).min(source.len());
183
184 let lines = source[from..to].to_vec();
185 let index = InMemmoryAnalysisStore::find_sorted_index(&lines, index);
186 (lines, from, index)
187 }
188}
189
190#[cfg(test)]
191mod tests {
192 use super::*;
193
194 fn log_line_with_index(index: usize) -> LogLine {
195 LogLine {
196 index: index.to_string(),
197 ..Default::default()
198 }
199 }
200}