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
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:
4 files changed
+71
-18
No files matched your search
@@ -101,12 +101,22 @@ pub struct ClientArgs {
|
|||||||
)]
|
)]
|
||||||
pub format: OutputFormat,
|
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.
|
/// Comma-separated event row columns, or default/all.
|
||||||
#[arg(
|
#[arg(
|
||||||
short = 'c',
|
short = 'c',
|
||||||
long,
|
long,
|
||||||
value_name = "COLUMNS",
|
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>,
|
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)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum)]
|
||||||
pub enum OutputFormat {
|
pub enum OutputFormat {
|
||||||
/// Readable terminal table output.
|
/// Readable terminal table output.
|
||||||
|
|||||||
@@ -12,12 +12,13 @@ use irtt_client::{
|
|||||||
#[cfg(feature = "stats")]
|
#[cfg(feature = "stats")]
|
||||||
use irtt_stats::{EventStatsUpdate, IpdvPairUpdate};
|
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};
|
use crate::cmd::format::{format_duration, format_signed_duration, ABSENT};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub(super) struct OutputConfig {
|
pub(super) struct OutputConfig {
|
||||||
format: OutputFormat,
|
format: OutputFormat,
|
||||||
|
events: Vec<EventKind>,
|
||||||
columns: Vec<Column>,
|
columns: Vec<Column>,
|
||||||
header: HeaderMode,
|
header: HeaderMode,
|
||||||
default_table_rows: bool,
|
default_table_rows: bool,
|
||||||
@@ -30,6 +31,7 @@ impl OutputConfig {
|
|||||||
columns: Option<&str>,
|
columns: Option<&str>,
|
||||||
header: HeaderMode,
|
header: HeaderMode,
|
||||||
verbose: bool,
|
verbose: bool,
|
||||||
|
events: Vec<EventKind>,
|
||||||
) -> Result<Self, String> {
|
) -> Result<Self, String> {
|
||||||
let uses_default_columns = columns
|
let uses_default_columns = columns
|
||||||
.map(|columns| columns.trim() == "default")
|
.map(|columns| columns.trim() == "default")
|
||||||
@@ -42,6 +44,7 @@ impl OutputConfig {
|
|||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
format,
|
format,
|
||||||
|
events,
|
||||||
columns,
|
columns,
|
||||||
header,
|
header,
|
||||||
default_table_rows,
|
default_table_rows,
|
||||||
@@ -102,7 +105,10 @@ impl OutputConfig {
|
|||||||
#[cfg(feature = "stats")] stats: Option<&EventStatsUpdate>,
|
#[cfg(feature = "stats")] stats: Option<&EventStatsUpdate>,
|
||||||
) -> Option<String> {
|
) -> Option<String> {
|
||||||
let row = OutputRow::from_event(event);
|
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;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -322,17 +328,17 @@ impl OutputRow {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn event_name(&self) -> &'static str {
|
fn event_kind(&self) -> EventKind {
|
||||||
match self {
|
match self {
|
||||||
Self::SessionStarted(_) => "session_started",
|
Self::SessionStarted(_) => EventKind::SessionStarted,
|
||||||
Self::NoTestCompleted(_) => "no_test_completed",
|
Self::NoTestCompleted(_) => EventKind::NoTestCompleted,
|
||||||
Self::SessionClosed { .. } => "session_closed",
|
Self::SessionClosed { .. } => EventKind::SessionClosed,
|
||||||
Self::EchoSent { .. } => "echo_sent",
|
Self::EchoSent { .. } => EventKind::EchoSent,
|
||||||
Self::EchoReply(_) => "echo_reply",
|
Self::EchoReply(_) => EventKind::EchoReply,
|
||||||
Self::Loss { .. } => "loss",
|
Self::Loss { .. } => EventKind::Loss,
|
||||||
Self::Duplicate { .. } => "duplicate",
|
Self::Duplicate { .. } => EventKind::Duplicate,
|
||||||
Self::Late { .. } => "late",
|
Self::Late { .. } => EventKind::Late,
|
||||||
Self::Warning { .. } => "warning",
|
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> {
|
fn cell_for(row: &OutputRow, column: Column, context: RenderContext<'_>) -> Option<CellValue> {
|
||||||
match column {
|
match column {
|
||||||
Column::Target => context.target.map(CellValue::text),
|
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::Seq => row.seq().map(|seq| CellValue::Unsigned(u128::from(seq))),
|
||||||
Column::Remote => row
|
Column::Remote => row
|
||||||
.remote()
|
.remote()
|
||||||
|
|||||||
@@ -171,6 +171,7 @@ async fn run_stream_output(
|
|||||||
args.columns.as_deref(),
|
args.columns.as_deref(),
|
||||||
args.header,
|
args.header,
|
||||||
args.verbose,
|
args.verbose,
|
||||||
|
args.events.clone(),
|
||||||
)
|
)
|
||||||
.map_err(|err| io::Error::new(io::ErrorKind::InvalidInput, err))?;
|
.map_err(|err| io::Error::new(io::ErrorKind::InvalidInput, err))?;
|
||||||
#[cfg(feature = "stats")]
|
#[cfg(feature = "stats")]
|
||||||
|
|||||||
+17
-4
@@ -313,10 +313,23 @@ Choose one of four event-row formats. The default is `table`:
|
|||||||
- `jsonl`: one JSON object per event.
|
- `jsonl`: one JSON object per event.
|
||||||
|
|
||||||
CSV, TSV, and JSON Lines include all columns by default for structured export;
|
CSV, TSV, and JSON Lines include all columns by default for structured export;
|
||||||
`table` defaults to a compact column set and hides `echo_sent` rows. An
|
`table` defaults to a compact column set and hides `echo_sent` events.
|
||||||
explicit `--columns` selection other than `default` shows all event rows,
|
You can show those events by explicitly selecting them with `--events`
|
||||||
including `echo_sent`; `--columns default` is equivalent to omitting the
|
or by using custom columns.
|
||||||
option and keeps `echo_sent` hidden for table output.
|
|
||||||
|
### `--events EVENTS`
|
||||||
|
|
||||||
|
Choose which events to print with a comma-separated list, or repeat the
|
||||||
|
option to select more events. The available names are `session_started`,
|
||||||
|
`no_test_completed`, `session_closed`, `echo_sent`, `echo_reply`, `loss`,
|
||||||
|
`duplicate`, `late`, and `warning`. Without this option, the client prints
|
||||||
|
all events normally shown by the selected format.
|
||||||
|
|
||||||
|
For CSV output containing only echo replies:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
irtt-client <server> --events echo_reply --format csv --header never --no-stats
|
||||||
|
```
|
||||||
|
|
||||||
### `--columns COLUMNS` / `-c COLUMNS`
|
### `--columns COLUMNS` / `-c COLUMNS`
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user