From 1aa4ea179bdf8ca6fd769c28ada932ef94f3cb73 Mon Sep 17 00:00:00 2001 From: ldm0 Date: Fri, 21 Aug 2026 17:41:14 +0800 Subject: [PATCH] fix(cli): stop inferring subcommands from flags --- moli/src/cli.rs | 138 ++++++++++++---------------------------------- moli/tests/cli.rs | 27 +++++---- 2 files changed, 51 insertions(+), 114 deletions(-) diff --git a/moli/src/cli.rs b/moli/src/cli.rs index 7782b04400..d51da52f5f 100644 --- a/moli/src/cli.rs +++ b/moli/src/cli.rs @@ -2,64 +2,6 @@ use std::{ffi::OsString, num::NonZeroU32}; use clap::{Args, Parser, Subcommand, ValueEnum}; -const FETCH_INFER_FLAGS: &[&str] = &[ - "--dump", - "-d", - "--header", - "-H", - "--trace-network", - "--trace-matched-response-body", - "--noscript", - "-n", - "--strip-mode", - "--with-base", - "--with-frames", - "--wait-until", - "-w", - "--redirect-wait-ms", - "--wait-selector", - "-s", - "--wait-script", - "--wait-script-file", - "--delay-ms", - "--wait-response-url", - "--wait-response-body", - "--wait-response-json", - "--document-start-script", - "--document-start-script-file", - "--image", - "-i", - "--font", - "-f", - "--audio", - "-a", - "--video", - "-v", - "--media", - "-m", - "--text-track", - "-T", - "--resource", - "-r", - "--disable-subframes", - "--wait-ms", - "--profile-dir", - "-P", - "--web-bot-auth-key-file", - "--web-bot-auth-keyid", - "--web-bot-auth-domain", - "--web-bot-auth-profile", -]; -const SERVE_INFER_FLAGS: &[&str] = &[ - "--host", - "--port", - "-p", - "--timeout", - "-t", - "--layout", - "-l", -]; -const EXPLICIT_COMMANDS: &[&str] = &["fetch", "serve"]; const DUMP_MODES: &[&str] = &[ "json", "html", @@ -538,28 +480,10 @@ fn infer_command(next: Option<&OsString>) -> Option<&'static str> { }; let next = next.to_string_lossy(); - if EXPLICIT_COMMANDS.contains(&next.as_ref()) { - return None; - } - if next.starts_with("http://") || next.starts_with("https://") { return Some("fetch"); } - if FETCH_INFER_FLAGS - .iter() - .any(|flag| next == *flag || next.starts_with(&format!("{flag}="))) - { - return Some("fetch"); - } - - if SERVE_INFER_FLAGS - .iter() - .any(|flag| next == *flag || next.starts_with(&format!("{flag}="))) - { - return Some("serve"); - } - None } @@ -595,9 +519,14 @@ mod tests { } #[test] - fn delay_ms_infers_fetch_command() { - let args = - normalize_args_for_compat(["moli", "--delay-ms", "250", "https://example.test/"]); + fn parses_delay_ms_with_explicit_fetch_command() { + let args = normalize_args_for_compat([ + "moli", + "fetch", + "--delay-ms", + "250", + "https://example.test/", + ]); let cli = Cli::parse_from(args); match cli.command { @@ -607,9 +536,13 @@ mod tests { } #[test] - fn redirect_wait_ms_infers_fetch_command() { - let args = - normalize_args_for_compat(["moli", "--redirect-wait-ms=1500", "https://example.test/"]); + fn parses_redirect_wait_ms_with_explicit_fetch_command() { + let args = normalize_args_for_compat([ + "moli", + "fetch", + "--redirect-wait-ms=1500", + "https://example.test/", + ]); let cli = Cli::parse_from(args); match cli.command { @@ -631,8 +564,8 @@ mod tests { } #[test] - fn port_equals_infers_serve_command() { - let args = normalize_args_for_compat(["moli", "--port=0"]); + fn parses_port_equals_with_explicit_serve_command() { + let args = normalize_args_for_compat(["moli", "serve", "--port=0"]); let cli = Cli::parse_from(args); match cli.command { @@ -771,8 +704,8 @@ mod tests { } #[test] - fn short_dump_infers_fetch_command_and_defaults_its_value() { - let args = normalize_args_for_compat(["moli", "-d", "https://example.test/"]); + fn short_dump_defaults_its_value_with_explicit_fetch_command() { + let args = normalize_args_for_compat(["moli", "fetch", "-d", "https://example.test/"]); let cli = Cli::parse_from(args); match cli.command { @@ -783,24 +716,21 @@ mod tests { } #[test] - fn short_port_infers_serve_command() { - let args = normalize_args_for_compat(["moli", "-p", "0"]); - let cli = Cli::parse_from(args); - - match cli.command { - Commands::Serve(args) => assert_eq!(args.port, 0), - other => panic!("expected serve command, got {other:?}"), - } - } - - #[test] - fn layout_infers_serve_command() { - let args = normalize_args_for_compat(["moli", "--layout"]); - let cli = Cli::parse_from(args); - - match cli.command { - Commands::Serve(args) => assert!(args.common.layout), - other => panic!("expected serve command, got {other:?}"), + fn flags_do_not_infer_subcommands() { + for arguments in [ + ["moli", "--dump", "html", "https://example.test/"].as_slice(), + ["moli", "-d", "html", "https://example.test/"].as_slice(), + ["moli", "--port=0"].as_slice(), + ["moli", "-p", "0"].as_slice(), + ["moli", "--layout"].as_slice(), + ] { + let error = Cli::try_parse_from(normalize_args_for_compat(arguments.iter().copied())) + .expect_err("a flag must not infer a subcommand"); + assert_eq!( + error.kind(), + clap::error::ErrorKind::UnknownArgument, + "unexpected parse result for {arguments:?}" + ); } } } diff --git a/moli/tests/cli.rs b/moli/tests/cli.rs index 056e625c59..109a8233d7 100644 --- a/moli/tests/cli.rs +++ b/moli/tests/cli.rs @@ -196,7 +196,7 @@ fn rejects_removed_wpt_dump_mode() { } #[test] -fn parses_binary_dump_modes_with_inferred_fetch_command() { +fn parses_binary_dump_modes_with_explicit_fetch_command() { for (value, expected) in [ ("screenshot", DumpFormat::Screenshot), ("screenshot_full", DumpFormat::ScreenshotFull), @@ -204,6 +204,7 @@ fn parses_binary_dump_modes_with_inferred_fetch_command() { ] { let cli = Cli::try_parse_from(normalize_args_for_compat([ "moli", + "fetch", "--dump", value, "https://example.com", @@ -402,9 +403,10 @@ fn infers_fetch_mode_from_bare_url() { } #[test] -fn infers_fetch_mode_from_fetch_only_flags_and_defaults_dump_to_html() { +fn parses_bare_dump_with_explicit_fetch_command_and_defaults_to_html() { let cli = Cli::try_parse_from(normalize_args_for_compat([ "moli", + "fetch", "--dump", "https://example.com", ])) @@ -438,9 +440,10 @@ fn infers_fetch_mode_from_fetch_only_flags_and_defaults_dump_to_html() { } #[test] -fn infers_fetch_mode_from_header_flag() { +fn parses_header_flag_with_explicit_fetch_command() { let cli = Cli::try_parse_from(normalize_args_for_compat([ "moli", + "fetch", "-H", "X-Test: one", "https://example.com", @@ -478,7 +481,7 @@ fn infers_fetch_mode_from_header_flag() { } #[test] -fn every_optional_resource_flag_infers_fetch_mode() { +fn every_optional_resource_flag_parses_with_explicit_fetch_command() { let cases = [ ("--image", 0), ("--font", 1), @@ -492,12 +495,13 @@ fn every_optional_resource_flag_infers_fetch_mode() { for (flag, enabled_index) in cases { let cli = Cli::try_parse_from(normalize_args_for_compat([ "moli", + "fetch", flag, "https://example.com", ])) - .unwrap_or_else(|error| panic!("{flag} should infer fetch mode: {error}")); + .unwrap_or_else(|error| panic!("{flag} should parse for fetch: {error}")); let Commands::Fetch(args) = cli.command else { - panic!("{flag} should infer the fetch command"); + panic!("{flag} should preserve the explicit fetch command"); }; let values = [ args.common.image, @@ -521,9 +525,10 @@ fn every_optional_resource_flag_infers_fetch_mode() { } #[test] -fn infers_fetch_mode_from_disable_subframes_flag() { +fn parses_disable_subframes_with_explicit_fetch_command() { let cli = Cli::try_parse_from(normalize_args_for_compat([ "moli", + "fetch", "--disable-subframes", "https://example.com", ])) @@ -555,9 +560,10 @@ fn infers_serve_mode_when_called_without_args() { } #[test] -fn infers_serve_mode_from_legacy_serve_flags() { +fn parses_serve_flags_with_explicit_command() { let cli = Cli::try_parse_from(normalize_args_for_compat([ "moli", + "serve", "--host", "0.0.0.0", "--port", @@ -792,7 +798,7 @@ fn fetch_strip_options_combine_cli_selections() { } #[test] -fn does_not_infer_mode_from_top_level_common_flags() { +fn does_not_infer_subcommand_from_flag() { let error = Cli::try_parse_from(normalize_args_for_compat(["moli", "--obey-robots"])).unwrap_err(); @@ -872,9 +878,10 @@ fn parses_fetch_domstable_wait_until() { } #[test] -fn infers_fetch_mode_from_wait_flags() { +fn parses_wait_flags_with_explicit_fetch_command() { let cli = Cli::try_parse_from(normalize_args_for_compat([ "moli", + "fetch", "--wait-selector", "#ready", "https://example.com",