grouped lines

This commit is contained in:
Robin Appelman 2024-07-25 19:59:46 +02:00
commit 483bb5691d
12 changed files with 158 additions and 92 deletions

View file

@ -19,7 +19,7 @@ pub fn footer(app: &App, page: UiPage) -> Table {
Table::new(
[Row::new([
help(page).to_string(),
format!("{} unmatched items", app.unmatched.len()),
format!("{} unmatched items", app.unmatched.lines.len()),
format!("{} parse errors", app.error_count),
])],
widths,

View file

@ -29,28 +29,16 @@ pub fn match_list(app: &App) -> Table {
Constraint::Min(10),
];
let all = Row::new([
Text::from("All lines"),
Text::from(String::new()),
Text::from(String::new()).alignment(Alignment::Right),
Text::from(sparkline(&app.histogram.counts(10))),
Text::from(app.lines.len().to_string()),
]);
let unmatched = if app.unmatched.is_empty() {
let all = log_row(&app.all, &app, "All lines");
let unmatched = if app.unmatched.lines.is_empty() {
Either::Right(empty())
} else {
Either::Left(once(Row::new([
Text::from("Unmatched lines"),
Text::from(String::new()),
Text::from(String::new()).alignment(Alignment::Right),
Text::from(sparkline(&app.unmatched_histogram.counts(10))),
Text::from(app.unmatched.len().to_string()),
])))
Either::Left(once(log_row(&app.unmatched, &app, "Unmatched lines")))
};
Table::new(
once(all)
.chain(app.matches.iter().map(|result| log_row(result, app)))
.chain(app.matches.iter().map(|result| log_row(result, app, "")))
.chain(unmatched),
widths,
)
@ -59,22 +47,32 @@ pub fn match_list(app: &App) -> Table {
.highlight_spacing(HighlightSpacing::Always)
}
fn log_row<'a>(result: &LogMatch, app: &'a App) -> Row<'a> {
let mut message = String::new();
let mut paths = String::new();
let mut lines = String::new();
for index in result.result.iter() {
let statement = app.log_statements.get(index).expect("invalid match index");
writeln!(&mut message, "{}", statement.message()).unwrap();
writeln!(&mut paths, "{}", statement.path()).unwrap();
writeln!(&mut lines, "{}", statement.line).unwrap();
fn log_row<'a>(result: &LogMatch, app: &'a App, name: &'static str) -> Row<'a> {
if let Some(match_result) = &result.result {
let mut message = String::new();
let mut paths = String::new();
let mut lines = String::new();
for index in match_result.iter() {
let statement = app.log_statements.get(index).expect("invalid match index");
writeln!(&mut message, "{}", statement.message()).unwrap();
writeln!(&mut paths, "{}", statement.path()).unwrap();
writeln!(&mut lines, "{}", statement.line).unwrap();
}
Row::new([
Text::from(message),
Text::from(paths),
Text::from(lines).alignment(Alignment::Right),
Text::from(sparkline(&result.histogram.counts(10))),
Text::from(result.count().to_string()),
])
.height(match_result.len() as u16)
} else {
Row::new([
Text::from(name),
Text::from(""),
Text::from(""),
Text::from(sparkline(&result.histogram.counts(10))),
Text::from(result.count().to_string()),
])
}
Row::new([
Text::from(message),
Text::from(paths),
Text::from(lines).alignment(Alignment::Right),
Text::from(sparkline(&result.histogram.counts(10))),
Text::from(result.count().to_string()),
])
.height(result.result.len() as u16)
}

View file

@ -80,12 +80,12 @@ fn ui(frame: &mut Frame, app: &App, state: &mut UiState) {
UiState::MatchList { table_state } => {
let selected = table_state.selected().unwrap_or(0);
let histogram = if selected == 0 {
&app.histogram
&app.all.histogram
} else if selected < app.matches.len() + 1 {
let log_match = &app.matches[selected - 1];
&log_match.histogram
} else {
&app.unmatched_histogram
&app.unmatched.histogram
};
frame.render_widget(UiHistogram::new(histogram), layout[0]);
frame.render_stateful_widget(match_list(app), layout[1], table_state);
@ -96,19 +96,25 @@ fn ui(frame: &mut Frame, app: &App, state: &mut UiState) {
table_state,
} => {
let log_match = &app.matches[*index];
let lines = log_match.lines.iter().map(|i| &app.lines[*i]);
frame.render_widget(UiHistogram::new(&log_match.histogram), layout[0]);
frame.render_stateful_widget(grouped_lines(lines), layout[1], table_state);
let selected_group = &log_match.grouped[table_state.selected().unwrap_or_default()];
frame.render_widget(UiHistogram::new(&selected_group.histogram), layout[0]);
frame.render_stateful_widget(grouped_lines(app, log_match), layout[1], table_state);
frame.render_widget(footer(app, page), layout[2]);
}
UiState::All { table_state } => {
frame.render_stateful_widget(grouped_lines(app.lines.iter()), layout[1], table_state);
let selected_group = &app.all.grouped[table_state.selected().unwrap_or_default()];
frame.render_widget(UiHistogram::new(&selected_group.histogram), layout[0]);
frame.render_stateful_widget(grouped_lines(app, &app.all), layout[1], table_state);
frame.render_widget(footer(app, page), layout[2]);
}
UiState::Unmatched { table_state } => {
let lines = app.unmatched.iter().map(|i| &app.lines[*i]);
frame.render_stateful_widget(grouped_lines(lines), layout[1], table_state);
let selected_group = &app.unmatched.grouped[table_state.selected().unwrap_or_default()];
frame.render_widget(UiHistogram::new(&selected_group.histogram), layout[0]);
frame.render_stateful_widget(
grouped_lines(app, &app.unmatched),
layout[1],
table_state,
);
frame.render_widget(footer(app, page), layout[2]);
}
}

View file

@ -1,35 +1,47 @@
use crate::logline::LogLine;
use crate::ui::style::{TABLE_HEADER_STYLE, TABLE_SELECTED_STYLE, TIME_FORMAT};
use crate::app::{App, GroupedLines, LogMatch};
use crate::ui::histogram::sparkline;
use crate::ui::style::{TABLE_HEADER_STYLE, TABLE_SELECTED_STYLE};
use ratatui::layout::Constraint;
use ratatui::text::Text;
use ratatui::widgets::{Cell, HighlightSpacing, Row, Table};
use time::format_description::well_known::Iso8601;
pub fn grouped_lines<'a, I: Iterator<Item = &'a LogLine> + 'a>(lines: I) -> Table<'a> {
let header = ["Level", "App", "Message", "Date"]
.into_iter()
.map(Cell::from)
.collect::<Row>()
.style(TABLE_HEADER_STYLE)
.height(1);
pub fn grouped_lines(app: &App, log_match: &LogMatch) -> Table<'static> {
let grouped = &log_match.grouped;
let header = [
Text::from("Level"),
Text::from("App"),
Text::from("Message"),
Text::from("Time"),
Text::from("Count"),
]
.into_iter()
.map(Cell::from)
.collect::<Row>()
.style(TABLE_HEADER_STYLE)
.height(1);
let widths = [
Constraint::Min(10),
Constraint::Min(20),
Constraint::Percentage(100),
Constraint::Min(30),
Constraint::Length(10),
Constraint::Min(10),
];
let table = Table::new(lines.map(|line| log_row(line)), widths)
let table = Table::new(grouped.iter().map(|group| group_row(app, group)), widths)
.header(header)
.highlight_style(TABLE_SELECTED_STYLE)
.highlight_spacing(HighlightSpacing::Always);
table
}
fn log_row(line: &LogLine) -> Row {
fn group_row(app: &App, group: &GroupedLines) -> Row<'static> {
let line = &app.lines[group.lines[0]];
Row::new([
line.level.as_str().to_string(),
line.app.to_string(),
line.message.clone(),
line.time.format(&Iso8601::<TIME_FORMAT>).unwrap(),
sparkline(&group.histogram.counts(10)),
group.len().to_string(),
])
}

View file

@ -51,9 +51,9 @@ impl UiState {
fn table_count(&self, app: &App) -> usize {
match self {
UiState::MatchList { .. } => app.match_lines(),
UiState::Match { selected, .. } => app.matches[*selected].count(),
UiState::All { .. } => app.lines.len(),
UiState::Unmatched { .. } => app.unmatched.len(),
UiState::Match { selected, .. } => app.matches[*selected].grouped.len(),
UiState::All { .. } => app.all.grouped.len(),
UiState::Unmatched { .. } => app.unmatched.grouped.len(),
UiState::Quit => 0,
}
}