improve performance of long error list

This commit is contained in:
Robin Appelman 2025-09-28 21:42:24 +02:00
commit 538f5db41d
5 changed files with 52 additions and 25 deletions

View file

@ -28,10 +28,8 @@ impl<'logs> App<'logs> {
.find_lines(move |line| line.request_id == request_id)
}
pub fn error_lines(&self) -> impl Iterator<Item = (&'logs str, &'logs JsonError)> + use<'logs> {
self.lines.errors().iter().map(|(line_number, error)| {
(self.log_file.nth(*line_number).unwrap_or_default(), error)
})
pub fn error_lines<'a>(&'a self) -> &'a [(&'logs str, JsonError)] {
self.lines.errors()
}
pub fn error_count(&self) -> usize {

View file

@ -1,4 +1,4 @@
use crate::logfile::{LogLine, LogLineNumber};
use crate::logfile::LogLine;
use serde::Deserialize;
use serde_json::Error as JsonError;
use std::ops::Index;
@ -21,14 +21,14 @@ impl From<&usize> for LogIndex {
pub struct ParsedLogs<'logfile> {
parsed: Vec<LogLine<'logfile>>,
error_lines: Vec<(LogLineNumber, JsonError)>,
error_lines: Vec<(&'logfile str, JsonError)>,
}
impl<'logfile> ParsedLogs<'logfile> {
pub fn all(&self) -> &[LogLine<'logfile>] {
&self.parsed
}
pub fn errors(&self) -> &[(LogLineNumber, JsonError)] {
pub fn errors(&self) -> &[(&'logfile str, JsonError)] {
&self.error_lines
}
@ -48,8 +48,8 @@ impl<'logfile> ParsedLogs<'logfile> {
}
}
impl<'a> FromIterator<Result<LogLine<'a>, (LogLineNumber, JsonError)>> for ParsedLogs<'a> {
fn from_iter<T: IntoIterator<Item = Result<LogLine<'a>, (LogLineNumber, JsonError)>>>(
impl<'a> FromIterator<Result<LogLine<'a>, (&'a str, JsonError)>> for ParsedLogs<'a> {
fn from_iter<T: IntoIterator<Item = Result<LogLine<'a>, (&'a str, JsonError)>>>(
iter: T,
) -> Self {
let iter = iter.into_iter();

View file

@ -125,7 +125,7 @@ fn main() -> MainResult {
progress.icr();
parsed.map_err(|err| (line_number, err))
parsed.map_err(|err| (line, err))
})
.progress_with_style(progress_style.clone())
.collect();

View file

@ -1,22 +1,51 @@
use crate::app::App;
use crate::ui::style::TABLE_HEADER_STYLE;
use crate::ui::table::ScrollbarTable;
use ratatui::layout::Constraint;
use crate::ui::table::{ScrollbarTable, ScrollbarTableState};
use ratatui::buffer::Buffer;
use ratatui::layout::{Constraint, Rect};
use ratatui::text::Text;
use ratatui::widgets::{Cell, Row};
use ratatui::widgets::{Cell, Row, StatefulWidget};
pub fn error_list<'a>(app: &App<'a>) -> ScrollbarTable<'a> {
let header = [Text::from("Error"), Text::from("Line")]
.into_iter()
.map(Cell::from)
.collect::<Row>()
.style(TABLE_HEADER_STYLE)
.height(1);
let widths = [Constraint::Percentage(50), Constraint::Percentage(50)];
ScrollbarTable::new(app.error_lines().map(error_row), widths).header(header)
pub fn error_list<'a>(app: &'a App<'a>) -> ErrorList<'a> {
ErrorList {
errors: app.error_lines(),
}
}
fn error_row<'a>((line, err): (&'a str, &'a serde_json::Error)) -> Row<'a> {
pub struct ErrorList<'a> {
errors: &'a [(&'a str, serde_json::Error)],
}
impl StatefulWidget for ErrorList<'_> {
type State = ScrollbarTableState;
fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State)
where
Self: Sized,
{
let header = [Text::from("Error"), Text::from("Line")]
.into_iter()
.map(Cell::from)
.collect::<Row>()
.style(TABLE_HEADER_STYLE)
.height(1);
let widths = [Constraint::Percentage(50), Constraint::Percentage(50)];
let table = ScrollbarTable::new(
self.errors.iter().enumerate().map(|(i, (line, error))| {
if i.abs_diff(state.selected()) < 100 {
error_row(line, error)
} else {
Row::new::<[Text; 0]>([])
}
}),
widths,
)
.header(header);
table.render(area, buf, state);
}
}
fn error_row<'a>(line: &'a str, err: &'a serde_json::Error) -> Row<'a> {
Row::new([Text::from(format!("{err}")), Text::from(line)])
}

View file

@ -638,8 +638,8 @@ impl<'a> UiState<'a> {
(UiState::Errors(state), UiEvent::Copy) => {
let raw = app
.error_lines()
.nth(state.table_state.selected())
.map(|(line, _)| line)
.get(state.table_state.selected())
.map(|(line, _)| *line)
.unwrap_or_default();
copy_osc(raw);
(false, UiState::Errors(state))