refactor(app): clarify application and binary roles
Rename the application Cargo package from irtt-cli to irtt-rs (crates/irtt-cli -> crates/irtt-app), and hard-rename its scriptable client binary from irtt-cli to irtt-client, now that the application also ships a first-class server and the old name reads as an umbrella CLI for both roles. Give each dedicated binary (irtt-client, irtt-tui, irtt-server) its own entry point instead of funneling through the multicall dispatcher's argv0/subcommand logic, so a dedicated binary only links the applet code for the role it implements and always runs that role regardless of the name it's invoked or copied under. irtt-rs remains the feature-selectable multicall dispatcher and is now the only binary that inspects argv0 or subcommands. Move future application releases to the irtt-rs/vX.Y.Z tag namespace (regenerated via `dist generate`), and bump the application package to 0.6.0 as a breaking distribution change. Library crate versions are unaffected.
This commit is contained in:
49 files changed
+365
-213
No files matched your search
@@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "irtt-cli"
|
||||
version = "0.5.0"
|
||||
description = "Command-line IRTT-compatible client and server"
|
||||
name = "irtt-rs"
|
||||
version = "0.6.0"
|
||||
description = "IRTT-compatible client, TUI, and server applications"
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
rust-version.workspace = true
|
||||
@@ -12,6 +12,7 @@ keywords = ["irtt", "networking", "latency", "rtt"]
|
||||
categories = ["command-line-utilities", "network-programming"]
|
||||
|
||||
[lib]
|
||||
name = "irtt_app"
|
||||
doc = false
|
||||
|
||||
[[bin]]
|
||||
@@ -19,9 +20,12 @@ name = "irtt-rs"
|
||||
path = "src/bin/irtt-rs.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "irtt-cli"
|
||||
path = "src/bin/irtt-cli.rs"
|
||||
name = "irtt-client"
|
||||
path = "src/bin/irtt-client.rs"
|
||||
required-features = ["client"]
|
||||
# This applet shares its name with the irtt-client library crate, whose rustdoc
|
||||
# output it would otherwise collide with.
|
||||
doc = false
|
||||
|
||||
[[bin]]
|
||||
name = "irtt-tui"
|
||||
@@ -20,7 +20,7 @@ pub enum AppletDispatch {
|
||||
|
||||
pub fn detect_applet_from_argv0(argv0: &str) -> Option<RequestedApplet> {
|
||||
match applet_basename(argv0) {
|
||||
"irtt-cli" => Some(RequestedApplet::Client),
|
||||
"irtt-client" => Some(RequestedApplet::Client),
|
||||
"irtt-tui" => Some(RequestedApplet::Tui),
|
||||
"irttd" | "irtt-server" => Some(RequestedApplet::Server),
|
||||
"irtt-rs" => None,
|
||||
@@ -58,7 +58,7 @@ pub fn dispatch_from_argv(argv: Vec<OsString>) -> Result<AppletDispatch, String>
|
||||
"-h" | "--help" => Ok(AppletDispatch::Help(dispatcher_help())),
|
||||
"client" => Ok(AppletDispatch::Run {
|
||||
applet: RequestedApplet::Client,
|
||||
argv: applet_argv("irtt-cli", &argv[2..]),
|
||||
argv: applet_argv("irtt-client", &argv[2..]),
|
||||
}),
|
||||
"tui" => Ok(AppletDispatch::Run {
|
||||
applet: RequestedApplet::Tui,
|
||||
@@ -111,7 +111,7 @@ fn server_applet_about() -> &'static str {
|
||||
}
|
||||
|
||||
fn known_applet_binary_names() -> &'static str {
|
||||
"irtt-rs, irtt-cli, irtt-tui, irtt-server"
|
||||
"irtt-rs, irtt-client, irtt-tui, irtt-server"
|
||||
}
|
||||
|
||||
fn applet_command_names() -> &'static str {
|
||||
@@ -119,11 +119,11 @@ fn applet_command_names() -> &'static str {
|
||||
}
|
||||
|
||||
fn dispatcher_after_help() -> String {
|
||||
let mut help = "Recognized applet binary names: irtt-cli, irtt-tui, irtt-server".to_owned();
|
||||
let mut help = "Recognized applet binary names: irtt-client, irtt-tui, irtt-server".to_owned();
|
||||
|
||||
let mut missing = Vec::new();
|
||||
if !cfg!(feature = "client") {
|
||||
missing.push("irtt-cli requires the client feature");
|
||||
missing.push("irtt-client requires the client feature");
|
||||
}
|
||||
if !cfg!(feature = "server") {
|
||||
missing.push("irtt-server requires the server feature");
|
||||
@@ -188,7 +188,7 @@ mod tests {
|
||||
#[test]
|
||||
fn applet_detection_maps_binary_names() {
|
||||
assert_eq!(
|
||||
detect_applet_from_argv0("/usr/bin/irtt-cli"),
|
||||
detect_applet_from_argv0("/usr/bin/irtt-client"),
|
||||
Some(RequestedApplet::Client)
|
||||
);
|
||||
assert_eq!(
|
||||
@@ -203,6 +203,12 @@ mod tests {
|
||||
assert_eq!(detect_applet_from_argv0("custom-name"), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn old_irtt_cli_binary_name_is_no_longer_a_recognized_applet() {
|
||||
assert_eq!(detect_applet_from_argv0("irtt-cli"), None);
|
||||
assert_eq!(detect_applet_from_argv0("/usr/bin/irtt-cli"), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn applet_detection_ignores_the_platform_executable_suffix() {
|
||||
let exe = std::env::consts::EXE_SUFFIX;
|
||||
@@ -212,7 +218,7 @@ mod tests {
|
||||
Some(RequestedApplet::Server)
|
||||
);
|
||||
assert_eq!(
|
||||
detect_applet_from_argv0(&format!("irtt-cli{exe}")),
|
||||
detect_applet_from_argv0(&format!("irtt-client{exe}")),
|
||||
Some(RequestedApplet::Client)
|
||||
);
|
||||
|
||||
@@ -288,7 +294,7 @@ mod tests {
|
||||
.unwrap(),
|
||||
AppletDispatch::Run {
|
||||
applet: RequestedApplet::Client,
|
||||
argv: vec!["irtt-cli".into(), "host:2112".into()],
|
||||
argv: vec!["irtt-client".into(), "host:2112".into()],
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
@@ -311,7 +317,7 @@ mod tests {
|
||||
fn unknown_irtt_binary_name_is_an_error() {
|
||||
let err = dispatch_from_argv(vec!["/usr/bin/irtt-typo".into()]).unwrap_err();
|
||||
assert!(err.contains("unknown applet name 'irtt-typo'"));
|
||||
assert!(err.contains("irtt-cli"));
|
||||
assert!(err.contains("irtt-client"));
|
||||
if cfg!(feature = "tui") {
|
||||
assert!(err.contains("irtt-tui"));
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
use std::process::ExitCode;
|
||||
|
||||
fn main() -> ExitCode {
|
||||
irtt_cli::entry::main()
|
||||
irtt_app::entry::client_main()
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
use std::process::ExitCode;
|
||||
|
||||
fn main() -> ExitCode {
|
||||
irtt_cli::entry::main()
|
||||
irtt_app::entry::dispatcher_main()
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
use std::process::ExitCode;
|
||||
|
||||
fn main() -> ExitCode {
|
||||
irtt_cli::entry::main()
|
||||
irtt_app::entry::server_main()
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
use std::process::ExitCode;
|
||||
|
||||
fn main() -> ExitCode {
|
||||
irtt_cli::entry::main()
|
||||
irtt_app::entry::tui_main()
|
||||
}
|
||||
@@ -9,14 +9,14 @@ use clap::{Parser, ValueEnum};
|
||||
pub const DEFAULT_CLIENT_DURATION: Duration = Duration::from_secs(10);
|
||||
|
||||
#[derive(Debug, Clone, Parser)]
|
||||
#[command(name = "irtt-cli", about = "Minimal IRTT-compatible stream client")]
|
||||
#[command(name = "irtt-client", about = "Minimal IRTT-compatible stream client")]
|
||||
pub struct ClientArgs {
|
||||
/// Server address/host, optionally prefixed with LABEL=. Repeat for multi-target mode.
|
||||
#[arg(
|
||||
value_name = "TARGET",
|
||||
num_args = 0..,
|
||||
value_parser = parse_target,
|
||||
long_help = "Server address/host, optionally prefixed with LABEL=. Repeat for multi-target mode.\n\nExamples:\n irtt-cli host-a:2112 host-b:2112\n irtt-cli eu=host-a:2112 us=host-b:2112\n irtt-cli host-a:2112 eu=host-b:2112"
|
||||
long_help = "Server address/host, optionally prefixed with LABEL=. Repeat for multi-target mode.\n\nExamples:\n irtt-client host-a:2112 host-b:2112\n irtt-client eu=host-a:2112 us=host-b:2112\n irtt-client host-a:2112 eu=host-b:2112"
|
||||
)]
|
||||
pub targets: Vec<TargetArg>,
|
||||
|
||||
@@ -149,7 +149,7 @@ mod tests {
|
||||
use irtt_proto::{Clock, ReceivedStats, StampAt};
|
||||
|
||||
fn parse(args: &[&str]) -> Result<ClientArgs, clap::Error> {
|
||||
let mut argv = vec!["irtt-cli"];
|
||||
let mut argv = vec!["irtt-client"];
|
||||
argv.extend_from_slice(args);
|
||||
ClientArgs::try_parse_from(argv)
|
||||
}
|
||||
File renamed without changes.
File renamed without changes.
@@ -392,7 +392,7 @@ mod tests {
|
||||
use clap::Parser;
|
||||
|
||||
ClientArgs::try_parse_from([
|
||||
"irtt-cli",
|
||||
"irtt-client",
|
||||
"--duration",
|
||||
"4200s",
|
||||
"--interval",
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,136 @@
|
||||
use std::{
|
||||
env,
|
||||
ffi::OsString,
|
||||
process::ExitCode,
|
||||
sync::{atomic::AtomicBool, Arc},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
applet::{dispatch_from_argv, AppletDispatch, RequestedApplet},
|
||||
signal::install_signal_handler,
|
||||
};
|
||||
|
||||
/// Entry point for the `irtt-rs` multicall dispatcher binary.
|
||||
///
|
||||
/// Chooses an applet by subcommand or by recognized argv0 and may reference
|
||||
/// every applet enabled by the active feature set.
|
||||
pub fn dispatcher_main() -> ExitCode {
|
||||
run_with_exit_code("irtt-rs", run_dispatcher_from_env)
|
||||
}
|
||||
|
||||
/// Entry point for the dedicated `irtt-client` binary.
|
||||
///
|
||||
/// Runs the stream client directly; it never consults argv0 or the applet
|
||||
/// dispatcher, so a renamed or copied executable still runs the client.
|
||||
#[cfg(feature = "client")]
|
||||
pub fn client_main() -> ExitCode {
|
||||
run_with_exit_code("irtt-client", || {
|
||||
run_client_applet(env::args_os().collect())
|
||||
})
|
||||
}
|
||||
|
||||
/// Entry point for the dedicated `irtt-tui` binary.
|
||||
///
|
||||
/// Runs the terminal UI directly; it never consults argv0 or the applet
|
||||
/// dispatcher, so a renamed or copied executable still runs the TUI.
|
||||
#[cfg(feature = "tui")]
|
||||
pub fn tui_main() -> ExitCode {
|
||||
run_with_exit_code("irtt-tui", || run_tui_applet(env::args_os().collect()))
|
||||
}
|
||||
|
||||
/// Entry point for the dedicated `irtt-server` binary.
|
||||
///
|
||||
/// Runs the server directly; it never consults argv0 or the applet
|
||||
/// dispatcher, so a renamed or copied executable still runs the server.
|
||||
#[cfg(feature = "server")]
|
||||
pub fn server_main() -> ExitCode {
|
||||
run_with_exit_code("irtt-server", || {
|
||||
run_server_applet(env::args_os().collect())
|
||||
})
|
||||
}
|
||||
|
||||
fn run_with_exit_code(
|
||||
program: &str,
|
||||
f: impl FnOnce() -> Result<(), Box<dyn std::error::Error>>,
|
||||
) -> ExitCode {
|
||||
match f() {
|
||||
Ok(()) => ExitCode::SUCCESS,
|
||||
Err(err) => {
|
||||
eprintln!("{program}: {err}");
|
||||
ExitCode::FAILURE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn run_dispatcher_from_env() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let argv: Vec<OsString> = env::args_os().collect();
|
||||
let (requested, argv) = match dispatch_from_argv(argv)? {
|
||||
AppletDispatch::Run { applet, argv } => (applet, argv),
|
||||
AppletDispatch::Help(help) => {
|
||||
print!("{help}");
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
|
||||
match requested {
|
||||
RequestedApplet::Client => run_client_applet(argv),
|
||||
RequestedApplet::Tui => run_tui_applet(argv),
|
||||
RequestedApplet::Server => run_server_applet(argv),
|
||||
}
|
||||
}
|
||||
|
||||
/// Installs the shutdown signal handler shared by every applet and hands the
|
||||
/// resulting flag to `f`.
|
||||
fn with_shutdown_flag(
|
||||
f: impl FnOnce(&AtomicBool) -> Result<(), Box<dyn std::error::Error>>,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let shutdown_requested = Arc::new(AtomicBool::new(false));
|
||||
install_signal_handler(Arc::clone(&shutdown_requested))
|
||||
.map_err(|err| format!("failed to install signal handler: {err}"))?;
|
||||
f(shutdown_requested.as_ref())
|
||||
}
|
||||
|
||||
#[cfg(feature = "client")]
|
||||
fn run_client_applet(argv: Vec<OsString>) -> Result<(), Box<dyn std::error::Error>> {
|
||||
use clap::Parser;
|
||||
|
||||
with_shutdown_flag(|shutdown_requested| {
|
||||
let args = crate::cmd::client::ClientArgs::parse_from(argv);
|
||||
crate::cmd::client::run_stream(args, shutdown_requested)
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "client"))]
|
||||
fn run_client_applet(_argv: Vec<OsString>) -> Result<(), Box<dyn std::error::Error>> {
|
||||
Err("client applet is not available; rebuild with the client feature".into())
|
||||
}
|
||||
|
||||
#[cfg(feature = "server")]
|
||||
fn run_server_applet(argv: Vec<OsString>) -> Result<(), Box<dyn std::error::Error>> {
|
||||
use clap::Parser;
|
||||
|
||||
with_shutdown_flag(|shutdown_requested| {
|
||||
let args = crate::cmd::server::ServerArgs::parse_from(argv);
|
||||
crate::cmd::server::run_server(args, shutdown_requested)
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "server"))]
|
||||
fn run_server_applet(_argv: Vec<OsString>) -> Result<(), Box<dyn std::error::Error>> {
|
||||
Err("server applet is not available; rebuild with the server feature".into())
|
||||
}
|
||||
|
||||
#[cfg(feature = "tui")]
|
||||
fn run_tui_applet(argv: Vec<OsString>) -> Result<(), Box<dyn std::error::Error>> {
|
||||
use clap::Parser;
|
||||
|
||||
with_shutdown_flag(|shutdown_requested| {
|
||||
let args = crate::cmd::tui::TuiArgs::parse_from(argv);
|
||||
crate::cmd::tui::run_tui(args, shutdown_requested)
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "tui"))]
|
||||
fn run_tui_applet(_argv: Vec<OsString>) -> Result<(), Box<dyn std::error::Error>> {
|
||||
Err("TUI applet is not available; rebuild with the tui feature".into())
|
||||
}
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
+93
-4
@@ -13,6 +13,31 @@ fn irtt_rs_with_arg0(arg0: &str) -> Command {
|
||||
command
|
||||
}
|
||||
|
||||
/// Copies a dedicated binary to `name` in a temp dir and returns a `Command`
|
||||
/// invoking it under that name, proving dedicated binaries decide their role
|
||||
/// from the entry point they were built with, not from argv0.
|
||||
#[cfg(all(unix, any(feature = "client", feature = "server")))]
|
||||
fn copied_binary(cargo_bin_exe: &str, name: &str) -> Command {
|
||||
let dir = std::env::temp_dir().join(format!(
|
||||
"irtt-copied-binary-test-{}-{}",
|
||||
std::process::id(),
|
||||
name
|
||||
));
|
||||
std::fs::create_dir_all(&dir).unwrap();
|
||||
let dest = dir.join(name);
|
||||
std::fs::copy(cargo_bin_exe, &dest).unwrap();
|
||||
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
let mut perms = std::fs::metadata(&dest).unwrap().permissions();
|
||||
perms.set_mode(0o755);
|
||||
std::fs::set_permissions(&dest, perms).unwrap();
|
||||
}
|
||||
|
||||
Command::new(dest)
|
||||
}
|
||||
|
||||
fn output_text(output: &Output) -> String {
|
||||
let mut text = String::new();
|
||||
text.push_str(&String::from_utf8_lossy(&output.stdout));
|
||||
@@ -97,9 +122,37 @@ fn canonical_server_subcommand_reports_unavailable_when_disabled() {
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(feature = "client")]
|
||||
#[test]
|
||||
fn dedicated_client_binary_always_enters_the_client_role() {
|
||||
let output = std::process::Command::new(env!("CARGO_BIN_EXE_irtt-client"))
|
||||
.arg("--help")
|
||||
.output()
|
||||
.unwrap();
|
||||
let text = output_text(&output);
|
||||
|
||||
assert!(output.status.success(), "{text}");
|
||||
assert!(text.contains("Minimal IRTT-compatible stream client"));
|
||||
assert!(text.contains("--format <FORMAT>"));
|
||||
}
|
||||
|
||||
#[cfg(feature = "tui")]
|
||||
#[test]
|
||||
fn dedicated_tui_binary_always_enters_the_tui_role() {
|
||||
let output = std::process::Command::new(env!("CARGO_BIN_EXE_irtt-tui"))
|
||||
.arg("--help")
|
||||
.output()
|
||||
.unwrap();
|
||||
let text = output_text(&output);
|
||||
|
||||
assert!(output.status.success(), "{text}");
|
||||
assert!(text.contains("Minimal IRTT-compatible TUI client"));
|
||||
assert!(text.contains("--duration <DURATION>"));
|
||||
}
|
||||
|
||||
#[cfg(feature = "server")]
|
||||
#[test]
|
||||
fn standalone_server_binary_exposes_the_same_arguments() {
|
||||
fn dedicated_server_binary_always_enters_the_server_role() {
|
||||
let output = std::process::Command::new(env!("CARGO_BIN_EXE_irtt-server"))
|
||||
.arg("--help")
|
||||
.output()
|
||||
@@ -111,11 +164,37 @@ fn standalone_server_binary_exposes_the_same_arguments() {
|
||||
assert!(text.contains("--bind <ADDR>"));
|
||||
}
|
||||
|
||||
#[cfg(all(unix, feature = "client"))]
|
||||
#[test]
|
||||
fn renaming_the_dedicated_client_binary_does_not_change_its_role() {
|
||||
let output = copied_binary(env!("CARGO_BIN_EXE_irtt-client"), "whatever")
|
||||
.arg("--help")
|
||||
.output()
|
||||
.unwrap();
|
||||
let text = output_text(&output);
|
||||
|
||||
assert!(output.status.success(), "{text}");
|
||||
assert!(text.contains("Minimal IRTT-compatible stream client"));
|
||||
}
|
||||
|
||||
#[cfg(all(unix, feature = "server"))]
|
||||
#[test]
|
||||
fn renaming_the_dedicated_server_binary_does_not_change_its_role() {
|
||||
let output = copied_binary(env!("CARGO_BIN_EXE_irtt-server"), "irtt-cli")
|
||||
.arg("--help")
|
||||
.output()
|
||||
.unwrap();
|
||||
let text = output_text(&output);
|
||||
|
||||
assert!(output.status.success(), "{text}");
|
||||
assert!(text.contains("Minimal IRTT-compatible UDP server"));
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
#[cfg(feature = "client")]
|
||||
#[test]
|
||||
fn client_applet_name_dispatches_to_client() {
|
||||
let output = irtt_rs_with_arg0("irtt-cli")
|
||||
let output = irtt_rs_with_arg0("irtt-client")
|
||||
.arg("--help")
|
||||
.output()
|
||||
.unwrap();
|
||||
@@ -130,7 +209,7 @@ fn client_applet_name_dispatches_to_client() {
|
||||
#[cfg(all(unix, not(feature = "client")))]
|
||||
#[test]
|
||||
fn client_applet_name_reports_unavailable_when_disabled() {
|
||||
let output = irtt_rs_with_arg0("irtt-cli")
|
||||
let output = irtt_rs_with_arg0("irtt-client")
|
||||
.arg("--help")
|
||||
.output()
|
||||
.unwrap();
|
||||
@@ -205,7 +284,17 @@ fn unknown_irtt_applet_name_errors() {
|
||||
|
||||
assert!(!output.status.success());
|
||||
assert!(text.contains("unknown applet name 'irtt-typo'"), "{text}");
|
||||
assert!(text.contains("irtt-cli"));
|
||||
assert!(text.contains("irtt-client"));
|
||||
assert!(text.contains("irtt-tui"));
|
||||
assert!(text.contains("irtt-server"));
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
#[test]
|
||||
fn old_irtt_cli_binary_name_is_no_longer_a_recognized_applet() {
|
||||
let output = irtt_rs_with_arg0("irtt-cli").output().unwrap();
|
||||
let text = output_text(&output);
|
||||
|
||||
assert!(!output.status.success());
|
||||
assert!(text.contains("unknown applet name 'irtt-cli'"), "{text}");
|
||||
}
|
||||
+22
-22
@@ -55,7 +55,7 @@ impl InterruptibleFakeServer {
|
||||
|
||||
#[test]
|
||||
fn old_target_option_is_rejected_as_an_unknown_argument() {
|
||||
let output = Command::new(env!("CARGO_BIN_EXE_irtt-cli"))
|
||||
let output = Command::new(env!("CARGO_BIN_EXE_irtt-client"))
|
||||
.args(["--target", "eu=127.0.0.1:2112"])
|
||||
.output()
|
||||
.unwrap();
|
||||
@@ -67,7 +67,7 @@ fn old_target_option_is_rejected_as_an_unknown_argument() {
|
||||
|
||||
#[test]
|
||||
fn list_columns_succeeds_without_target() {
|
||||
let output = Command::new(env!("CARGO_BIN_EXE_irtt-cli"))
|
||||
let output = Command::new(env!("CARGO_BIN_EXE_irtt-client"))
|
||||
.arg("--list-columns")
|
||||
.output()
|
||||
.unwrap();
|
||||
@@ -88,7 +88,7 @@ fn single_target_default_table_includes_target_and_accepts_pacing() {
|
||||
let server = InTreeServer::start();
|
||||
let target = server.addr.to_string();
|
||||
|
||||
let output = Command::new(env!("CARGO_BIN_EXE_irtt-cli"))
|
||||
let output = Command::new(env!("CARGO_BIN_EXE_irtt-client"))
|
||||
.args([
|
||||
"--duration",
|
||||
"30ms",
|
||||
@@ -125,7 +125,7 @@ fn single_target_default_table_includes_target_and_accepts_pacing() {
|
||||
fn single_labeled_target_default_table_includes_target() {
|
||||
let server = InTreeServer::start();
|
||||
|
||||
let output = Command::new(env!("CARGO_BIN_EXE_irtt-cli"))
|
||||
let output = Command::new(env!("CARGO_BIN_EXE_irtt-client"))
|
||||
.args([
|
||||
"--duration",
|
||||
"30ms",
|
||||
@@ -160,7 +160,7 @@ fn single_labeled_target_default_table_includes_target() {
|
||||
fn single_labeled_target_default_csv_includes_target_and_event_wall_ns() {
|
||||
let server = InTreeServer::start();
|
||||
|
||||
let output = Command::new(env!("CARGO_BIN_EXE_irtt-cli"))
|
||||
let output = Command::new(env!("CARGO_BIN_EXE_irtt-client"))
|
||||
.args([
|
||||
"--duration",
|
||||
"30ms",
|
||||
@@ -199,7 +199,7 @@ fn single_positional_target_default_csv_includes_target_as_positional_label() {
|
||||
let server = InTreeServer::start();
|
||||
let target = server.addr.to_string();
|
||||
|
||||
let output = Command::new(env!("CARGO_BIN_EXE_irtt-cli"))
|
||||
let output = Command::new(env!("CARGO_BIN_EXE_irtt-client"))
|
||||
.args([
|
||||
"--duration",
|
||||
"30ms",
|
||||
@@ -237,7 +237,7 @@ fn multi_target_default_table_includes_both_labels() {
|
||||
let a = InTreeServer::start();
|
||||
let b = InTreeServer::start();
|
||||
|
||||
let output = Command::new(env!("CARGO_BIN_EXE_irtt-cli"))
|
||||
let output = Command::new(env!("CARGO_BIN_EXE_irtt-client"))
|
||||
.args([
|
||||
"--duration",
|
||||
"40ms",
|
||||
@@ -287,7 +287,7 @@ fn multi_target_final_summary_preserves_cli_argument_order() {
|
||||
let a = InTreeServer::start();
|
||||
let m = InTreeServer::start();
|
||||
|
||||
let output = Command::new(env!("CARGO_BIN_EXE_irtt-cli"))
|
||||
let output = Command::new(env!("CARGO_BIN_EXE_irtt-client"))
|
||||
.args([
|
||||
"--duration",
|
||||
"40ms",
|
||||
@@ -331,7 +331,7 @@ fn mixed_labeled_and_unlabeled_positional_targets_both_appear_in_output() {
|
||||
let b = InTreeServer::start();
|
||||
let unlabeled_addr = a.addr.to_string();
|
||||
|
||||
let output = Command::new(env!("CARGO_BIN_EXE_irtt-cli"))
|
||||
let output = Command::new(env!("CARGO_BIN_EXE_irtt-client"))
|
||||
.args([
|
||||
"--duration",
|
||||
"40ms",
|
||||
@@ -376,7 +376,7 @@ fn single_target_custom_target_column_renders_positional_label() {
|
||||
let server = InTreeServer::start();
|
||||
let target = server.addr.to_string();
|
||||
|
||||
let output = Command::new(env!("CARGO_BIN_EXE_irtt-cli"))
|
||||
let output = Command::new(env!("CARGO_BIN_EXE_irtt-client"))
|
||||
.args([
|
||||
"--duration",
|
||||
"30ms",
|
||||
@@ -414,7 +414,7 @@ fn single_target_custom_target_column_renders_positional_label() {
|
||||
fn custom_columns_remain_authoritative_and_omit_target() {
|
||||
let server = InTreeServer::start();
|
||||
|
||||
let output = Command::new(env!("CARGO_BIN_EXE_irtt-cli"))
|
||||
let output = Command::new(env!("CARGO_BIN_EXE_irtt-client"))
|
||||
.args([
|
||||
"--duration",
|
||||
"30ms",
|
||||
@@ -448,7 +448,7 @@ fn custom_columns_remain_authoritative_and_omit_target() {
|
||||
fn columns_default_keyword_includes_target_for_single_target() {
|
||||
let server = InTreeServer::start();
|
||||
|
||||
let output = Command::new(env!("CARGO_BIN_EXE_irtt-cli"))
|
||||
let output = Command::new(env!("CARGO_BIN_EXE_irtt-client"))
|
||||
.args([
|
||||
"--duration",
|
||||
"30ms",
|
||||
@@ -484,7 +484,7 @@ fn columns_default_keyword_includes_target_for_single_target() {
|
||||
fn single_labeled_target_default_jsonl_includes_target() {
|
||||
let server = InTreeServer::start();
|
||||
|
||||
let output = Command::new(env!("CARGO_BIN_EXE_irtt-cli"))
|
||||
let output = Command::new(env!("CARGO_BIN_EXE_irtt-client"))
|
||||
.args([
|
||||
"--duration",
|
||||
"30ms",
|
||||
@@ -517,7 +517,7 @@ fn single_labeled_target_default_jsonl_includes_target() {
|
||||
#[test]
|
||||
fn continuous_single_target_peer_close_exits_nonzero() {
|
||||
let server = start_peer_close_server(test_params(None, Duration::from_millis(10)));
|
||||
let mut command = Command::new(env!("CARGO_BIN_EXE_irtt-cli"));
|
||||
let mut command = Command::new(env!("CARGO_BIN_EXE_irtt-client"));
|
||||
command.args([
|
||||
"--duration",
|
||||
"0s",
|
||||
@@ -553,7 +553,7 @@ fn continuous_single_target_peer_close_exits_nonzero() {
|
||||
fn continuous_single_target_interruption_drains_final_events_and_succeeds() {
|
||||
let params = test_params(None, Duration::from_millis(10));
|
||||
let server = start_interruptible_echo_server(params);
|
||||
let mut command = Command::new(env!("CARGO_BIN_EXE_irtt-cli"));
|
||||
let mut command = Command::new(env!("CARGO_BIN_EXE_irtt-client"));
|
||||
command.args([
|
||||
"--duration",
|
||||
"0s",
|
||||
@@ -611,7 +611,7 @@ fn multi_target_csv_emits_rows_for_both_labels() {
|
||||
let a = InTreeServer::start();
|
||||
let b = InTreeServer::start();
|
||||
|
||||
let output = Command::new(env!("CARGO_BIN_EXE_irtt-cli"))
|
||||
let output = Command::new(env!("CARGO_BIN_EXE_irtt-client"))
|
||||
.args([
|
||||
"--duration",
|
||||
"40ms",
|
||||
@@ -654,7 +654,7 @@ fn finite_multi_target_peer_close_is_accepted_as_completion() {
|
||||
let params = test_params(Some(Duration::from_millis(40)), Duration::from_millis(10));
|
||||
let a = start_peer_close_server(params.clone());
|
||||
let b = start_peer_close_server(params);
|
||||
let mut command = Command::new(env!("CARGO_BIN_EXE_irtt-cli"));
|
||||
let mut command = Command::new(env!("CARGO_BIN_EXE_irtt-client"));
|
||||
command.args([
|
||||
"--duration",
|
||||
"40ms",
|
||||
@@ -716,7 +716,7 @@ fn continuous_all_peer_closed_targets_exit_nonzero() {
|
||||
.send(())
|
||||
.expect("target B stopped waiting for peer-close release");
|
||||
});
|
||||
let mut command = Command::new(env!("CARGO_BIN_EXE_irtt-cli"));
|
||||
let mut command = Command::new(env!("CARGO_BIN_EXE_irtt-client"));
|
||||
command.args([
|
||||
"--duration",
|
||||
"0s",
|
||||
@@ -766,7 +766,7 @@ fn continuous_partial_peer_close_preserves_queued_rows_and_reports_peer_close()
|
||||
let (healthy_reply_tx, healthy_reply_rx) = mpsc::channel();
|
||||
let healthy = start_echo_server_with_first_reply(params.clone(), healthy_reply_tx);
|
||||
let peer_closed = start_gated_peer_close_server(params, vec![healthy_reply_rx]);
|
||||
let mut command = Command::new(env!("CARGO_BIN_EXE_irtt-cli"));
|
||||
let mut command = Command::new(env!("CARGO_BIN_EXE_irtt-client"));
|
||||
command.args([
|
||||
"--duration",
|
||||
"0s",
|
||||
@@ -820,7 +820,7 @@ fn continuous_mixed_peer_close_and_open_failure_exits_nonzero() {
|
||||
let failure = start_open_failure_server_with_signal(params.clone(), failure_tx);
|
||||
let healthy = start_echo_server_with_first_reply(params.clone(), healthy_reply_tx);
|
||||
let peer_closed = start_gated_peer_close_server(params, vec![failure_rx, healthy_reply_rx]);
|
||||
let mut command = Command::new(env!("CARGO_BIN_EXE_irtt-cli"));
|
||||
let mut command = Command::new(env!("CARGO_BIN_EXE_irtt-client"));
|
||||
command.args([
|
||||
"--duration",
|
||||
"0s",
|
||||
@@ -872,7 +872,7 @@ fn explicit_group_interruption_succeeds_without_peer_close_error() {
|
||||
let params = test_params(None, Duration::from_millis(10));
|
||||
let a = start_interruptible_echo_server(params.clone());
|
||||
let b = start_interruptible_echo_server(params);
|
||||
let mut command = Command::new(env!("CARGO_BIN_EXE_irtt-cli"));
|
||||
let mut command = Command::new(env!("CARGO_BIN_EXE_irtt-client"));
|
||||
command.args([
|
||||
"--duration",
|
||||
"0s",
|
||||
@@ -932,7 +932,7 @@ fn all_open_failures_exit_nonzero_with_diagnostics() {
|
||||
let params = test_params(None, Duration::from_millis(10));
|
||||
let a = start_open_failure_server(params.clone());
|
||||
let b = start_open_failure_server(params);
|
||||
let mut command = Command::new(env!("CARGO_BIN_EXE_irtt-cli"));
|
||||
let mut command = Command::new(env!("CARGO_BIN_EXE_irtt-client"));
|
||||
command.args([
|
||||
"--duration",
|
||||
"0s",
|
||||
File renamed without changes.
@@ -1,99 +0,0 @@
|
||||
use std::{
|
||||
env,
|
||||
ffi::OsString,
|
||||
process::ExitCode,
|
||||
sync::{atomic::AtomicBool, Arc},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
applet::{dispatch_from_argv, AppletDispatch, RequestedApplet},
|
||||
signal::install_signal_handler,
|
||||
};
|
||||
|
||||
pub fn main() -> ExitCode {
|
||||
match run_from_env() {
|
||||
Ok(()) => ExitCode::SUCCESS,
|
||||
Err(err) => {
|
||||
eprintln!("irtt-rs: {err}");
|
||||
ExitCode::FAILURE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn run_from_env() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let argv: Vec<OsString> = env::args_os().collect();
|
||||
let (requested, argv) = match dispatch_from_argv(argv)? {
|
||||
AppletDispatch::Run { applet, argv } => (applet, argv),
|
||||
AppletDispatch::Help(help) => {
|
||||
print!("{help}");
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
|
||||
let shutdown_requested = Arc::new(AtomicBool::new(false));
|
||||
install_signal_handler(Arc::clone(&shutdown_requested))
|
||||
.map_err(|err| format!("failed to install signal handler: {err}"))?;
|
||||
|
||||
match requested {
|
||||
RequestedApplet::Client => run_client_applet(argv, shutdown_requested.as_ref()),
|
||||
RequestedApplet::Tui => run_tui_applet(argv, shutdown_requested.as_ref()),
|
||||
RequestedApplet::Server => run_server_applet(argv, shutdown_requested.as_ref()),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "client")]
|
||||
fn run_client_applet(
|
||||
argv: Vec<OsString>,
|
||||
shutdown_requested: &AtomicBool,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
use clap::Parser;
|
||||
|
||||
let args = crate::cmd::client::ClientArgs::parse_from(argv);
|
||||
crate::cmd::client::run_stream(args, shutdown_requested)
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "client"))]
|
||||
fn run_client_applet(
|
||||
_argv: Vec<OsString>,
|
||||
_shutdown_requested: &AtomicBool,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
Err("client applet is not available; rebuild with the client feature".into())
|
||||
}
|
||||
|
||||
#[cfg(feature = "server")]
|
||||
fn run_server_applet(
|
||||
argv: Vec<OsString>,
|
||||
shutdown_requested: &AtomicBool,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
use clap::Parser;
|
||||
|
||||
let args = crate::cmd::server::ServerArgs::parse_from(argv);
|
||||
crate::cmd::server::run_server(args, shutdown_requested)
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "server"))]
|
||||
fn run_server_applet(
|
||||
_argv: Vec<OsString>,
|
||||
_shutdown_requested: &AtomicBool,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
Err("server applet is not available; rebuild with the server feature".into())
|
||||
}
|
||||
|
||||
#[cfg(feature = "tui")]
|
||||
fn run_tui_applet(
|
||||
argv: Vec<OsString>,
|
||||
shutdown_requested: &AtomicBool,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
use clap::Parser;
|
||||
|
||||
let args = crate::cmd::tui::TuiArgs::parse_from(argv);
|
||||
crate::cmd::tui::run_tui(args, shutdown_requested)
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "tui"))]
|
||||
fn run_tui_applet(
|
||||
_argv: Vec<OsString>,
|
||||
_shutdown_requested: &AtomicBool,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
Err("TUI applet is not available; rebuild with the tui feature".into())
|
||||
}
|
||||
Reference in new issue
Block a user