mirror of
https://codeberg.org/icewind/logsmash.git
synced 2026-06-03 18:14:11 +02:00
case insentive filtering
This commit is contained in:
parent
a0624c4493
commit
831ae1a9e8
7 changed files with 91 additions and 24 deletions
74
src/app.rs
74
src/app.rs
|
|
@ -3,7 +3,9 @@ use crate::logline::LogLine;
|
||||||
use crate::matcher::MatchResult;
|
use crate::matcher::MatchResult;
|
||||||
use crate::timegraph::TimeGraph;
|
use crate::timegraph::TimeGraph;
|
||||||
use logsmash_data::{LoggingStatementWithPathPrefix, StatementList};
|
use logsmash_data::{LoggingStatementWithPathPrefix, StatementList};
|
||||||
|
use regex::{escape, Regex, RegexBuilder};
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
use std::fmt::Display;
|
||||||
|
|
||||||
pub struct App<'a> {
|
pub struct App<'a> {
|
||||||
pub lines: Vec<LogLine<'a>>,
|
pub lines: Vec<LogLine<'a>>,
|
||||||
|
|
@ -73,12 +75,12 @@ impl LogMatch {
|
||||||
.filter_map(|index| app.log_statements.get(index))
|
.filter_map(|index| app.log_statements.get(index))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn matches(&self, app: &App, filter: &str) -> bool {
|
pub fn matches(&self, app: &App, filter: &Filter) -> bool {
|
||||||
if filter.is_empty() {
|
if filter.is_empty() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
self.statements(app)
|
self.statements(app)
|
||||||
.any(|statement| statement.pattern.contains(filter))
|
.any(|statement| filter.matches(statement.pattern))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -130,11 +132,75 @@ impl GroupedLines {
|
||||||
self.lines.len()
|
self.lines.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn matches(&self, app: &App, filter: &str) -> bool {
|
pub fn matches(&self, app: &App, filter: &Filter) -> bool {
|
||||||
if filter.is_empty() {
|
if filter.is_empty() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
let line = &app.lines[self.lines[0]];
|
let line = &app.lines[self.lines[0]];
|
||||||
line.message.contains(filter)
|
filter.matches(&line.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Clone)]
|
||||||
|
pub struct Filter {
|
||||||
|
filter: String,
|
||||||
|
regex: Option<Regex>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub static EMPTY_FILTER: Filter = Filter {
|
||||||
|
filter: String::new(),
|
||||||
|
regex: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
impl Filter {
|
||||||
|
fn build_regex(filter: &str) -> Option<Regex> {
|
||||||
|
if filter.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(
|
||||||
|
RegexBuilder::new(&escape(&filter))
|
||||||
|
.case_insensitive(true)
|
||||||
|
.build()
|
||||||
|
.unwrap(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn new(filter: String) -> Self {
|
||||||
|
let regex = Self::build_regex(&filter);
|
||||||
|
Filter { filter, regex }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn matches(&self, string: &str) -> bool {
|
||||||
|
match &self.regex {
|
||||||
|
Some(regex) => regex.is_match(string),
|
||||||
|
None => true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.filter.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push(&mut self, c: char) {
|
||||||
|
self.filter.push(c);
|
||||||
|
self.regex = Self::build_regex(&self.filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pop(&mut self) {
|
||||||
|
self.filter.pop();
|
||||||
|
self.regex = Self::build_regex(&self.filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clear(&mut self) {
|
||||||
|
self.filter.clear();
|
||||||
|
self.regex = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Filter {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "{}", self.filter)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
use crate::app::Filter;
|
||||||
use ahash::AHasher;
|
use ahash::AHasher;
|
||||||
use logsmash_data::LogLevel;
|
use logsmash_data::LogLevel;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
@ -126,12 +127,12 @@ impl<'a> LogLine<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn matches(&self, filter: &str) -> bool {
|
pub fn matches(&self, filter: &Filter) -> bool {
|
||||||
if filter.is_empty() {
|
if filter.is_empty() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// todo: reqid, more?
|
// todo: reqid, more?
|
||||||
self.app.contains(filter) || self.message.contains(filter)
|
filter.matches(&self.app) || filter.matches(&self.message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::app::App;
|
use crate::app::{App, Filter};
|
||||||
use crate::ui::state::UiPage;
|
use crate::ui::state::UiPage;
|
||||||
use ratatui::layout::Constraint;
|
use ratatui::layout::Constraint;
|
||||||
use ratatui::prelude::Style;
|
use ratatui::prelude::Style;
|
||||||
|
|
@ -8,7 +8,7 @@ use ratatui::widgets::{Row, Table};
|
||||||
|
|
||||||
pub enum FooterParams<'a> {
|
pub enum FooterParams<'a> {
|
||||||
Normal { page: UiPage },
|
Normal { page: UiPage },
|
||||||
FilterInput { page: UiPage, filter: &'a str },
|
FilterInput { page: UiPage, filter: &'a Filter },
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn footer<'a>(app: &App<'a>, params: FooterParams<'a>) -> Table<'a> {
|
pub fn footer<'a>(app: &App<'a>, params: FooterParams<'a>) -> Table<'a> {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::app::{App, LogMatch};
|
use crate::app::{App, Filter, LogMatch};
|
||||||
use crate::ui::style::TABLE_HEADER_STYLE;
|
use crate::ui::style::TABLE_HEADER_STYLE;
|
||||||
use crate::ui::table::ScrollbarTable;
|
use crate::ui::table::ScrollbarTable;
|
||||||
use itertools::Either;
|
use itertools::Either;
|
||||||
|
|
@ -7,7 +7,7 @@ use ratatui::widgets::{Cell, Row};
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
use std::iter::{empty, once};
|
use std::iter::{empty, once};
|
||||||
|
|
||||||
pub fn match_list<'a>(app: &'a App<'a>, filter: &str) -> ScrollbarTable<'a> {
|
pub fn match_list<'a>(app: &'a App<'a>, filter: &Filter) -> ScrollbarTable<'a> {
|
||||||
let header = [
|
let header = [
|
||||||
Text::from("Statement"),
|
Text::from("Statement"),
|
||||||
Text::from("File"),
|
Text::from("File"),
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::app::App;
|
use crate::app::{App, Filter};
|
||||||
use crate::logline::{format_time, LogLine};
|
use crate::logline::{format_time, LogLine};
|
||||||
use crate::ui::style::TABLE_HEADER_STYLE;
|
use crate::ui::style::TABLE_HEADER_STYLE;
|
||||||
use crate::ui::table::ScrollbarTable;
|
use crate::ui::table::ScrollbarTable;
|
||||||
|
|
@ -6,7 +6,7 @@ use ratatui::layout::{Alignment, Constraint};
|
||||||
use ratatui::text::Text;
|
use ratatui::text::Text;
|
||||||
use ratatui::widgets::{Cell, Row};
|
use ratatui::widgets::{Cell, Row};
|
||||||
|
|
||||||
pub fn raw_logs<'a>(app: &'a App<'a>, lines: &[usize], filter: &str) -> ScrollbarTable<'a> {
|
pub fn raw_logs<'a>(app: &'a App<'a>, lines: &[usize], filter: &Filter) -> ScrollbarTable<'a> {
|
||||||
let lines = lines.iter().copied().map(|i| &app.lines[i]);
|
let lines = lines.iter().copied().map(|i| &app.lines[i]);
|
||||||
let header = [
|
let header = [
|
||||||
Text::from("Level"),
|
Text::from("Level"),
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::app::{App, GroupedLines, LogMatch};
|
use crate::app::{App, Filter, GroupedLines, LogMatch};
|
||||||
use crate::ui::style::TABLE_HEADER_STYLE;
|
use crate::ui::style::TABLE_HEADER_STYLE;
|
||||||
use crate::ui::table::ScrollbarTable;
|
use crate::ui::table::ScrollbarTable;
|
||||||
use ratatui::layout::Constraint;
|
use ratatui::layout::Constraint;
|
||||||
|
|
@ -8,7 +8,7 @@ use ratatui::widgets::{Cell, Row};
|
||||||
pub fn grouped_lines<'a>(
|
pub fn grouped_lines<'a>(
|
||||||
app: &'a App<'a>,
|
app: &'a App<'a>,
|
||||||
log_match: &'a LogMatch,
|
log_match: &'a LogMatch,
|
||||||
filter: &str,
|
filter: &Filter,
|
||||||
) -> ScrollbarTable<'a> {
|
) -> ScrollbarTable<'a> {
|
||||||
let grouped = &log_match.grouped;
|
let grouped = &log_match.grouped;
|
||||||
let header = [
|
let header = [
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::app::{App, LogMatch};
|
use crate::app::{App, Filter, LogMatch, EMPTY_FILTER};
|
||||||
use crate::logline::{FullLogLine, LogLine};
|
use crate::logline::{FullLogLine, LogLine};
|
||||||
use crate::ui::footer::FooterParams;
|
use crate::ui::footer::FooterParams;
|
||||||
use crate::ui::table::ScrollbarTableState;
|
use crate::ui::table::ScrollbarTableState;
|
||||||
|
|
@ -28,7 +28,7 @@ pub enum Mode {
|
||||||
pub struct MatchListState<'a> {
|
pub struct MatchListState<'a> {
|
||||||
app: &'a App<'a>,
|
app: &'a App<'a>,
|
||||||
pub table_state: ScrollbarTableState,
|
pub table_state: ScrollbarTableState,
|
||||||
pub filter: String,
|
pub filter: Filter,
|
||||||
mode: Mode,
|
mode: Mode,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -58,7 +58,7 @@ impl<'a> MatchListState<'a> {
|
||||||
result,
|
result,
|
||||||
table_state,
|
table_state,
|
||||||
previous: Box::new(self.into()),
|
previous: Box::new(self.into()),
|
||||||
filter: String::new(),
|
filter: Filter::default(),
|
||||||
mode: Mode::Normal,
|
mode: Mode::Normal,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -75,7 +75,7 @@ pub struct MatchState<'a> {
|
||||||
pub result: &'a LogMatch,
|
pub result: &'a LogMatch,
|
||||||
pub table_state: ScrollbarTableState,
|
pub table_state: ScrollbarTableState,
|
||||||
pub previous: Box<UiState<'a>>,
|
pub previous: Box<UiState<'a>>,
|
||||||
pub filter: String,
|
pub filter: Filter,
|
||||||
mode: Mode,
|
mode: Mode,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -104,7 +104,7 @@ impl<'a> MatchState<'a> {
|
||||||
lines,
|
lines,
|
||||||
table_state,
|
table_state,
|
||||||
previous: Box::new(self.into()),
|
previous: Box::new(self.into()),
|
||||||
filter: String::new(),
|
filter: Filter::default(),
|
||||||
mode: Mode::Normal,
|
mode: Mode::Normal,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -121,7 +121,7 @@ pub struct LogsState<'a> {
|
||||||
pub lines: &'a [usize],
|
pub lines: &'a [usize],
|
||||||
pub table_state: ScrollbarTableState,
|
pub table_state: ScrollbarTableState,
|
||||||
pub previous: Box<UiState<'a>>,
|
pub previous: Box<UiState<'a>>,
|
||||||
pub filter: String,
|
pub filter: Filter,
|
||||||
mode: Mode,
|
mode: Mode,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -200,7 +200,7 @@ impl<'a> UiState<'a> {
|
||||||
UiState::MatchList(MatchListState {
|
UiState::MatchList(MatchListState {
|
||||||
app,
|
app,
|
||||||
table_state: ScrollbarTableState::new(app.match_lines()),
|
table_state: ScrollbarTableState::new(app.match_lines()),
|
||||||
filter: String::new(),
|
filter: Filter::default(),
|
||||||
mode: Mode::Normal,
|
mode: Mode::Normal,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -233,7 +233,7 @@ impl<'a> UiState<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn filter(&self) -> Option<&str> {
|
pub fn filter(&self) -> Option<&Filter> {
|
||||||
match self {
|
match self {
|
||||||
UiState::MatchList(state) => Some(&state.filter),
|
UiState::MatchList(state) => Some(&state.filter),
|
||||||
UiState::Match(state) => Some(&state.filter),
|
UiState::Match(state) => Some(&state.filter),
|
||||||
|
|
@ -242,7 +242,7 @@ impl<'a> UiState<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn filter_mut(&mut self) -> Option<&mut String> {
|
pub fn filter_mut(&mut self) -> Option<&mut Filter> {
|
||||||
match self {
|
match self {
|
||||||
UiState::MatchList(state) => Some(&mut state.filter),
|
UiState::MatchList(state) => Some(&mut state.filter),
|
||||||
UiState::Match(state) => Some(&mut state.filter),
|
UiState::Match(state) => Some(&mut state.filter),
|
||||||
|
|
@ -468,7 +468,7 @@ impl<'a> UiState<'a> {
|
||||||
match self.mode() {
|
match self.mode() {
|
||||||
Mode::Normal => FooterParams::Normal { page: self.page() },
|
Mode::Normal => FooterParams::Normal { page: self.page() },
|
||||||
Mode::FilterInput => FooterParams::FilterInput {
|
Mode::FilterInput => FooterParams::FilterInput {
|
||||||
filter: self.filter().unwrap_or_default(),
|
filter: self.filter().unwrap_or(&EMPTY_FILTER),
|
||||||
page: self.page(),
|
page: self.page(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue