mirror of
https://codeberg.org/icewind/logsmash.git
synced 2026-06-03 10:04:12 +02:00
group by for distinct logs
This commit is contained in:
parent
a36df49377
commit
3982c2e354
4 changed files with 80 additions and 43 deletions
|
|
@ -165,6 +165,7 @@ impl<'logs> GroupingResult<'logs> for Groupings<'logs> {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub enum GroupingOptions {
|
||||
Url,
|
||||
App,
|
||||
|
|
|
|||
|
|
@ -57,8 +57,8 @@ fn help(page: UiPage) -> &'static str {
|
|||
match page {
|
||||
UiPage::GroupList => "«Q» Exit - «Enter» Select - «F» Filter - «E» Show parse errors",
|
||||
UiPage::Group => "«Q» Exit - «Enter» Select - «F» Filter - «G» Group By - «Esc» Back",
|
||||
UiPage::Logs => {
|
||||
"«Q» Exit - «F» Filter - «Esc» Back - «C» Copy log line - «R» Show logs for request"
|
||||
UiPage::DistinctLogs => {
|
||||
"«Q» Exit - «F» Filter - «Esc» Back - «C» Copy log line - «G» Group By - «R» Show logs for request"
|
||||
}
|
||||
UiPage::Log => {
|
||||
"«Q» Exit - «Esc» Back - «R» Toggle raw - «C» Copy log line - «R» Show logs for request"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use crate::app::App;
|
||||
use crate::error::UiError;
|
||||
use crate::grouping::{GroupingOptions, LogGrouping};
|
||||
use crate::grouping::LogGrouping;
|
||||
use crate::ui::by_identifier::logs_by_identifier;
|
||||
use crate::ui::error_list::error_list;
|
||||
use crate::ui::footer::footer;
|
||||
|
|
@ -11,8 +11,8 @@ use crate::ui::list::SelectList;
|
|||
use crate::ui::single_group::single_group;
|
||||
use crate::ui::single_log::single_log;
|
||||
use crate::ui::state::{
|
||||
ErrorLinesState, ErrorState, GroupByMenuState, GroupListState, GroupState, LogState,
|
||||
LogsByIdentifierState, UiState,
|
||||
DistinctLogsState, ErrorLinesState, ErrorState, GroupByMenuState, GroupListState, GroupState,
|
||||
LogState, UiState,
|
||||
};
|
||||
use ratatui::crossterm::event::{DisableMouseCapture, EnableMouseCapture};
|
||||
use ratatui::crossterm::terminal::{
|
||||
|
|
@ -162,21 +162,23 @@ fn ui(frame: &mut Frame, app: &App, state: &mut UiState) {
|
|||
frame.render_widget(footer(app, state.footer_params()), layout[2]);
|
||||
}
|
||||
UiState::GroupByMenu(GroupByMenuState {
|
||||
options,
|
||||
previous,
|
||||
list_state,
|
||||
..
|
||||
}) => {
|
||||
ui(frame, app, previous);
|
||||
let area = center(
|
||||
frame.area(),
|
||||
Constraint::Percentage(20),
|
||||
Constraint::Length(GroupingOptions::all().count() as u16 + 2), // top and bottom border + content
|
||||
Constraint::Length(options.len() as u16 + 2), // top and bottom border + content
|
||||
);
|
||||
let popup = SelectList::new(GroupingOptions::all().map(|option| option.as_str()))
|
||||
let popup = SelectList::new(options.iter().map(|option| option.as_str()))
|
||||
.block(Block::bordered().title("Group By"));
|
||||
frame.render_widget(Clear, area);
|
||||
frame.render_stateful_widget(popup, area, list_state);
|
||||
}
|
||||
UiState::ByIdentifier(LogsByIdentifierState {
|
||||
UiState::Distinct(DistinctLogsState {
|
||||
lines,
|
||||
table_state,
|
||||
filter,
|
||||
|
|
|
|||
104
src/ui/state.rs
104
src/ui/state.rs
|
|
@ -19,7 +19,7 @@ pub enum UiState<'a> {
|
|||
GroupList(GroupListState<'a>),
|
||||
Group(GroupState<'a>),
|
||||
GroupByMenu(GroupByMenuState<'a>),
|
||||
ByIdentifier(LogsByIdentifierState<'a>),
|
||||
Distinct(DistinctLogsState<'a>),
|
||||
Log(LogState<'a>),
|
||||
Errors(ErrorLinesState<'a>),
|
||||
Error(ErrorState<'a>),
|
||||
|
|
@ -35,7 +35,7 @@ impl<'a> UiState<'a> {
|
|||
previous: Some(_),
|
||||
..
|
||||
})
|
||||
| UiState::ByIdentifier(LogsByIdentifierState { .. })
|
||||
| UiState::Distinct(DistinctLogsState { .. })
|
||||
| UiState::Log(LogState { .. })
|
||||
| UiState::Errors(ErrorLinesState { .. })
|
||||
)
|
||||
|
|
@ -54,6 +54,7 @@ pub struct GroupListState<'a> {
|
|||
pub table_state: ScrollbarTableState,
|
||||
pub filter: Filter,
|
||||
pub previous: Option<Box<UiState<'a>>>,
|
||||
pub next: UiPage,
|
||||
mode: Mode,
|
||||
}
|
||||
|
||||
|
|
@ -75,14 +76,28 @@ impl<'a> GroupListState<'a> {
|
|||
.clone()
|
||||
};
|
||||
|
||||
let table_state = ScrollbarTableState::new(result.by_identifier().len() + 1);
|
||||
UiState::Group(GroupState {
|
||||
result,
|
||||
table_state,
|
||||
previous: Box::new(self.into()),
|
||||
filter: Filter::default(),
|
||||
mode: Mode::Normal,
|
||||
})
|
||||
let line_count = result.lines.lines.len();
|
||||
match self.next {
|
||||
UiPage::Group => {
|
||||
let result_len = result.by_identifier().len();
|
||||
UiState::Group(GroupState {
|
||||
result,
|
||||
table_state: ScrollbarTableState::new(result_len + 1),
|
||||
previous: Box::new(self.into()),
|
||||
filter: Filter::default(),
|
||||
mode: Mode::Normal,
|
||||
})
|
||||
}
|
||||
UiPage::DistinctLogs => UiState::Distinct(DistinctLogsState {
|
||||
table_state: ScrollbarTableState::new(line_count + 1),
|
||||
lines: result.lines.lines,
|
||||
grouping: GroupedLogGrouping::Message,
|
||||
previous: Box::new(self.into()),
|
||||
filter: Filter::default(),
|
||||
mode: Mode::Normal,
|
||||
}),
|
||||
_ => panic!("invalid next for grouped list"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -93,8 +108,10 @@ impl PartialEq for GroupListState<'_> {
|
|||
}
|
||||
|
||||
pub struct GroupByMenuState<'a> {
|
||||
pub options: Vec<GroupingOptions>,
|
||||
pub previous: Box<UiState<'a>>,
|
||||
pub list_state: SizedListState,
|
||||
pub next: UiPage,
|
||||
}
|
||||
|
||||
impl PartialEq for GroupByMenuState<'_> {
|
||||
|
|
@ -109,12 +126,13 @@ impl<'a> GroupByMenuState<'a> {
|
|||
}
|
||||
|
||||
pub fn enter(self, selected: usize) -> UiState<'a> {
|
||||
let group_option = GroupingOptions::all().nth(selected).unwrap();
|
||||
let group_option = self.options[selected];
|
||||
let lines = match self.previous.as_ref() {
|
||||
UiState::Group(group) => &group.result.lines,
|
||||
_ => panic!("Group by called from non-group"),
|
||||
UiState::Group(group) => &group.result.lines.lines,
|
||||
UiState::Distinct(group) => &group.lines,
|
||||
_ => panic!("Group by called from invalid state"),
|
||||
};
|
||||
let items = group_option.group_by(lines.lines.clone());
|
||||
let items = group_option.group_by(lines.clone());
|
||||
let count = items.len();
|
||||
|
||||
UiState::GroupList(GroupListState {
|
||||
|
|
@ -124,6 +142,7 @@ impl<'a> GroupByMenuState<'a> {
|
|||
mode: Mode::Normal,
|
||||
table_state: ScrollbarTableState::new(count),
|
||||
previous: Some(self.previous),
|
||||
next: self.next,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -143,8 +162,10 @@ impl<'a> GroupState<'a> {
|
|||
|
||||
fn group_by_menu(self) -> UiState<'a> {
|
||||
UiState::GroupByMenu(GroupByMenuState {
|
||||
options: GroupingOptions::all().collect(),
|
||||
previous: Box::new(self.into()),
|
||||
list_state: SizedListState::new(GroupingOptions::all().count()),
|
||||
next: UiPage::Group,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -169,7 +190,7 @@ impl<'a> GroupState<'a> {
|
|||
};
|
||||
let lines = selected_line.lines.as_slice();
|
||||
let table_state = ScrollbarTableState::new(lines.len());
|
||||
UiState::ByIdentifier(LogsByIdentifierState {
|
||||
UiState::Distinct(DistinctLogsState {
|
||||
lines: lines.into(),
|
||||
table_state,
|
||||
previous: Box::new(self.into()),
|
||||
|
|
@ -192,7 +213,7 @@ pub enum GroupedLogGrouping {
|
|||
Request,
|
||||
}
|
||||
|
||||
pub struct LogsByIdentifierState<'a> {
|
||||
pub struct DistinctLogsState<'a> {
|
||||
pub lines: Vec<&'a LogLine<'a>>,
|
||||
pub table_state: ScrollbarTableState,
|
||||
pub previous: Box<UiState<'a>>,
|
||||
|
|
@ -201,7 +222,7 @@ pub struct LogsByIdentifierState<'a> {
|
|||
pub grouping: GroupedLogGrouping,
|
||||
}
|
||||
|
||||
impl<'a> LogsByIdentifierState<'a> {
|
||||
impl<'a> DistinctLogsState<'a> {
|
||||
fn selected(&self) -> usize {
|
||||
self.table_state.selected()
|
||||
}
|
||||
|
|
@ -219,6 +240,17 @@ impl<'a> LogsByIdentifierState<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn group_by_menu(self) -> UiState<'a> {
|
||||
UiState::GroupByMenu(GroupByMenuState {
|
||||
options: GroupingOptions::all()
|
||||
.filter(|option| *option != GroupingOptions::App) // app is already unique
|
||||
.collect(),
|
||||
previous: Box::new(self.into()),
|
||||
list_state: SizedListState::new(GroupingOptions::all().count()),
|
||||
next: UiPage::DistinctLogs,
|
||||
})
|
||||
}
|
||||
|
||||
fn enter(self, selected: usize, app: &App<'a>) -> UiState<'a> {
|
||||
let log = self.get_selected(selected);
|
||||
let raw_line = app.get_source_line(log.line_number).unwrap();
|
||||
|
|
@ -254,7 +286,7 @@ impl<'a> LogsByIdentifierState<'a> {
|
|||
let lines: Vec<_> = app.lines_by_request(&log.request_id).collect();
|
||||
|
||||
let table_state = ScrollbarTableState::new(lines.len());
|
||||
UiState::ByIdentifier(LogsByIdentifierState {
|
||||
UiState::Distinct(DistinctLogsState {
|
||||
lines,
|
||||
mode: Mode::Normal,
|
||||
filter: Filter::default(),
|
||||
|
|
@ -265,7 +297,7 @@ impl<'a> LogsByIdentifierState<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
impl PartialEq for LogsByIdentifierState<'_> {
|
||||
impl PartialEq for DistinctLogsState<'_> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.lines == other.lines
|
||||
}
|
||||
|
|
@ -294,7 +326,7 @@ impl<'a> LogState<'a> {
|
|||
let lines: Vec<_> = app.lines_by_request(&self.log.request_id).collect();
|
||||
|
||||
let table_state = ScrollbarTableState::new(lines.len());
|
||||
UiState::ByIdentifier(LogsByIdentifierState {
|
||||
UiState::Distinct(DistinctLogsState {
|
||||
lines,
|
||||
mode: Mode::Normal,
|
||||
filter: Filter::default(),
|
||||
|
|
@ -323,6 +355,7 @@ impl<'a> UiState<'a> {
|
|||
filter: Filter::default(),
|
||||
mode: Mode::Normal,
|
||||
previous: None,
|
||||
next: UiPage::Group,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -331,7 +364,7 @@ impl<'a> UiState<'a> {
|
|||
UiState::Quit | UiState::GroupList(_) => UiPage::GroupList,
|
||||
UiState::Group(_) => UiPage::Group,
|
||||
UiState::GroupByMenu(_) => UiPage::Group, // todo
|
||||
UiState::ByIdentifier(_) => UiPage::Logs,
|
||||
UiState::Distinct(_) => UiPage::DistinctLogs,
|
||||
UiState::Log(_) => UiPage::Log,
|
||||
UiState::Errors(_) => UiPage::Errors,
|
||||
UiState::Error(_) => UiPage::Error,
|
||||
|
|
@ -342,7 +375,7 @@ impl<'a> UiState<'a> {
|
|||
match self {
|
||||
UiState::GroupList(state) => state.mode,
|
||||
UiState::Group(state) => state.mode,
|
||||
UiState::ByIdentifier(state) => state.mode,
|
||||
UiState::Distinct(state) => state.mode,
|
||||
_ => Mode::Normal,
|
||||
}
|
||||
}
|
||||
|
|
@ -351,7 +384,7 @@ impl<'a> UiState<'a> {
|
|||
match self {
|
||||
UiState::GroupList(state) => state.mode = mode,
|
||||
UiState::Group(state) => state.mode = mode,
|
||||
UiState::ByIdentifier(state) => state.mode = mode,
|
||||
UiState::Distinct(state) => state.mode = mode,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
|
@ -360,7 +393,7 @@ impl<'a> UiState<'a> {
|
|||
match self {
|
||||
UiState::GroupList(state) => Some(&state.filter),
|
||||
UiState::Group(state) => Some(&state.filter),
|
||||
UiState::ByIdentifier(state) => Some(&state.filter),
|
||||
UiState::Distinct(state) => Some(&state.filter),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
|
@ -369,7 +402,7 @@ impl<'a> UiState<'a> {
|
|||
match self {
|
||||
UiState::GroupList(state) => Some(&mut state.filter),
|
||||
UiState::Group(state) => Some(&mut state.filter),
|
||||
UiState::ByIdentifier(state) => Some(&mut state.filter),
|
||||
UiState::Distinct(state) => Some(&mut state.filter),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
|
@ -378,7 +411,7 @@ impl<'a> UiState<'a> {
|
|||
match self {
|
||||
UiState::GroupList(state) => Some(&state.table_state),
|
||||
UiState::Group(state) => Some(&state.table_state),
|
||||
UiState::ByIdentifier(state) => Some(&state.table_state),
|
||||
UiState::Distinct(state) => Some(&state.table_state),
|
||||
UiState::Log(state) => Some(&state.table_state),
|
||||
UiState::Errors(state) => Some(&state.table_state),
|
||||
_ => None,
|
||||
|
|
@ -389,7 +422,7 @@ impl<'a> UiState<'a> {
|
|||
match self {
|
||||
UiState::GroupList(state) => Some(&mut state.table_state),
|
||||
UiState::Group(state) => Some(&mut state.table_state),
|
||||
UiState::ByIdentifier(state) => Some(&mut state.table_state),
|
||||
UiState::Distinct(state) => Some(&mut state.table_state),
|
||||
UiState::Log(state) => Some(&mut state.table_state),
|
||||
UiState::Errors(state) => Some(&mut state.table_state),
|
||||
_ => None,
|
||||
|
|
@ -453,7 +486,7 @@ impl<'a> UiState<'a> {
|
|||
match self {
|
||||
UiState::GroupList(_) => UI_HEADER_SIZE + 1,
|
||||
UiState::Group(_) => UI_HEADER_SIZE + 1,
|
||||
UiState::ByIdentifier(_) => UI_HEADER_SIZE + 1,
|
||||
UiState::Distinct(_) => UI_HEADER_SIZE + 1,
|
||||
UiState::GroupByMenu(_) => 0,
|
||||
UiState::Log(_) => 0,
|
||||
UiState::Errors(_) => 0,
|
||||
|
|
@ -538,14 +571,14 @@ impl<'a> UiState<'a> {
|
|||
let selected = state.selected();
|
||||
(true, state.enter(selected))
|
||||
}
|
||||
(UiState::ByIdentifier(state), UiEvent::Select) => {
|
||||
(UiState::Distinct(state), UiEvent::Select) => {
|
||||
let selected = state.selected();
|
||||
(true, state.enter(selected, app))
|
||||
}
|
||||
(UiState::ByIdentifier(state), UiEvent::Enter(selected)) => {
|
||||
(UiState::Distinct(state), UiEvent::Enter(selected)) => {
|
||||
(true, state.enter(selected, app))
|
||||
}
|
||||
(UiState::ByIdentifier(state), UiEvent::Copy) => {
|
||||
(UiState::Distinct(state), UiEvent::Copy) => {
|
||||
let selected = state.selected();
|
||||
let mut table_state = TableState::default();
|
||||
table_state.select(Some(0));
|
||||
|
|
@ -553,8 +586,9 @@ impl<'a> UiState<'a> {
|
|||
let line = state.lines[selected];
|
||||
let raw = app.get_source_line(line.line_number).unwrap_or_default();
|
||||
copy_osc(raw);
|
||||
(false, UiState::ByIdentifier(state))
|
||||
(false, UiState::Distinct(state))
|
||||
}
|
||||
(UiState::Distinct(state), UiEvent::GroupBy) => (true, state.group_by_menu()),
|
||||
(UiState::Log(state), UiEvent::Copy) => {
|
||||
let raw = app
|
||||
.get_source_line(state.log.line_number)
|
||||
|
|
@ -562,7 +596,7 @@ impl<'a> UiState<'a> {
|
|||
copy_osc(raw);
|
||||
(false, UiState::Log(state))
|
||||
}
|
||||
(UiState::ByIdentifier(state), UiEvent::ByRequest) => {
|
||||
(UiState::Distinct(state), UiEvent::ByRequest) => {
|
||||
let selected = state.selected();
|
||||
(true, state.by_request(selected, app))
|
||||
}
|
||||
|
|
@ -622,7 +656,7 @@ impl<'a> UiState<'a> {
|
|||
previous: Some(previous),
|
||||
..
|
||||
})
|
||||
| UiState::ByIdentifier(LogsByIdentifierState { previous, .. })
|
||||
| UiState::Distinct(DistinctLogsState { previous, .. })
|
||||
| UiState::Log(LogState { previous, .. })
|
||||
| UiState::Errors(ErrorLinesState { previous, .. }),
|
||||
UiEvent::Back,
|
||||
|
|
@ -646,7 +680,7 @@ impl<'a> UiState<'a> {
|
|||
pub enum UiPage {
|
||||
GroupList,
|
||||
Group,
|
||||
Logs,
|
||||
DistinctLogs,
|
||||
Log,
|
||||
Errors,
|
||||
Error,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue