feat(client): filter output by selected event types
Rust CI / cargo fmt (push) Successful in 16s
Rust CI / cargo clippy (push) Successful in 31s
Rust CI / cargo check (x86_64-unknown-linux-gnu) (push) Successful in 25s
Rust CI / cargo check (x86_64-unknown-linux-musl) (push) Successful in 31s
Rust CI / cargo test (x86_64-unknown-linux-gnu) (push) Successful in 1m11s
Rust CI / cargo doc (push) Successful in 27s
Rust CI / cargo test (x86_64-unknown-linux-musl) (push) Successful in 1m17s
Fuzz Smoke / cargo fuzz smoke (push) Successful in 1m38s
Netem Interop / netem interop (push) Successful in 1m19s
Supply chain / cargo-deny advisories (push) Successful in 19s
Supply chain / cargo-deny bans licenses sources (push) Successful in 18s
Supply chain / actionlint (push) Failing after 3s
Build binaries / Build (arm-musleabi) (push) Successful in 4m50s
Build binaries / Build (aarch64-musl) (push) Successful in 5m5s
Build binaries / Build (arm-musleabihf) (push) Successful in 4m45s
Build binaries / Build (armv7-musleabi) (push) Successful in 4m38s
Build binaries / Build (armv7-musleabihf) (push) Successful in 4m52s
Build binaries / Build (i686-musl) (push) Successful in 4m54s
Build binaries / Build (mips64-muslabi64) (push) Successful in 4m33s
Build binaries / Build (mips-musl) (push) Successful in 4m35s
Build binaries / Build (mips64el-muslabi64) (push) Successful in 4m44s
Build binaries / Build (mipsel-musl) (push) Successful in 4m45s
Build binaries / Build (riscv64gc-musl) (push) Successful in 4m47s
Build binaries / Build (x86_64-musl) (push) Successful in 5m19s

This commit is contained in:
Nils Andreas Svee committed 2026-10-28 15:28:26 +07:00
1 parent 5b5e38a059
commit d2d5f87e91
4 files changed
+71 -18

No files matched your search

+34 -1
View File
@@ -101,12 +101,22 @@ pub struct ClientArgs {
)]
pub format: OutputFormat,
/// Filter output by event type.
#[arg(
long,
value_name = "EVENTS",
value_delimiter = ',',
value_enum,
long_help = "Filter output by event type. Accepts comma-separated names or repeated options. If omitted, the normal output behavior is unchanged."
)]
pub events: Vec<EventKind>,
/// Comma-separated event row columns, or default/all.
#[arg(
short = 'c',
long,
value_name = "COLUMNS",
long_help = "Comma-separated event row columns, or default/all.\n\nThe default table columns are compact and hide echo_sent rows. Custom table columns include all event rows. Run --list-columns to see valid names."
long_help = "Comma-separated event row columns, or default/all.\n\nThe default table columns are compact and hide echo_sent rows unless explicitly selected with --events. Custom table columns include all event rows. Run --list-columns to see valid names."
)]
pub columns: Option<String>,
@@ -189,6 +199,29 @@ impl std::ops::Deref for ClientArgs {
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum)]
#[value(rename_all = "snake_case")]
pub enum EventKind {
SessionStarted,
NoTestCompleted,
SessionClosed,
EchoSent,
EchoReply,
Loss,
Duplicate,
Late,
Warning,
}
impl EventKind {
pub(super) fn name(self) -> String {
self.to_possible_value()
.expect("all event kinds have a CLI value")
.get_name()
.to_owned()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum)]
pub enum OutputFormat {
/// Readable terminal table output.
+19 -13
View File
@@ -12,12 +12,13 @@ use irtt_client::{
#[cfg(feature = "stats")]
use irtt_stats::{EventStatsUpdate, IpdvPairUpdate};
use super::args::{HeaderMode, OutputFormat};
use super::args::{EventKind, HeaderMode, OutputFormat};
use crate::cmd::format::{format_duration, format_signed_duration, ABSENT};
#[derive(Debug, Clone)]
pub(super) struct OutputConfig {
format: OutputFormat,
events: Vec<EventKind>,
columns: Vec<Column>,
header: HeaderMode,
default_table_rows: bool,
@@ -30,6 +31,7 @@ impl OutputConfig {
columns: Option<&str>,
header: HeaderMode,
verbose: bool,
events: Vec<EventKind>,
) -> Result<Self, String> {
let uses_default_columns = columns
.map(|columns| columns.trim() == "default")
@@ -42,6 +44,7 @@ impl OutputConfig {
Ok(Self {
format,
events,
columns,
header,
default_table_rows,
@@ -102,7 +105,10 @@ impl OutputConfig {
#[cfg(feature = "stats")] stats: Option<&EventStatsUpdate>,
) -> Option<String> {
let row = OutputRow::from_event(event);
if self.default_table_rows && row.is_default_table_hidden() {
if !self.events.is_empty() && !self.events.contains(&row.event_kind()) {
return None;
}
if self.events.is_empty() && self.default_table_rows && row.is_default_table_hidden() {
return None;
}
@@ -322,17 +328,17 @@ impl OutputRow {
}
}
fn event_name(&self) -> &'static str {
fn event_kind(&self) -> EventKind {
match self {
Self::SessionStarted(_) => "session_started",
Self::NoTestCompleted(_) => "no_test_completed",
Self::SessionClosed { .. } => "session_closed",
Self::EchoSent { .. } => "echo_sent",
Self::EchoReply(_) => "echo_reply",
Self::Loss { .. } => "loss",
Self::Duplicate { .. } => "duplicate",
Self::Late { .. } => "late",
Self::Warning { .. } => "warning",
Self::SessionStarted(_) => EventKind::SessionStarted,
Self::NoTestCompleted(_) => EventKind::NoTestCompleted,
Self::SessionClosed { .. } => EventKind::SessionClosed,
Self::EchoSent { .. } => EventKind::EchoSent,
Self::EchoReply(_) => EventKind::EchoReply,
Self::Loss { .. } => EventKind::Loss,
Self::Duplicate { .. } => EventKind::Duplicate,
Self::Late { .. } => EventKind::Late,
Self::Warning { .. } => EventKind::Warning,
}
}
@@ -784,7 +790,7 @@ fn render_jsonl_row(row: &OutputRow, columns: &[Column], context: RenderContext<
fn cell_for(row: &OutputRow, column: Column, context: RenderContext<'_>) -> Option<CellValue> {
match column {
Column::Target => context.target.map(CellValue::text),
Column::Event => Some(CellValue::text(row.event_name())),
Column::Event => Some(CellValue::text(row.event_kind().name())),
Column::Seq => row.seq().map(|seq| CellValue::Unsigned(u128::from(seq))),
Column::Remote => row
.remote()
+1
View File
@@ -171,6 +171,7 @@ async fn run_stream_output(
args.columns.as_deref(),
args.header,
args.verbose,
args.events.clone(),
)
.map_err(|err| io::Error::new(io::ErrorKind::InvalidInput, err))?;
#[cfg(feature = "stats")]