fix(cli): stop inferring subcommands from flags

This commit is contained in:
ldm0
2026-08-21 17:41:14 +08:00
parent 0370ade6c3
commit 1aa4ea179b
2 changed files with 51 additions and 114 deletions
+34 -104
View File
@@ -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:?}"
);
}
}
}
+17 -10
View File
@@ -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",