forward filters (ugly)
This commit is contained in:
parent
1f6679f57f
commit
de51666742
8 changed files with 167 additions and 97 deletions
|
|
@ -25,7 +25,7 @@ mod serialize_regex {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
pub enum MatcherValue {
|
pub enum MatcherValue {
|
||||||
Exact(String),
|
Exact(String),
|
||||||
Regex(#[serde(with = "serialize_regex")] Regex),
|
Regex(#[serde(with = "serialize_regex")] Regex),
|
||||||
|
|
@ -53,7 +53,7 @@ impl MatcherValue {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
pub enum Matcher {
|
pub enum Matcher {
|
||||||
Field { name: String, value: MatcherValue },
|
Field { name: String, value: MatcherValue },
|
||||||
Message { value: MatcherValue },
|
Message { value: MatcherValue },
|
||||||
|
|
@ -98,13 +98,13 @@ impl Matcher {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Serialize, Deserialize)]
|
#[derive(Clone, Serialize, Deserialize, Debug)]
|
||||||
pub enum FilterKind {
|
pub enum FilterKind {
|
||||||
Inline,
|
Inline,
|
||||||
Remove,
|
Remove,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
pub struct Filter {
|
pub struct Filter {
|
||||||
pub matcher: Matcher,
|
pub matcher: Matcher,
|
||||||
pub kind: FilterKind,
|
pub kind: FilterKind,
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::tui::filter::Filter;
|
use crate::tui::filter::Filter;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
pub struct Filters {
|
pub struct Filters {
|
||||||
filters: Vec<Arc<Filter>>,
|
filters: Vec<Arc<Filter>>,
|
||||||
undo_pos: usize,
|
undo_pos: usize,
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use std::{collections::HashMap, iter, mem, sync::Arc};
|
use std::{collections::HashMap, fs::File, io::Write, iter, mem, sync::Arc};
|
||||||
|
|
||||||
use crate::tui::{
|
use crate::tui::{
|
||||||
filter::Filter,
|
filter::Filter,
|
||||||
|
|
@ -8,7 +8,7 @@ use crate::tui::{
|
||||||
view::LogView,
|
view::LogView,
|
||||||
},
|
},
|
||||||
model::LogEntry,
|
model::LogEntry,
|
||||||
processing::{IntoLogStream, LogStream},
|
processing::{FilterList, IntoLogStream, LogStream},
|
||||||
};
|
};
|
||||||
use tui_widget_list::ListState;
|
use tui_widget_list::ListState;
|
||||||
|
|
||||||
|
|
@ -56,82 +56,95 @@ impl LogViewer {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn filtered_root_stream(&self) -> Box<dyn LogStream> {
|
pub fn filtered_root_stream(&self) -> Box<dyn LogStream> {
|
||||||
let mut curr = self.root_stream.clone();
|
self.root_stream.with_filters(self.filters.get())
|
||||||
for filter in self.filters.get() {
|
|
||||||
curr = Box::new(curr.filter(Arc::clone(filter)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
curr
|
fn update_filters(&mut self, old_filters: &FilterList) {
|
||||||
}
|
|
||||||
|
|
||||||
fn update_filters(&mut self) {
|
|
||||||
self.cache.clear();
|
self.cache.clear();
|
||||||
let offsets_list: Vec<_> = self
|
let offsets_list: Vec<_> = self
|
||||||
.stack
|
.stack
|
||||||
.iter()
|
.iter()
|
||||||
.map(|i| (i.selected(), i.selection_offset))
|
.map(|i| (i.selected(old_filters), i.selection_offset))
|
||||||
.chain(iter::once((
|
.chain(iter::once((
|
||||||
self.curr.selected(),
|
self.curr.selected(old_filters),
|
||||||
self.curr.selection_offset,
|
self.curr.selection_offset,
|
||||||
)))
|
)))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
fn find_elem_in_stream(
|
let mut log = File::options()
|
||||||
stream: &dyn LogStream,
|
.create(true)
|
||||||
elem: &Arc<LogEntry>,
|
.append(true)
|
||||||
) -> Option<Box<dyn LogStream>> {
|
.open("./log")
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
writeln!(&mut log, "active filters: {:?}", self.filters.get()).unwrap();
|
||||||
|
|
||||||
|
writeln!(
|
||||||
|
&mut log,
|
||||||
|
"{:?}",
|
||||||
|
offsets_list.iter().map(|i| i.1).collect::<Vec<_>>()
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let find_elem_in_stream =
|
||||||
|
|stream: &dyn LogStream, elem: &Arc<LogEntry>| -> Option<Box<dyn LogStream>> {
|
||||||
let mut temp_stream = stream.clone();
|
let mut temp_stream = stream.clone();
|
||||||
let mut max = 100usize;
|
while let Some((curr, _)) = temp_stream.next(self.filters.get()) {
|
||||||
while let Some((curr, _)) = temp_stream.next() {
|
|
||||||
if Arc::ptr_eq(&curr, elem) {
|
if Arc::ptr_eq(&curr, elem) {
|
||||||
return Some(temp_stream);
|
return Some(temp_stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
if max == 0 {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
max -= 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
None
|
None
|
||||||
}
|
};
|
||||||
|
|
||||||
let mut current_stream = self.filtered_root_stream();
|
let mut current_stream = self.filtered_root_stream();
|
||||||
let mut new_stack = Vec::<LogView>::new();
|
let mut new_stack = Vec::<LogView>::new();
|
||||||
|
let mut missing = false;
|
||||||
|
|
||||||
'outer: for (elem, old_offset) in offsets_list {
|
for (elem, old_offset) in offsets_list {
|
||||||
let Some((elem, _)) = elem else {
|
let Some((elem, inline_depth)) = elem else {
|
||||||
|
writeln!(&mut log, "no elem").unwrap();
|
||||||
break;
|
break;
|
||||||
};
|
};
|
||||||
|
writeln!(
|
||||||
// If the value we're looking for is removed by the filter,
|
&mut log,
|
||||||
// we'll have a hard time finding it so quit
|
"reconstruction {:?} prev at {old_offset:?}",
|
||||||
// if filter.removes(&elem) {
|
elem.message_or_name()
|
||||||
// break;
|
)
|
||||||
// }
|
.unwrap();
|
||||||
|
|
||||||
// find the nearest stream in which this element can be found
|
// find the nearest stream in which this element can be found
|
||||||
let mut curr = current_stream.as_ref();
|
let mut curr = current_stream.as_ref();
|
||||||
let mut parents = new_stack.iter().rev();
|
let mut parents = new_stack.iter().rev();
|
||||||
let mut found_in_toplevel = true;
|
let mut found_in_toplevel = true;
|
||||||
let mut stream = loop {
|
let mut stream = loop {
|
||||||
|
writeln!(&mut log, "find in stream").unwrap();
|
||||||
if let Some(stream) = find_elem_in_stream(curr, &elem) {
|
if let Some(stream) = find_elem_in_stream(curr, &elem) {
|
||||||
|
writeln!(&mut log, "found (toplevel={found_in_toplevel})").unwrap();
|
||||||
break stream;
|
break stream;
|
||||||
}
|
}
|
||||||
found_in_toplevel = false;
|
found_in_toplevel = false;
|
||||||
|
|
||||||
if let Some(parent) = parents.next() {
|
if let Some(parent) = parents.next() {
|
||||||
|
writeln!(&mut log, "searching parent").unwrap();
|
||||||
curr = parent.iter.as_ref();
|
curr = parent.iter.as_ref();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
break 'outer;
|
writeln!(&mut log, "no more parents").unwrap();
|
||||||
|
missing = true;
|
||||||
|
|
||||||
|
// TODO: better guess at how far down we need to go
|
||||||
|
// now its just 0
|
||||||
|
|
||||||
|
break current_stream;
|
||||||
};
|
};
|
||||||
|
|
||||||
let offset = if found_in_toplevel {
|
let offset = if found_in_toplevel {
|
||||||
let mut offset = 0;
|
let mut offset = 0;
|
||||||
for _ in 0..old_offset {
|
for _ in 0..old_offset {
|
||||||
if stream.prev().is_none() {
|
if stream.prev(self.filters.get()).is_none() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
offset += 1;
|
offset += 1;
|
||||||
|
|
@ -142,29 +155,60 @@ impl LogViewer {
|
||||||
0
|
0
|
||||||
};
|
};
|
||||||
|
|
||||||
current_stream = stream.clone();
|
writeln!(&mut log, "new offset: {offset:?}").unwrap();
|
||||||
|
|
||||||
|
let nested_stream = elem.from_start(inline_depth);
|
||||||
new_stack.push(LogView {
|
new_stack.push(LogView {
|
||||||
iter: stream,
|
iter: stream,
|
||||||
selection_offset: offset,
|
selection_offset: offset,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// If we found a missing entry, don't try deeper entries,
|
||||||
|
// they won't exist
|
||||||
|
if missing {
|
||||||
|
break;
|
||||||
|
} else if let Some(nested_stream) = nested_stream {
|
||||||
|
current_stream = nested_stream.with_filters(self.filters.get());
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
writeln!(
|
||||||
|
&mut log,
|
||||||
|
"{:?}",
|
||||||
|
new_stack
|
||||||
|
.iter()
|
||||||
|
.map(|i| (
|
||||||
|
i.iter
|
||||||
|
.clone()
|
||||||
|
.next(self.filters.get())
|
||||||
|
.map(|i| i.0.message_or_name()),
|
||||||
|
i.selection_offset
|
||||||
|
))
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
// Take the top of the stack as `curr`, unless
|
// Take the top of the stack as `curr`, unless
|
||||||
// the new stack is empty, then just reset the current view.
|
// the new stack is empty, then just reset the current view.
|
||||||
if let Some(curr) = new_stack.pop() {
|
if let Some(curr) = new_stack.pop() {
|
||||||
|
writeln!(&mut log, "popped new curr off stack").unwrap();
|
||||||
self.curr = curr;
|
self.curr = curr;
|
||||||
} else {
|
} else {
|
||||||
|
writeln!(&mut log, "reconstructing root, somehow").unwrap();
|
||||||
self.curr = LogView {
|
self.curr = LogView {
|
||||||
iter: self.filtered_root_stream().clone(),
|
iter: self.filtered_root_stream().clone(),
|
||||||
selection_offset: 0,
|
selection_offset: self.stack.first().unwrap_or(&self.curr).selection_offset,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
self.stack = new_stack;
|
self.stack = new_stack;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_filter(&mut self, filter: Arc<Filter>) {
|
pub fn add_filter(&mut self, filter: Arc<Filter>) {
|
||||||
|
let old_filters = self.filters.clone();
|
||||||
self.filters.push(Arc::clone(&filter));
|
self.filters.push(Arc::clone(&filter));
|
||||||
self.update_filters();
|
self.update_filters(old_filters.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_num_items(&mut self, num_visible_items: usize) {
|
pub fn update_num_items(&mut self, num_visible_items: usize) {
|
||||||
|
|
@ -172,7 +216,7 @@ impl LogViewer {
|
||||||
if self.curr.selection_offset == 0 {
|
if self.curr.selection_offset == 0 {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
self.curr.iter.next();
|
self.curr.iter.next(self.filters.get());
|
||||||
self.curr.selection_offset -= 1;
|
self.curr.selection_offset -= 1;
|
||||||
}
|
}
|
||||||
self.last_height = num_visible_items;
|
self.last_height = num_visible_items;
|
||||||
|
|
@ -204,7 +248,7 @@ impl LogViewer {
|
||||||
let mut temp_iter = self.curr.iter.clone();
|
let mut temp_iter = self.curr.iter.clone();
|
||||||
let mut res = Vec::new();
|
let mut res = Vec::new();
|
||||||
for _ in 0..max {
|
for _ in 0..max {
|
||||||
let Some(i) = temp_iter.next() else {
|
let Some(i) = temp_iter.next(self.filters.get()) else {
|
||||||
break;
|
break;
|
||||||
};
|
};
|
||||||
res.push(i);
|
res.push(i);
|
||||||
|
|
@ -242,7 +286,7 @@ impl LogViewer {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn selected(&self) -> Option<(Arc<LogEntry>, usize)> {
|
pub fn selected(&self) -> Option<(Arc<LogEntry>, usize)> {
|
||||||
self.curr.selected()
|
self.curr.selected(self.filters.get())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn prev(&mut self) {
|
pub fn prev(&mut self) {
|
||||||
|
|
@ -253,7 +297,7 @@ impl LogViewer {
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
if self.curr.selection_offset == 0 {
|
if self.curr.selection_offset == 0 {
|
||||||
let _ = self.curr.iter.prev();
|
let _ = self.curr.iter.prev(self.filters.get());
|
||||||
} else {
|
} else {
|
||||||
self.curr.selection_offset -= 1;
|
self.curr.selection_offset -= 1;
|
||||||
}
|
}
|
||||||
|
|
@ -283,7 +327,7 @@ impl LogViewer {
|
||||||
pub fn page_up(&mut self) {
|
pub fn page_up(&mut self) {
|
||||||
for _ in 0..self.last_height {
|
for _ in 0..self.last_height {
|
||||||
if self.curr.selection_offset == 0 {
|
if self.curr.selection_offset == 0 {
|
||||||
let _ = self.curr.iter.prev();
|
let _ = self.curr.iter.prev(self.filters.get());
|
||||||
} else {
|
} else {
|
||||||
self.curr.selection_offset -= 1;
|
self.curr.selection_offset -= 1;
|
||||||
}
|
}
|
||||||
|
|
@ -299,7 +343,7 @@ impl LogViewer {
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
self.curr.selection_offset = 0;
|
self.curr.selection_offset = 0;
|
||||||
while self.curr.iter.prev().is_some() {}
|
while self.curr.iter.prev(self.filters.get()).is_some() {}
|
||||||
self.input_state = InputState::None;
|
self.input_state = InputState::None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -318,13 +362,15 @@ impl LogViewer {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn undo(&mut self) {
|
pub fn undo(&mut self) {
|
||||||
|
let old_filters = self.filters.clone();
|
||||||
self.filters.undo();
|
self.filters.undo();
|
||||||
self.update_filters();
|
self.update_filters(old_filters.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn redo(&mut self) {
|
pub fn redo(&mut self) {
|
||||||
|
let old_filters = self.filters.clone();
|
||||||
self.filters.redo();
|
self.filters.redo();
|
||||||
self.update_filters();
|
self.update_filters(old_filters.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn enter(&mut self) {
|
pub fn enter(&mut self) {
|
||||||
|
|
@ -333,21 +379,24 @@ impl LogViewer {
|
||||||
let Some((s, _)) = self.selected() else {
|
let Some((s, _)) = self.selected() else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let Some(i) = s.from_start(0) else {
|
let Some(i) = s.from_start(0).map(|i| i.with_filters(self.filters.get())) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
if i.clone().next().is_none() {
|
if i.clone().next(self.filters.get()).is_none() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if i.clone().next().is_some_and(|(i, _)| i.is_return()) {
|
if i.clone()
|
||||||
|
.next(self.filters.get())
|
||||||
|
.is_some_and(|(i, _)| i.is_return())
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.stack.push(mem::replace(
|
self.stack.push(mem::replace(
|
||||||
&mut self.curr,
|
&mut self.curr,
|
||||||
LogView {
|
LogView {
|
||||||
iter: Box::new(i),
|
iter: i,
|
||||||
selection_offset: 0,
|
selection_offset: 0,
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::tui::{model::LogEntry, processing::LogStream};
|
use crate::tui::{
|
||||||
|
model::LogEntry,
|
||||||
|
processing::{FilterList, LogStream},
|
||||||
|
};
|
||||||
|
|
||||||
pub struct LogView {
|
pub struct LogView {
|
||||||
pub iter: Box<dyn LogStream>,
|
pub iter: Box<dyn LogStream>,
|
||||||
|
|
@ -8,13 +11,13 @@ pub struct LogView {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LogView {
|
impl LogView {
|
||||||
pub fn selected(&self) -> Option<(Arc<LogEntry>, usize)> {
|
pub fn selected(&self, fl: &FilterList) -> Option<(Arc<LogEntry>, usize)> {
|
||||||
let mut temp_iter = self.iter.clone();
|
let mut temp_iter = self.iter.clone();
|
||||||
for _ in 0..self.selection_offset {
|
for _ in 0..self.selection_offset {
|
||||||
let _ = temp_iter.next()?;
|
let _ = temp_iter.next(fl)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
temp_iter.next()
|
temp_iter.next(fl)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -149,22 +149,22 @@ impl LogEntry {
|
||||||
enter, sub_entries, ..
|
enter, sub_entries, ..
|
||||||
} => {
|
} => {
|
||||||
if let Some(val) = enter.all_fields().fields.get("name") {
|
if let Some(val) = enter.all_fields().fields.get("name") {
|
||||||
LineText::new(
|
let (prefix, sym) = if sub_entries.is_empty()
|
||||||
if sub_entries.is_empty()
|
|
||||||
|| sub_entries.get(0).is_some_and(|i| i.is_return())
|
|| sub_entries.get(0).is_some_and(|i| i.is_return())
|
||||||
{
|
{
|
||||||
SPACES_BEFORE.to_string()
|
(SPACES_BEFORE.to_string(), "⟲")
|
||||||
} else {
|
} else {
|
||||||
|
(
|
||||||
format!(
|
format!(
|
||||||
"{:4}⭣{:4}⇊ ",
|
"{:4}⭣{:4}⇊ ",
|
||||||
sub_entries.len(),
|
sub_entries.len(),
|
||||||
self.count().wrapping_sub(1)
|
self.count().wrapping_sub(1)
|
||||||
|
),
|
||||||
|
"↪",
|
||||||
)
|
)
|
||||||
},
|
};
|
||||||
format!("↪ {val}"),
|
|
||||||
self.message_or_name(),
|
LineText::new(prefix, format!("{sym} {val}"), self.message_or_name(), tree)
|
||||||
tree,
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
LineText::new(
|
LineText::new(
|
||||||
SPACES_BEFORE.to_string(),
|
SPACES_BEFORE.to_string(),
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
pub type FilterList = [Arc<Filter>];
|
||||||
|
|
||||||
use crate::tui::{
|
use crate::tui::{
|
||||||
filter::{Filter, FilterKind},
|
filter::{Filter, FilterKind},
|
||||||
model::LogEntry,
|
model::LogEntry,
|
||||||
|
|
@ -19,7 +21,7 @@ pub struct LogEntryStream {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LogStream for LogEntryStream {
|
impl LogStream for LogEntryStream {
|
||||||
fn next(&mut self) -> Option<(Arc<LogEntry>, usize)> {
|
fn next(&mut self, _fl: &FilterList) -> Option<(Arc<LogEntry>, usize)> {
|
||||||
let LogEntry::Sub { sub_entries, .. } = self.inner.as_ref() else {
|
let LogEntry::Sub { sub_entries, .. } = self.inner.as_ref() else {
|
||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
|
|
@ -29,7 +31,7 @@ impl LogStream for LogEntryStream {
|
||||||
Some((Arc::clone(res), self.inline_depth))
|
Some((Arc::clone(res), self.inline_depth))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn prev(&mut self) -> Option<(Arc<LogEntry>, usize)> {
|
fn prev(&mut self, _fl: &FilterList) -> Option<(Arc<LogEntry>, usize)> {
|
||||||
let LogEntry::Sub { sub_entries, .. } = self.inner.as_ref() else {
|
let LogEntry::Sub { sub_entries, .. } = self.inner.as_ref() else {
|
||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
|
|
@ -85,8 +87,8 @@ impl IntoLogStream for &Arc<LogEntry> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait LogStream {
|
pub trait LogStream {
|
||||||
fn next(&mut self) -> Option<(Arc<LogEntry>, usize)>;
|
fn next(&mut self, fl: &FilterList) -> Option<(Arc<LogEntry>, usize)>;
|
||||||
fn prev(&mut self) -> Option<(Arc<LogEntry>, usize)>;
|
fn prev(&mut self, fl: &FilterList) -> Option<(Arc<LogEntry>, usize)>;
|
||||||
|
|
||||||
fn enclosing_log_entry(&self) -> Option<(Arc<LogEntry>, usize)>;
|
fn enclosing_log_entry(&self) -> Option<(Arc<LogEntry>, usize)>;
|
||||||
|
|
||||||
|
|
@ -98,6 +100,15 @@ pub trait LogStream {
|
||||||
stack: vec![(self.enclosing_log_entry(), self.clone())],
|
stack: vec![(self.enclosing_log_entry(), self.clone())],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn with_filters(&self, fl: &FilterList) -> Box<dyn LogStream> {
|
||||||
|
let mut curr = self.clone();
|
||||||
|
for filter in fl {
|
||||||
|
curr = Box::new(curr.filter(Arc::clone(filter)));
|
||||||
|
}
|
||||||
|
|
||||||
|
curr
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct FilteredLogStream {
|
pub struct FilteredLogStream {
|
||||||
|
|
@ -106,11 +117,11 @@ pub struct FilteredLogStream {
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! generate_candidate {
|
macro_rules! generate_candidate {
|
||||||
($_self: tt, $iter_method: ident, $forwards: expr) => {
|
($_self: tt, $iter_method: ident, $fl: ident, $forwards: expr) => {
|
||||||
loop {
|
loop {
|
||||||
let stack_len = $_self.stack.len();
|
let stack_len = $_self.stack.len();
|
||||||
let (_enclosing, top) = $_self.stack.last_mut().unwrap();
|
let (_enclosing, top) = $_self.stack.last_mut().unwrap();
|
||||||
if let Some((top, inline_depth)) = top.$iter_method() {
|
if let Some((top, inline_depth)) = top.$iter_method($fl) {
|
||||||
// if we can find it in the top of stack iterator, neat!
|
// if we can find it in the top of stack iterator, neat!
|
||||||
return Some((top, inline_depth));
|
return Some((top, inline_depth));
|
||||||
} else if stack_len > 1 {
|
} else if stack_len > 1 {
|
||||||
|
|
@ -126,9 +137,9 @@ macro_rules! generate_candidate {
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! generate_filter {
|
macro_rules! generate_filter {
|
||||||
($_self: tt, $candidate: ident, $into_iter: ident, $forwards: expr) => {
|
($_self: tt, $candidate: ident, $fl: ident, $into_iter: ident, $forwards: expr) => {
|
||||||
loop {
|
loop {
|
||||||
let (elem, inline_depth) = $_self.$candidate()?;
|
let (elem, inline_depth) = $_self.$candidate($fl)?;
|
||||||
|
|
||||||
let Filter { matcher, kind } = $_self.filter.as_ref();
|
let Filter { matcher, kind } = $_self.filter.as_ref();
|
||||||
|
|
||||||
|
|
@ -138,9 +149,10 @@ macro_rules! generate_filter {
|
||||||
// When we inline, add this item to the stack
|
// When we inline, add this item to the stack
|
||||||
// so we continue iterating inside it.
|
// so we continue iterating inside it.
|
||||||
if let Some(iter) = elem.$into_iter(inline_depth + 1) {
|
if let Some(iter) = elem.$into_iter(inline_depth + 1) {
|
||||||
|
let iter = iter.with_filters($fl);
|
||||||
$_self
|
$_self
|
||||||
.stack
|
.stack
|
||||||
.push((Some((Arc::clone(&elem), inline_depth)), Box::new(iter)));
|
.push((Some((Arc::clone(&elem), inline_depth)), iter));
|
||||||
}
|
}
|
||||||
// Continue so we actually return a nested item.
|
// Continue so we actually return a nested item.
|
||||||
if $forwards {
|
if $forwards {
|
||||||
|
|
@ -162,21 +174,21 @@ macro_rules! generate_filter {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FilteredLogStream {
|
impl FilteredLogStream {
|
||||||
fn next_candidate(&mut self) -> Option<(Arc<LogEntry>, usize)> {
|
fn next_candidate(&mut self, fl: &FilterList) -> Option<(Arc<LogEntry>, usize)> {
|
||||||
generate_candidate!(self, next, true)
|
generate_candidate!(self, next, fl, true)
|
||||||
}
|
}
|
||||||
fn prev_candidate(&mut self) -> Option<(Arc<LogEntry>, usize)> {
|
fn prev_candidate(&mut self, fl: &FilterList) -> Option<(Arc<LogEntry>, usize)> {
|
||||||
generate_candidate!(self, prev, false)
|
generate_candidate!(self, prev, fl, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LogStream for FilteredLogStream {
|
impl LogStream for FilteredLogStream {
|
||||||
fn next(&mut self) -> Option<(Arc<LogEntry>, usize)> {
|
fn next(&mut self, fl: &FilterList) -> Option<(Arc<LogEntry>, usize)> {
|
||||||
generate_filter!(self, next_candidate, from_start, true)
|
generate_filter!(self, next_candidate, fl, from_start, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn prev(&mut self) -> Option<(Arc<LogEntry>, usize)> {
|
fn prev(&mut self, fl: &FilterList) -> Option<(Arc<LogEntry>, usize)> {
|
||||||
generate_filter!(self, prev_candidate, from_end, false)
|
generate_filter!(self, prev_candidate, fl, from_end, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clone(&self) -> Box<dyn LogStream> {
|
fn clone(&self) -> Box<dyn LogStream> {
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ use std::{
|
||||||
|
|
||||||
use crate::tui::{
|
use crate::tui::{
|
||||||
model::{LogEntry, RawLogEntry},
|
model::{LogEntry, RawLogEntry},
|
||||||
processing::LogStream,
|
processing::{FilterList, LogStream},
|
||||||
};
|
};
|
||||||
|
|
||||||
struct LogFileEntryGenerator {
|
struct LogFileEntryGenerator {
|
||||||
|
|
@ -139,14 +139,14 @@ pub struct LogFileReaderStream {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LogStream for LogFileReaderStream {
|
impl LogStream for LogFileReaderStream {
|
||||||
fn next(&mut self) -> Option<(Arc<LogEntry>, usize)> {
|
fn next(&mut self, _fl: &FilterList) -> Option<(Arc<LogEntry>, usize)> {
|
||||||
let entry = self.reader.fill_buf_to_access_index(self.curr)?;
|
let entry = self.reader.fill_buf_to_access_index(self.curr)?;
|
||||||
self.curr += 1;
|
self.curr += 1;
|
||||||
|
|
||||||
Some((entry, 0))
|
Some((entry, 0))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn prev(&mut self) -> Option<(Arc<LogEntry>, usize)> {
|
fn prev(&mut self, _fl: &FilterList) -> Option<(Arc<LogEntry>, usize)> {
|
||||||
if self.curr == 0 {
|
if self.curr == 0 {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use std::{cell::OnceCell, sync::Arc};
|
use std::{cell::OnceCell, iter, sync::Arc};
|
||||||
|
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use ratatui::widgets::{List, ListItem, Widget};
|
use ratatui::widgets::{List, ListItem, Widget};
|
||||||
|
|
@ -24,7 +24,12 @@ impl TreeState {
|
||||||
let mut curr = String::new();
|
let mut curr = String::new();
|
||||||
let mut prev_depth = 0;
|
let mut prev_depth = 0;
|
||||||
|
|
||||||
for (depth, next_depth) in items.iter().map(|i| i.1).circular_tuple_windows() {
|
for (depth, next_depth) in items
|
||||||
|
.iter()
|
||||||
|
.map(|i| i.1 as isize)
|
||||||
|
.chain(iter::once(-1isize))
|
||||||
|
.tuple_windows()
|
||||||
|
{
|
||||||
if depth > prev_depth {
|
if depth > prev_depth {
|
||||||
if depth > 1 {
|
if depth > 1 {
|
||||||
let _ = curr.pop();
|
let _ = curr.pop();
|
||||||
|
|
@ -43,6 +48,7 @@ impl TreeState {
|
||||||
curr.push('└');
|
curr.push('└');
|
||||||
curr.push('─');
|
curr.push('─');
|
||||||
} else {
|
} else {
|
||||||
|
curr.push(' ');
|
||||||
curr.push(' ');
|
curr.push(' ');
|
||||||
curr.push(' ');
|
curr.push(' ');
|
||||||
curr.push('├');
|
curr.push('├');
|
||||||
|
|
@ -65,7 +71,7 @@ impl TreeState {
|
||||||
if depth > 0 {
|
if depth > 0 {
|
||||||
let _ = curr.pop();
|
let _ = curr.pop();
|
||||||
let _ = curr.pop();
|
let _ = curr.pop();
|
||||||
if next_depth < depth - 1 {
|
if next_depth < depth {
|
||||||
curr.push('└');
|
curr.push('└');
|
||||||
curr.push('─');
|
curr.push('─');
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue