diff --git a/crates/ptth_multi_call_server/src/main.rs b/crates/ptth_multi_call_server/src/main.rs index 607d046..479453c 100644 --- a/crates/ptth_multi_call_server/src/main.rs +++ b/crates/ptth_multi_call_server/src/main.rs @@ -1,7 +1,9 @@ use std::{ + ffi::OsString, iter::FromIterator, }; +#[derive (Debug, PartialEq)] enum Subcommand { PtthServer, PtthQuicEndServer, @@ -22,30 +24,109 @@ fn parse_subcommand (name: &str) -> Option } } +fn parse_args (args: &[OsString]) -> anyhow::Result <(Subcommand, &[OsString])> +{ + let arg_0 = match args.get (0) { + Some (x) => x, + None => anyhow::bail! ("arg 0 must be the exe name"), + }; + + let arg_0 = arg_0.to_str ().ok_or_else (|| anyhow::anyhow! ("arg 0 should be valid UTF-8"))?; + match parse_subcommand (arg_0) { + Some (x) => return Ok ((x, args)), + None => (), + } + + let arg_1 = match args.get (1) { + Some (x) => x, + None => anyhow::bail! ("arg 1 must be the subcommand if arg 0 is not"), + }; + + let arg_1 = arg_1.to_str ().ok_or_else (|| anyhow::anyhow! ("arg 1 subcommand should be valid UTF-8"))?; + match parse_subcommand (arg_1) { + Some (x) => return Ok ((x, &args [1..])), + None => (), + } + + anyhow::bail! ("Subcommand must be either arg 0 (exe name) or arg 1") +} + #[tokio::main] async fn main () -> anyhow::Result <()> { tracing_subscriber::fmt::init (); let args = Vec::from_iter (std::env::args_os ()); - let arg_0 = args [0].to_str ().expect ("exe name should be valid UTF-8"); - match parse_subcommand (arg_0) { - Some (Subcommand::PtthServer) => return ptth_server::executable::main (&args).await, - Some (Subcommand::PtthQuicEndServer) => return quic_demo::executable_end_server::main (&args).await, - _ => (), + let (subcommand, args) = parse_args (&args)?; + match subcommand { + Subcommand::PtthServer => ptth_server::executable::main (&args).await, + Subcommand::PtthQuicEndServer => quic_demo::executable_end_server::main (&args).await, + } +} + +#[cfg (test)] +mod tests { + use super::*; + + #[test] + fn multi_call () -> anyhow::Result <()> { + let negative_cases = vec! [ + vec! [], + vec! ["invalid_exe_name"], + vec! ["ptth_multi_call_server"], + vec! ["ptth_multi_call_server", "invalid_subcommand"], + ]; + + for input in &negative_cases { + let input: Vec <_> = input.iter ().map (OsString::from).collect (); + + let actual = parse_args (&input); + assert! (actual.is_err ()); + } + + let positive_cases = vec! [ + (vec! ["ptth_server"], (Subcommand::PtthServer, vec! ["ptth_server"])), + (vec! ["ptth_server", "--help"], (Subcommand::PtthServer, vec! ["ptth_server", "--help"])), + (vec! ["ptth_quic_end_server", "--help"], (Subcommand::PtthQuicEndServer, vec! ["ptth_quic_end_server", "--help"])), + (vec! ["ptth_multi_call_server", "ptth_server"], (Subcommand::PtthServer, vec! ["ptth_server"])), + ( + vec! [ + "ptth_multi_call_server", + "ptth_server", + "--help" + ], + ( + Subcommand::PtthServer, + vec! [ + "ptth_server", + "--help" + ] + ) + ), + ( + vec! [ + "invalid_exe_name", + "ptth_server", + "--help" + ], + ( + Subcommand::PtthServer, + vec! [ + "ptth_server", + "--help" + ] + ) + ), + ]; + + for (input, (expected_subcommand, expected_args)) in &positive_cases { + let input: Vec <_> = input.iter ().map (OsString::from).collect (); + + let (actual_subcommand, actual_args) = parse_args (&input)?; + assert_eq! (expected_subcommand, &actual_subcommand); + assert_eq! (expected_args, actual_args); + } + + Ok (()) } - - let arg_1 = match args.get (1) { - Some (x) => x, - None => anyhow::bail! ("Subcommand must be the first argument if it's not the exe name"), - }; - - let arg_1 = arg_1.to_str ().expect ("subcommand should be valid UTF-8"); - match parse_subcommand (arg_1) { - Some (Subcommand::PtthServer) => return ptth_server::executable::main (&args [1..]).await, - Some (Subcommand::PtthQuicEndServer) => return quic_demo::executable_end_server::main (&args [1..]).await, - _ => (), - } - - anyhow::bail! ("Subcommand should be provided in exe name or first argument, e.g. `./ptth_multi_call_server ptth_server`"); }