group by remote and user

This commit is contained in:
Robin Appelman 2025-08-16 23:00:06 +02:00
commit 181559d841
4 changed files with 92 additions and 21 deletions

View file

@ -1,14 +1,19 @@
mod app;
mod remote;
mod unique;
mod url;
mod user;
use crate::app::{Filter, LineSet};
use crate::grouping::app::{AppGrouping, APP_GROUPING_UI};
use crate::grouping::remote::{RemoteGrouping, REMOTE_GROUPING_UI};
use crate::grouping::url::{UrlGrouping, URL_GROUPING_UI};
use crate::grouping::user::{UserGrouping, USER_GROUPING_UI};
use crate::logfile::LogLine;
use crate::matcher::MatchResult;
use crate::timegraph::{SparkLine, TimeGraph};
use ahash::AHasher;
use derive_more::From;
use ratatui::layout::{Alignment, Constraint};
use std::borrow::Cow;
use std::cell::OnceCell;
@ -120,29 +125,13 @@ where
list
}
#[derive(PartialEq, Clone)]
#[derive(PartialEq, Clone, From)]
pub enum Groupings<'logs> {
Match(MatchResult),
Url(UrlGrouping<'logs>),
App(AppGrouping<'logs>),
}
impl<'logs> From<MatchResult> for Groupings<'logs> {
fn from(value: MatchResult) -> Self {
Groupings::Match(value)
}
}
impl<'logs> From<UrlGrouping<'logs>> for Groupings<'logs> {
fn from(value: UrlGrouping<'logs>) -> Self {
Groupings::Url(value)
}
}
impl<'logs> From<AppGrouping<'logs>> for Groupings<'logs> {
fn from(value: AppGrouping<'logs>) -> Self {
Groupings::App(value)
}
User(UserGrouping<'logs>),
Remote(RemoteGrouping<'logs>),
}
impl<'logs> GroupingResult<'logs> for Groupings<'logs> {
@ -151,6 +140,8 @@ impl<'logs> GroupingResult<'logs> for Groupings<'logs> {
Groupings::Match(r) => r.matches(filter),
Groupings::Url(r) => r.matches(filter),
Groupings::App(r) => r.matches(filter),
Groupings::User(r) => r.matches(filter),
Groupings::Remote(r) => r.matches(filter),
}
}
@ -161,6 +152,8 @@ impl<'logs> GroupingResult<'logs> for Groupings<'logs> {
}
Groupings::Url(r) => Box::new(r.render()),
Groupings::App(r) => Box::new(r.render()),
Groupings::User(r) => Box::new(r.render()),
Groupings::Remote(r) => Box::new(r.render()),
}
}
}
@ -169,11 +162,19 @@ impl<'logs> GroupingResult<'logs> for Groupings<'logs> {
pub enum GroupingOptions {
Url,
App,
User,
Remote,
}
impl GroupingOptions {
pub fn all() -> impl Iterator<Item = Self> {
[GroupingOptions::Url, GroupingOptions::App].into_iter()
[
GroupingOptions::Url,
GroupingOptions::App,
GroupingOptions::User,
GroupingOptions::Remote,
]
.into_iter()
}
pub fn group_key(&self, line: &LogLine) -> u64 {
@ -181,6 +182,8 @@ impl GroupingOptions {
match self {
GroupingOptions::Url => line.url.hash(&mut hasher),
GroupingOptions::App => line.app.hash(&mut hasher),
GroupingOptions::User => line.user.hash(&mut hasher),
GroupingOptions::Remote => line.remote.hash(&mut hasher),
}
hasher.finish()
}
@ -193,6 +196,12 @@ impl GroupingOptions {
GroupingOptions::App => Groupings::App(AppGrouping {
app: line.app.as_ref(),
}),
GroupingOptions::User => Groupings::User(UserGrouping {
user: line.user.as_str(),
}),
GroupingOptions::Remote => Groupings::Remote(RemoteGrouping {
remote: line.remote.as_str(),
}),
}
}
@ -200,6 +209,8 @@ impl GroupingOptions {
match self {
GroupingOptions::Url => "Url",
GroupingOptions::App => "App",
GroupingOptions::User => "User",
GroupingOptions::Remote => "Remote",
}
}
@ -207,6 +218,8 @@ impl GroupingOptions {
match self {
GroupingOptions::Url => URL_GROUPING_UI,
GroupingOptions::App => APP_GROUPING_UI,
GroupingOptions::User => USER_GROUPING_UI,
GroupingOptions::Remote => REMOTE_GROUPING_UI,
}
}

29
src/grouping/remote.rs Normal file
View file

@ -0,0 +1,29 @@
use crate::app::Filter;
use crate::grouping::{GroupingResult, GroupingUi};
use ratatui::layout::{Alignment, Constraint};
use std::borrow::Cow;
#[derive(PartialEq, Clone)]
pub struct RemoteGrouping<'a> {
pub remote: &'a str,
}
pub const REMOTE_GROUPING_UI: GroupingUi = GroupingUi {
header: &[("Remote", Alignment::Left)],
widths: &[Constraint::Percentage(100)],
};
impl<'a> GroupingResult<'a> for RemoteGrouping<'a> {
fn matches(&self, filter: &Filter) -> bool {
if filter.is_empty() {
return true;
}
filter
.parts()
.all(|filter_part| filter_part.is_match(self.remote))
}
fn render(&self) -> impl Iterator<Item = Cow<'a, str>> {
[self.remote.into()].into_iter()
}
}

29
src/grouping/user.rs Normal file
View file

@ -0,0 +1,29 @@
use crate::app::Filter;
use crate::grouping::{GroupingResult, GroupingUi};
use ratatui::layout::{Alignment, Constraint};
use std::borrow::Cow;
#[derive(PartialEq, Clone)]
pub struct UserGrouping<'a> {
pub user: &'a str,
}
pub const USER_GROUPING_UI: GroupingUi = GroupingUi {
header: &[("User", Alignment::Left)],
widths: &[Constraint::Percentage(100)],
};
impl<'a> GroupingResult<'a> for UserGrouping<'a> {
fn matches(&self, filter: &Filter) -> bool {
if filter.is_empty() {
return true;
}
filter
.parts()
.all(|filter_part| filter_part.is_match(self.user))
}
fn render(&self) -> impl Iterator<Item = Cow<'a, str>> {
[self.user.into()].into_iter()
}
}

View file

@ -53,7 +53,7 @@ impl<'a> PartialEq for LogLine<'a> {
}
}
#[derive(Clone)]
#[derive(Clone, Hash)]
pub enum LogUser {
None,
User(TinyAsciiStr<64>),