diff --git a/src/tui/filter.rs b/src/tui/filter.rs index 302030d..d9d0ee6 100644 --- a/src/tui/filter.rs +++ b/src/tui/filter.rs @@ -109,12 +109,3 @@ pub struct Filter { pub matcher: Matcher, pub kind: FilterKind, } - -impl Filter { - pub fn removes(&self, elem: &LogEntry) -> bool { - match self.kind { - FilterKind::Inline => false, - FilterKind::Remove => self.matcher.matches(elem), - } - } -} diff --git a/src/tui/log_viewer/filters.rs b/src/tui/log_viewer/filters.rs index 3d5ee53..a184ae1 100644 --- a/src/tui/log_viewer/filters.rs +++ b/src/tui/log_viewer/filters.rs @@ -1,20 +1,80 @@ -use std::sync::Arc; +use std::{fs::File, path::PathBuf, sync::Arc}; use serde::{Deserialize, Serialize}; -use crate::tui::filter::Filter; +use crate::tui::{filter::Filter, widgets::last_error::LastError}; #[derive(Serialize, Deserialize, Debug, Clone)] pub struct Filters { filters: Vec>, undo_pos: usize, + #[serde(skip)] + path: PathBuf, + #[serde(skip)] + error: Option, } impl Filters { - pub fn new() -> Self { + pub fn new(path: PathBuf, error: LastError) -> Self { + if path.exists() { + match File::open(&path) { + Ok(f) => match serde_json::from_reader(f) { + Ok(i) => { + return Self { + error: Some(error), + path, + ..i + }; + } + Err(e) => { + error.set(format!( + "failed to read contents of filter file at {}: {e}", + path.display() + )); + } + }, + Err(e) => { + error.set(format!( + "failed to open filter file at {}: {e}", + path.display() + )); + } + } + } + Self { filters: Vec::new(), undo_pos: 0, + path, + error: Some(error), + } + } + + fn filters_changed(&self) { + match File::options() + .create(true) + .write(true) + .truncate(true) + .open(&self.path) + { + Ok(f) => { + if let Err(e) = serde_json::to_writer(f, self) { + self.error.as_ref().inspect(|i| { + i.set(format!( + "failed to write contents of filter file at {}: {e}", + self.path.display() + )); + }); + } + } + Err(e) => { + self.error.as_ref().inspect(|i| { + i.set(format!( + "failed to open filter file at {}: {e}", + self.path.display() + )); + }); + } } } @@ -26,6 +86,7 @@ impl Filters { self.filters.truncate(self.undo_pos); self.filters.push(filter); self.undo_pos = self.filters.len(); + self.filters_changed(); } pub fn redo(&mut self) { @@ -33,9 +94,11 @@ impl Filters { if self.undo_pos > self.filters.len() { self.undo_pos = self.filters.len() } + self.filters_changed(); } pub fn undo(&mut self) { self.undo_pos = self.undo_pos.saturating_sub(1); + self.filters_changed(); } } diff --git a/src/tui/log_viewer/mod.rs b/src/tui/log_viewer/mod.rs index d0a5e25..c75f457 100644 --- a/src/tui/log_viewer/mod.rs +++ b/src/tui/log_viewer/mod.rs @@ -1,4 +1,4 @@ -use std::{collections::HashMap, fs::File, io::Write, iter, mem, sync::Arc}; +use std::{collections::HashMap, iter, mem, path::PathBuf, sync::Arc}; use crate::tui::{ filter::Filter, @@ -9,6 +9,7 @@ use crate::tui::{ }, model::LogEntry, processing::{FilterList, IntoLogStream, LogStream}, + widgets::last_error::LastError, }; use tui_widget_list::ListState; @@ -34,7 +35,9 @@ pub struct LogViewer { } impl LogViewer { - pub fn new(stream: impl LogStream) -> Self { + pub fn new(stream: impl LogStream, filters_path: PathBuf, error: LastError) -> Self { + let filters = Filters::new(filters_path, error); + let stream = stream.with_filters(filters.get()); Self { stack: Vec::new(), curr: LogView { @@ -50,7 +53,7 @@ impl LogViewer { last_fields_offset: 0, last_fields_height: 0, - filters: Filters::new(), + filters, input_state: InputState::None, } } @@ -71,20 +74,20 @@ impl LogViewer { ))) .collect(); - let mut log = File::options() - .create(true) - .append(true) - .open("./log") - .unwrap(); + // let mut log = File::options() + // .create(true) + // .append(true) + // .open("./log") + // .unwrap(); - writeln!(&mut log, "active filters: {:?}", self.filters.get()).unwrap(); + // writeln!(&mut log, "active filters: {:?}", self.filters.get()).unwrap(); - writeln!( - &mut log, - "{:?}", - offsets_list.iter().map(|i| i.1).collect::>() - ) - .unwrap(); + // writeln!( + // &mut log, + // "{:?}", + // offsets_list.iter().map(|i| i.1).collect::>() + // ) + // .unwrap(); let find_elem_in_stream = |stream: &dyn LogStream, elem: &Arc| -> Option> { @@ -104,35 +107,35 @@ impl LogViewer { for (elem, old_offset) in offsets_list { let Some((elem, inline_depth)) = elem else { - writeln!(&mut log, "no elem").unwrap(); + // writeln!(&mut log, "no elem").unwrap(); break; }; - writeln!( - &mut log, - "reconstruction {:?} prev at {old_offset:?}", - elem.message_or_name() - ) - .unwrap(); + // writeln!( + // &mut log, + // "reconstruction {:?} prev at {old_offset:?}", + // elem.message_or_name() + // ) + // .unwrap(); // find the nearest stream in which this element can be found let mut curr = current_stream.as_ref(); let mut parents = new_stack.iter().rev(); let mut found_in_toplevel = true; let mut stream = loop { - writeln!(&mut log, "find in stream").unwrap(); + // writeln!(&mut log, "find in stream").unwrap(); if let Some(stream) = find_elem_in_stream(curr, &elem) { - writeln!(&mut log, "found (toplevel={found_in_toplevel})").unwrap(); + // writeln!(&mut log, "found (toplevel={found_in_toplevel})").unwrap(); break stream; } found_in_toplevel = false; if let Some(parent) = parents.next() { - writeln!(&mut log, "searching parent").unwrap(); + // writeln!(&mut log, "searching parent").unwrap(); curr = parent.iter.as_ref(); continue; } - writeln!(&mut log, "no more parents").unwrap(); + // writeln!(&mut log, "no more parents").unwrap(); missing = true; // TODO: better guess at how far down we need to go @@ -155,7 +158,7 @@ impl LogViewer { 0 }; - writeln!(&mut log, "new offset: {offset:?}").unwrap(); + // writeln!(&mut log, "new offset: {offset:?}").unwrap(); let nested_stream = elem.from_start(inline_depth); new_stack.push(LogView { @@ -174,29 +177,29 @@ impl LogViewer { } } - 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::>() - ) - .unwrap(); + // 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::>() + // ) + // .unwrap(); // Take the top of the stack as `curr`, unless // the new stack is empty, then just reset the current view. if let Some(curr) = new_stack.pop() { - writeln!(&mut log, "popped new curr off stack").unwrap(); + // writeln!(&mut log, "popped new curr off stack").unwrap(); self.curr = curr; } else { - writeln!(&mut log, "reconstructing root, somehow").unwrap(); + // writeln!(&mut log, "reconstructing root, somehow").unwrap(); self.curr = LogView { iter: self.filtered_root_stream().clone(), selection_offset: self.stack.first().unwrap_or(&self.curr).selection_offset, diff --git a/src/tui/mod.rs b/src/tui/mod.rs index 6f36ffe..b5968e1 100644 --- a/src/tui/mod.rs +++ b/src/tui/mod.rs @@ -182,7 +182,9 @@ impl App { for file in fs::read_dir(logs_dir)? { let file = file?; - files.push(file); + if file.path().extension().is_some_and(|ext| ext == "log") { + files.push(file); + } } Ok(files) @@ -207,6 +209,23 @@ impl App { self.tabs.last_mut().unwrap() } + fn start_log_viewer(&mut self, path: PathBuf) { + let filters_path = path.with_added_extension("filters.json"); + match LogfileReader::new(&path) { + Ok(i) => { + self.current_file = Some(i.clone()); + self.replace_tab(Tab::LogViewer(LogViewer::new( + i.iter(), + filters_path, + self.last_error.clone(), + ))); + } + Err(_) => { + panic!() + } + } + } + fn handle_current_tab_keycode(&mut self, key: KeyEvent) { match self.tabs.last_mut().unwrap() { Tab::Help => {} @@ -227,15 +246,8 @@ impl App { if let Some(selected) = state.selected() && let Some(selected) = files.get(selected) { - match LogfileReader::new(&selected.path()) { - Ok(i) => { - self.current_file = Some(i.clone()); - self.replace_tab(Tab::LogViewer(LogViewer::new(i.iter()))); - } - Err(_) => { - panic!() - } - } + let path = selected.path(); + self.start_log_viewer(path); } } _ => {} @@ -452,17 +464,8 @@ impl App { if state.selected() == Some(row) && let Some(selected) = files.get(row) { - match LogfileReader::new(&selected.path()) { - Ok(i) => { - self.current_file = Some(i.clone()); - self.replace_tab(Tab::LogViewer(LogViewer::new( - i.iter(), - ))); - } - Err(_) => { - panic!() - } - } + let path = selected.path(); + self.start_log_viewer(path); } else { state.select(Some(row)); } diff --git a/src/tui/model.rs b/src/tui/model.rs index c4ad8f9..4b645d6 100644 --- a/src/tui/model.rs +++ b/src/tui/model.rs @@ -121,7 +121,7 @@ impl LogEntry { pub fn line_text(&self, tree: String) -> LineText { const NO_MESSAGE: &str = ""; - const SPACES_BEFORE: &str = " "; + const SPACES_BEFORE: &str = " "; let single_field = |raw: &RawLogEntry| { raw.fields @@ -156,7 +156,7 @@ impl LogEntry { } else { ( format!( - "{:4}⭣{:4}⇊ ", + "{:5}⭣{:5}⇊ ", sub_entries.len(), self.count().wrapping_sub(1) ), diff --git a/src/tui/processing.rs b/src/tui/processing.rs index 805780d..cb5c627 100644 --- a/src/tui/processing.rs +++ b/src/tui/processing.rs @@ -156,7 +156,8 @@ macro_rules! generate_filter { } // Continue so we actually return a nested item. if $forwards { - return Some((elem, inline_depth)); + // return Some((elem, inline_depth)); + continue; } else { continue; } diff --git a/src/tui/widgets/items.rs b/src/tui/widgets/items.rs index 8bfb0d2..e44edb4 100644 --- a/src/tui/widgets/items.rs +++ b/src/tui/widgets/items.rs @@ -37,20 +37,14 @@ impl TreeState { curr.push('│'); curr.push(' '); } - for _ in prev_depth..depth.saturating_sub(1) { + for _ in prev_depth..depth.saturating_sub(2) { curr.push(' '); curr.push(' '); } if next_depth < depth { - curr.push(' '); - curr.push(' '); - curr.push(' '); curr.push('└'); curr.push('─'); } else { - curr.push(' '); - curr.push(' '); - curr.push(' '); curr.push('├'); curr.push('─'); } @@ -65,7 +59,6 @@ impl TreeState { let _ = curr.pop(); let _ = curr.pop(); let _ = curr.pop(); - let _ = curr.pop(); } if depth > 0 { diff --git a/src/tui/widgets/last_error.rs b/src/tui/widgets/last_error.rs index 83fbddb..0d7b915 100644 --- a/src/tui/widgets/last_error.rs +++ b/src/tui/widgets/last_error.rs @@ -4,7 +4,7 @@ use ratatui::widgets::{Paragraph, Widget}; use crate::tui::widgets::styled::Styled; -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct LastError { inner: Rc>>, }