From b5e8ba5651074fa637a15754f302be042f61d36e Mon Sep 17 00:00:00 2001 From: crowlkats Date: Thu, 5 Sep 2024 09:55:53 +0200 Subject: [PATCH 1/3] feat(flags): allow double commas to escape values in path based flags --- cli/args/flags.rs | 251 ++++++++++++++++++++++++++++------------------ 1 file changed, 151 insertions(+), 100 deletions(-) diff --git a/cli/args/flags.rs b/cli/args/flags.rs index 44f97010f01244..3fc4b6726c2a41 100644 --- a/cli/args/flags.rs +++ b/cli/args/flags.rs @@ -1227,29 +1227,29 @@ pub fn flags_from_vec(args: Vec) -> clap::error::Result { match subcommand.as_str() { "add" => add_parse(&mut flags, &mut m), "remove" => remove_parse(&mut flags, &mut m), - "bench" => bench_parse(&mut flags, &mut m), + "bench" => bench_parse(&mut flags, &mut m)?, "bundle" => bundle_parse(&mut flags, &mut m), - "cache" => cache_parse(&mut flags, &mut m), - "check" => check_parse(&mut flags, &mut m), + "cache" => cache_parse(&mut flags, &mut m)?, + "check" => check_parse(&mut flags, &mut m)?, "clean" => clean_parse(&mut flags, &mut m), - "compile" => compile_parse(&mut flags, &mut m), + "compile" => compile_parse(&mut flags, &mut m)?, "completions" => completions_parse(&mut flags, &mut m, app), "coverage" => coverage_parse(&mut flags, &mut m), - "doc" => doc_parse(&mut flags, &mut m), - "eval" => eval_parse(&mut flags, &mut m), + "doc" => doc_parse(&mut flags, &mut m)?, + "eval" => eval_parse(&mut flags, &mut m)?, "fmt" => fmt_parse(&mut flags, &mut m), "init" => init_parse(&mut flags, &mut m), - "info" => info_parse(&mut flags, &mut m), - "install" => install_parse(&mut flags, &mut m), + "info" => info_parse(&mut flags, &mut m)?, + "install" => install_parse(&mut flags, &mut m)?, "json_reference" => json_reference_parse(&mut flags, &mut m, app), "jupyter" => jupyter_parse(&mut flags, &mut m), "lint" => lint_parse(&mut flags, &mut m), "lsp" => lsp_parse(&mut flags, &mut m), - "repl" => repl_parse(&mut flags, &mut m), + "repl" => repl_parse(&mut flags, &mut m)?, "run" => run_parse(&mut flags, &mut m, app, false)?, "serve" => serve_parse(&mut flags, &mut m, app)?, "task" => task_parse(&mut flags, &mut m), - "test" => test_parse(&mut flags, &mut m), + "test" => test_parse(&mut flags, &mut m)?, "types" => types_parse(&mut flags, &mut m), "uninstall" => uninstall_parse(&mut flags, &mut m), "upgrade" => upgrade_parse(&mut flags, &mut m), @@ -1583,7 +1583,7 @@ glob {*_,*.,}bench.{js,mjs,ts,mts,jsx,tsx}: Arg::new("ignore") .long("ignore") .num_args(1..) - .use_value_delimiter(true) + .action(ArgAction::Append) .require_equals(true) .help("Ignore files"), ) @@ -1835,7 +1835,7 @@ Generate html reports from lcov: Arg::new("ignore") .long("ignore") .num_args(1..) - .use_value_delimiter(true) + .action(ArgAction::Append) .require_equals(true) .help("Ignore coverage files") .value_hint(ValueHint::AnyPath), @@ -2116,7 +2116,7 @@ Ignore formatting a file by adding an ignore comment at the top of the file: Arg::new("ignore") .long("ignore") .num_args(1..) - .use_value_delimiter(true) + .action(ArgAction::Append) .require_equals(true) .help("Ignore formatting particular source files") .value_hint(ValueHint::AnyPath) @@ -2554,7 +2554,7 @@ Ignore linting a file by adding an ignore comment at the top of the file: Arg::new("ignore") .long("ignore") .num_args(1..) - .use_value_delimiter(true) + .action(ArgAction::Append) .require_equals(true) .help("Ignore linting particular source files") .value_hint(ValueHint::AnyPath) @@ -2595,7 +2595,7 @@ fn repl_subcommand() -> Command { Arg::new("eval-file") .long("eval-file") .num_args(1..) - .use_value_delimiter(true) + .action(ArgAction::Append) .require_equals(true) .help("Evaluates the provided file(s) as scripts when the REPL starts. Accepts file paths and URLs") .value_hint(ValueHint::AnyPath), @@ -2747,7 +2747,7 @@ Directory arguments are expanded to all contained files matching the glob Arg::new("ignore") .long("ignore") .num_args(1..) - .use_value_delimiter(true) + .action(ArgAction::Append) .require_equals(true) .help("Ignore files") .value_hint(ValueHint::AnyPath), @@ -3108,11 +3108,10 @@ Docs: https://docs.deno.com/go/permissions .long("allow-read") .short('R') .num_args(0..) - .use_value_delimiter(true) + .action(ArgAction::Append) .require_equals(true) .value_name("PATH") .help("Allow file system read access. Optionally specify allowed paths") - .value_parser(value_parser!(String)) .value_hint(ValueHint::AnyPath) .hide(true), ) @@ -3120,11 +3119,10 @@ Docs: https://docs.deno.com/go/permissions Arg::new("deny-read") .long("deny-read") .num_args(0..) - .use_value_delimiter(true) + .action(ArgAction::Append) .require_equals(true) .value_name("PATH") .help("Deny file system read access. Optionally specify denied paths") - .value_parser(value_parser!(String)) .value_hint(ValueHint::AnyPath) .hide(true), ) @@ -3133,11 +3131,10 @@ Docs: https://docs.deno.com/go/permissions .long("allow-write") .short('W') .num_args(0..) - .use_value_delimiter(true) + .action(ArgAction::Append) .require_equals(true) .value_name("PATH") .help("Allow file system write access. Optionally specify allowed paths") - .value_parser(value_parser!(String)) .value_hint(ValueHint::AnyPath) .hide(true), ) @@ -3145,11 +3142,10 @@ Docs: https://docs.deno.com/go/permissions Arg::new("deny-write") .long("deny-write") .num_args(0..) - .use_value_delimiter(true) + .action(ArgAction::Append) .require_equals(true) .value_name("PATH") .help("Deny file system write access. Optionally specify denied paths") - .value_parser(value_parser!(String)) .value_hint(ValueHint::AnyPath) .hide(true), ) @@ -3266,11 +3262,10 @@ Docs: https://docs.deno.com/go/permissions Arg::new("allow-ffi") .long("allow-ffi") .num_args(0..) - .use_value_delimiter(true) + .action(ArgAction::Append) .require_equals(true) .value_name("PATH") .help("(Unstable) Allow loading dynamic libraries. Optionally specify allowed directories or files") - .value_parser(value_parser!(String)) .value_hint(ValueHint::AnyPath) .hide(true), ) @@ -3278,11 +3273,10 @@ Docs: https://docs.deno.com/go/permissions Arg::new("deny-ffi") .long("deny-ffi") .num_args(0..) - .use_value_delimiter(true) + .action(ArgAction::Append) .require_equals(true) .value_name("PATH") .help("(Unstable) Deny loading dynamic libraries. Optionally specify denied directories or files") - .value_parser(value_parser!(String)) .value_hint(ValueHint::AnyPath) .hide(true), ) @@ -3409,7 +3403,7 @@ fn reload_arg() -> Arg { Arg::new("reload") .short('r') .num_args(0..) - .use_value_delimiter(true) + .action(ArgAction::Append) .require_equals(true) .long("reload") .value_name("CACHE_BLOCKLIST") @@ -3421,7 +3415,6 @@ fn reload_arg() -> Arg { npm:chalk Reload specific npm module", )) .value_hint(ValueHint::FilePath) - .value_parser(reload_arg_validate) .help_heading(DEPENDENCY_MANAGEMENT_HEADING) } @@ -3538,8 +3531,7 @@ fn hmr_arg(takes_files: bool) -> Arg { arg .value_name("FILES") .num_args(0..) - .value_parser(value_parser!(String)) - .use_value_delimiter(true) + .action(ArgAction::Append) .require_equals(true) .help( cstr!( @@ -3565,8 +3557,7 @@ fn watch_arg(takes_files: bool) -> Arg { arg .value_name("FILES") .num_args(0..) - .value_parser(value_parser!(String)) - .use_value_delimiter(true) + .action(ArgAction::Append) .require_equals(true) .help( cstr!( @@ -3605,8 +3596,7 @@ fn watch_exclude_arg() -> Arg { .help("Exclude provided files/patterns from watch mode") .value_name("FILES") .num_args(0..) - .value_parser(value_parser!(String)) - .use_value_delimiter(true) + .action(ArgAction::Append) .require_equals(true) .value_hint(ValueHint::AnyPath) .help_heading(FILE_WATCHING_HEADING) @@ -3778,7 +3768,7 @@ fn allow_scripts_arg() -> Arg { Arg::new("allow-scripts") .long("allow-scripts") .num_args(0..) - .use_value_delimiter(true) + .action(ArgAction::Append) .require_equals(true) .value_name("PACKAGE") .value_parser(parse_packages_allowed_scripts) @@ -3881,7 +3871,7 @@ fn allow_scripts_arg_parse(flags: &mut Flags, matches: &mut ArgMatches) { if parts.len() == 0 { flags.allow_scripts = PackagesAllowedScripts::All; } else { - flags.allow_scripts = PackagesAllowedScripts::Some(parts.collect()); + flags.allow_scripts = PackagesAllowedScripts::Some(parts.flat_map(escape_and_split_commas).collect()); } } @@ -3905,10 +3895,10 @@ fn remove_parse(flags: &mut Flags, matches: &mut ArgMatches) { }); } -fn bench_parse(flags: &mut Flags, matches: &mut ArgMatches) { +fn bench_parse(flags: &mut Flags, matches: &mut ArgMatches) -> clap::error::Result<()> { flags.type_check_mode = TypeCheckMode::Local; - runtime_args_parse(flags, matches, true, false); + runtime_args_parse(flags, matches, true, false)?; // NOTE: `deno bench` always uses `--no-prompt`, tests shouldn't ever do // interactive prompts, unless done by user code @@ -3917,7 +3907,7 @@ fn bench_parse(flags: &mut Flags, matches: &mut ArgMatches) { let json = matches.get_flag("json"); let ignore = match matches.remove_many::("ignore") { - Some(f) => f.collect(), + Some(f) => f.flat_map(escape_and_split_commas).collect(), None => vec![], }; @@ -3944,39 +3934,43 @@ fn bench_parse(flags: &mut Flags, matches: &mut ArgMatches) { no_run, watch: watch_arg_parse(matches), }); + + Ok(()) } fn bundle_parse(flags: &mut Flags, _matches: &mut ArgMatches) { flags.subcommand = DenoSubcommand::Bundle; } -fn cache_parse(flags: &mut Flags, matches: &mut ArgMatches) { - compile_args_parse(flags, matches); +fn cache_parse(flags: &mut Flags, matches: &mut ArgMatches) -> clap::error::Result<()> { + compile_args_parse(flags, matches)?; unstable_args_parse(flags, matches, UnstableArgsConfig::ResolutionOnly); frozen_lockfile_arg_parse(flags, matches); allow_scripts_arg_parse(flags, matches); let files = matches.remove_many::("file").unwrap().collect(); flags.subcommand = DenoSubcommand::Cache(CacheFlags { files }); + Ok(()) } -fn check_parse(flags: &mut Flags, matches: &mut ArgMatches) { +fn check_parse(flags: &mut Flags, matches: &mut ArgMatches) -> clap::error::Result<()> { flags.type_check_mode = TypeCheckMode::Local; - compile_args_without_check_parse(flags, matches); + compile_args_without_check_parse(flags, matches)?; unstable_args_parse(flags, matches, UnstableArgsConfig::ResolutionAndRuntime); let files = matches.remove_many::("file").unwrap().collect(); if matches.get_flag("all") || matches.get_flag("remote") { flags.type_check_mode = TypeCheckMode::All; } flags.subcommand = DenoSubcommand::Check(CheckFlags { files }); + Ok(()) } fn clean_parse(flags: &mut Flags, _matches: &mut ArgMatches) { flags.subcommand = DenoSubcommand::Clean; } -fn compile_parse(flags: &mut Flags, matches: &mut ArgMatches) { +fn compile_parse(flags: &mut Flags, matches: &mut ArgMatches) -> clap::error::Result<()> { flags.type_check_mode = TypeCheckMode::Local; - runtime_args_parse(flags, matches, true, false); + runtime_args_parse(flags, matches, true, false)?; let mut script = matches.remove_many::("script_arg").unwrap(); let source_file = script.next().unwrap(); @@ -4000,6 +3994,8 @@ fn compile_parse(flags: &mut Flags, matches: &mut ArgMatches) { icon, include, }); + + Ok(()) } fn completions_parse( @@ -4037,7 +4033,7 @@ fn coverage_parse(flags: &mut Flags, matches: &mut ArgMatches) { None => vec!["coverage".to_string()], // default }; let ignore = match matches.remove_many::("ignore") { - Some(f) => f.collect(), + Some(f) => f.flat_map(escape_and_split_commas).collect(), None => vec![], }; let include = match matches.remove_many::("include") { @@ -4070,10 +4066,10 @@ fn coverage_parse(flags: &mut Flags, matches: &mut ArgMatches) { }); } -fn doc_parse(flags: &mut Flags, matches: &mut ArgMatches) { +fn doc_parse(flags: &mut Flags, matches: &mut ArgMatches) -> clap::error::Result<()> { unstable_args_parse(flags, matches, UnstableArgsConfig::ResolutionOnly); import_map_arg_parse(flags, matches); - reload_arg_parse(flags, matches); + reload_arg_parse(flags, matches)?; lock_arg_parse(flags, matches); no_lock_arg_parse(flags, matches); no_npm_arg_parse(flags, matches); @@ -4132,10 +4128,11 @@ fn doc_parse(flags: &mut Flags, matches: &mut ArgMatches) { filter, private, }); + Ok(()) } -fn eval_parse(flags: &mut Flags, matches: &mut ArgMatches) { - runtime_args_parse(flags, matches, false, true); +fn eval_parse(flags: &mut Flags, matches: &mut ArgMatches) -> clap::error::Result<()> { + runtime_args_parse(flags, matches, false, true)?; unstable_args_parse(flags, matches, UnstableArgsConfig::ResolutionAndRuntime); flags.allow_all(); @@ -4147,6 +4144,7 @@ fn eval_parse(flags: &mut Flags, matches: &mut ArgMatches) { flags.argv.extend(code_args); flags.subcommand = DenoSubcommand::Eval(EvalFlags { print, code }); + Ok(()) } fn fmt_parse(flags: &mut Flags, matches: &mut ArgMatches) { @@ -4158,7 +4156,7 @@ fn fmt_parse(flags: &mut Flags, matches: &mut ArgMatches) { None => vec![], }; let ignore = match matches.remove_many::("ignore") { - Some(f) => f.collect(), + Some(f) => f.flat_map(escape_and_split_commas).collect(), None => vec![], }; @@ -4198,9 +4196,9 @@ fn init_parse(flags: &mut Flags, matches: &mut ArgMatches) { }); } -fn info_parse(flags: &mut Flags, matches: &mut ArgMatches) { +fn info_parse(flags: &mut Flags, matches: &mut ArgMatches) -> clap::error::Result<()> { unstable_args_parse(flags, matches, UnstableArgsConfig::ResolutionOnly); - reload_arg_parse(flags, matches); + reload_arg_parse(flags, matches)?; config_args_parse(flags, matches); import_map_arg_parse(flags, matches); location_arg_parse(flags, matches); @@ -4215,10 +4213,12 @@ fn info_parse(flags: &mut Flags, matches: &mut ArgMatches) { file: matches.remove_one::("file"), json, }); + + Ok(()) } -fn install_parse(flags: &mut Flags, matches: &mut ArgMatches) { - runtime_args_parse(flags, matches, true, true); +fn install_parse(flags: &mut Flags, matches: &mut ArgMatches) -> clap::error::Result<()> { + runtime_args_parse(flags, matches, true, true)?; let global = matches.get_flag("global"); if global { @@ -4252,6 +4252,7 @@ fn install_parse(flags: &mut Flags, matches: &mut ArgMatches) { kind: InstallKind::Local(local_flags), }) } + Ok(()) } fn json_reference_parse( @@ -4363,7 +4364,7 @@ fn lint_parse(flags: &mut Flags, matches: &mut ArgMatches) { None => vec![], }; let ignore = match matches.remove_many::("ignore") { - Some(f) => f.collect(), + Some(f) => f.flat_map(escape_and_split_commas).collect(), None => vec![], }; let fix = matches.get_flag("fix"); @@ -4401,13 +4402,13 @@ fn lint_parse(flags: &mut Flags, matches: &mut ArgMatches) { }); } -fn repl_parse(flags: &mut Flags, matches: &mut ArgMatches) { - runtime_args_parse(flags, matches, true, true); +fn repl_parse(flags: &mut Flags, matches: &mut ArgMatches) -> clap::error::Result<()> { + runtime_args_parse(flags, matches, true, true)?; unsafely_ignore_certificate_errors_parse(flags, matches); let eval_files = matches .remove_many::("eval-file") - .map(|values| values.collect()); + .map(|values| values.flat_map(escape_and_split_commas).collect::>()); handle_repl_flags( flags, @@ -4417,6 +4418,7 @@ fn repl_parse(flags: &mut Flags, matches: &mut ArgMatches) { is_default_command: false, }, ); + Ok(()) } fn run_parse( @@ -4425,7 +4427,7 @@ fn run_parse( app: Command, bare: bool, ) -> clap::error::Result<()> { - runtime_args_parse(flags, matches, true, true); + runtime_args_parse(flags, matches, true, true)?; ext_arg_parse(flags, matches); flags.code_cache_enabled = !matches.get_flag("no-code-cache"); @@ -4467,7 +4469,7 @@ fn serve_parse( let worker_count = parallel_arg_parse(matches).map(|v| v.get()); - runtime_args_parse(flags, matches, true, true); + runtime_args_parse(flags, matches, true, true)?; // If the user didn't pass --allow-net, add this port to the network // allowlist. If the host is 0.0.0.0, we add :{port} and allow the same network perms // as if it was passed to --allow-net directly. @@ -4553,15 +4555,15 @@ fn parallel_arg_parse(matches: &mut ArgMatches) -> Option { } } -fn test_parse(flags: &mut Flags, matches: &mut ArgMatches) { +fn test_parse(flags: &mut Flags, matches: &mut ArgMatches) -> clap::error::Result<()> { flags.type_check_mode = TypeCheckMode::Local; - runtime_args_parse(flags, matches, true, true); + runtime_args_parse(flags, matches, true, true)?; // NOTE: `deno test` always uses `--no-prompt`, tests shouldn't ever do // interactive prompts, unless done by user code flags.permissions.no_prompt = true; let ignore = match matches.remove_many::("ignore") { - Some(f) => f.collect(), + Some(f) => f.flat_map(escape_and_split_commas).collect(), None => vec![], }; @@ -4643,6 +4645,7 @@ fn test_parse(flags: &mut Flags, matches: &mut ArgMatches) { junit_path, hide_stacktraces, }); + Ok(()) } fn types_parse(flags: &mut Flags, _matches: &mut ArgMatches) { @@ -4692,42 +4695,79 @@ fn publish_parse(flags: &mut Flags, matches: &mut ArgMatches) { }); } -fn compile_args_parse(flags: &mut Flags, matches: &mut ArgMatches) { - compile_args_without_check_parse(flags, matches); +fn compile_args_parse(flags: &mut Flags, matches: &mut ArgMatches) -> clap::error::Result<()> { + compile_args_without_check_parse(flags, matches)?; no_check_arg_parse(flags, matches); check_arg_parse(flags, matches); + Ok(()) } fn compile_args_without_check_parse( flags: &mut Flags, matches: &mut ArgMatches, -) { +) -> clap::error::Result<()> { import_map_arg_parse(flags, matches); no_remote_arg_parse(flags, matches); no_npm_arg_parse(flags, matches); node_modules_and_vendor_dir_arg_parse(flags, matches); config_args_parse(flags, matches); - reload_arg_parse(flags, matches); + reload_arg_parse(flags, matches)?; lock_args_parse(flags, matches); ca_file_arg_parse(flags, matches); unsafely_ignore_certificate_errors_parse(flags, matches); + Ok(()) +} + +fn escape_and_split_commas(s: String) -> Vec { + let mut result = vec![]; + let mut current = String::new(); + let mut chars = s.chars(); + + while let Some(c) = chars.next() { + if c == ',' { + if let Some(next) = chars.next() { + if next == ',' { + current.push(','); + } else { + result.push(current.clone()); + current.clear(); + current.push(next); + } + } else { + result.push(current.clone()); + current.clear(); + } + } else { + current.push(c); + } + } + + result.push(current); + + dbg!(&result); + + result } -fn permission_args_parse(flags: &mut Flags, matches: &mut ArgMatches) { +fn permission_args_parse(flags: &mut Flags, matches: &mut ArgMatches) -> clap::error::Result<()> { if let Some(read_wl) = matches.remove_many::("allow-read") { - flags.permissions.allow_read = Some(read_wl.collect()); + let read_wl = read_wl.flat_map(escape_and_split_commas).collect::>(); + flags.permissions.allow_read = Some(read_wl); } if let Some(read_wl) = matches.remove_many::("deny-read") { - flags.permissions.deny_read = Some(read_wl.collect()); + let read_wl = read_wl.flat_map(escape_and_split_commas).collect::>(); + flags.permissions.deny_read = Some(read_wl); } if let Some(write_wl) = matches.remove_many::("allow-write") { - flags.permissions.allow_write = Some(write_wl.collect()); + let write_wl = write_wl.flat_map(escape_and_split_commas).collect::>(); + flags.permissions.allow_write = Some(write_wl); } if let Some(write_wl) = matches.remove_many::("deny-write") { - flags.permissions.deny_write = Some(write_wl.collect()); + let write_wl = write_wl.flat_map(escape_and_split_commas).collect::>(); + flags.permissions.deny_write = Some(write_wl); } if let Some(net_wl) = matches.remove_many::("allow-net") { @@ -4771,12 +4811,14 @@ fn permission_args_parse(flags: &mut Flags, matches: &mut ArgMatches) { } if let Some(ffi_wl) = matches.remove_many::("allow-ffi") { - flags.permissions.allow_ffi = Some(ffi_wl.collect()); + let ffi_wl = ffi_wl.flat_map(escape_and_split_commas).collect::>(); + flags.permissions.allow_ffi = Some(ffi_wl); debug!("ffi allowlist: {:#?}", &flags.permissions.allow_ffi); } if let Some(ffi_wl) = matches.remove_many::("deny-ffi") { - flags.permissions.deny_ffi = Some(ffi_wl.collect()); + let ffi_wl = ffi_wl.flat_map(escape_and_split_commas).collect::>(); + flags.permissions.deny_ffi = Some(ffi_wl); debug!("ffi denylist: {:#?}", &flags.permissions.deny_ffi); } @@ -4791,6 +4833,8 @@ fn permission_args_parse(flags: &mut Flags, matches: &mut ArgMatches) { if matches.get_flag("no-prompt") { flags.permissions.no_prompt = true; } + + Ok(()) } fn unsafely_ignore_certificate_errors_parse( @@ -4810,13 +4854,13 @@ fn runtime_args_parse( matches: &mut ArgMatches, include_perms: bool, include_inspector: bool, -) { +) -> clap::error::Result<()> { unstable_args_parse(flags, matches, UnstableArgsConfig::ResolutionAndRuntime); - compile_args_parse(flags, matches); + compile_args_parse(flags, matches)?; cached_only_arg_parse(flags, matches); frozen_lockfile_arg_parse(flags, matches); if include_perms { - permission_args_parse(flags, matches); + permission_args_parse(flags, matches)?; } if include_inspector { inspect_arg_parse(flags, matches); @@ -4827,6 +4871,7 @@ fn runtime_args_parse( enable_testing_features_arg_parse(flags, matches); env_file_arg_parse(flags, matches); strace_ops_parse(flags, matches); + Ok(()) } fn inspect_arg_parse(flags: &mut Flags, matches: &mut ArgMatches) { @@ -4843,9 +4888,9 @@ fn env_file_arg_parse(flags: &mut Flags, matches: &mut ArgMatches) { flags.env_file = matches.remove_one::("env-file"); } -fn reload_arg_parse(flags: &mut Flags, matches: &mut ArgMatches) { +fn reload_arg_parse(flags: &mut Flags, matches: &mut ArgMatches) -> clap::error::Result<()> { if let Some(cache_bl) = matches.remove_many::("reload") { - let raw_cache_blocklist: Vec = cache_bl.collect(); + let raw_cache_blocklist: Vec = cache_bl.flat_map(escape_and_split_commas).map(reload_arg_validate).collect::, _>>()?; if raw_cache_blocklist.is_empty() { flags.reload = true; } else { @@ -4854,6 +4899,8 @@ fn reload_arg_parse(flags: &mut Flags, matches: &mut ArgMatches) { flags.reload = false; } } + + Ok(()) } fn ca_file_arg_parse(flags: &mut Flags, matches: &mut ArgMatches) { @@ -4985,13 +5032,13 @@ fn node_modules_and_vendor_dir_arg_parse( flags.vendor = matches.remove_one::("vendor"); } -fn reload_arg_validate(urlstr: &str) -> Result { +fn reload_arg_validate(urlstr: String) -> Result { if urlstr.is_empty() { - return Err(String::from("Missing url. Check for extra commas.")); + return Err(std::io::Error::new(std::io::ErrorKind::Other, String::from("Missing url. Check for extra commas.")).into()) } - match Url::from_str(urlstr) { - Ok(_) => Ok(urlstr.to_string()), - Err(e) => Err(e.to_string()), + match Url::from_str(&urlstr) { + Ok(_) => Ok(urlstr), + Err(e) => Err(std::io::Error::new(std::io::ErrorKind::Other, e.to_string()).into()), } } @@ -5002,7 +5049,7 @@ fn watch_arg_parse(matches: &mut ArgMatches) -> Option { no_clear_screen: matches.get_flag("no-clear-screen"), exclude: matches .remove_many::("watch-exclude") - .map(|f| f.collect::>()) + .map(|f| f.flat_map(escape_and_split_commas).collect::>()) .unwrap_or_default(), }) } else { @@ -5015,12 +5062,12 @@ fn watch_arg_parse_with_paths( ) -> Option { if let Some(paths) = matches.remove_many::("watch") { return Some(WatchFlagsWithPaths { - paths: paths.collect(), + paths: paths.flat_map(escape_and_split_commas).collect(), hmr: false, no_clear_screen: matches.get_flag("no-clear-screen"), exclude: matches .remove_many::("watch-exclude") - .map(|f| f.collect::>()) + .map(|f| f.flat_map(escape_and_split_commas).collect::>()) .unwrap_or_default(), }); } @@ -5028,12 +5075,12 @@ fn watch_arg_parse_with_paths( if matches.try_contains_id("hmr").is_ok() { return matches.remove_many::("hmr").map(|paths| { WatchFlagsWithPaths { - paths: paths.collect(), + paths: paths.flat_map(escape_and_split_commas).collect(), hmr: true, no_clear_screen: matches.get_flag("no-clear-screen"), exclude: matches .remove_many::("watch-exclude") - .map(|f| f.collect::>()) + .map(|f| f.flat_map(escape_and_split_commas).collect::>()) .unwrap_or_default(), } }); @@ -7423,14 +7470,6 @@ mod tests { "script.ts" ]); assert!(r.is_err(), "Should reject a trailing comma"); - - let r = flags_from_vec(svec![ - "deno", - "run", - "--reload=http://deno.land/a,,http://deno.land/b", - "script.ts" - ]); - assert!(r.is_err(), "Should reject adjacent commas"); } #[test] @@ -10296,4 +10335,16 @@ mod tests { assert_eq!(long_flag, subcommand, "{} subcommand", command.get_name()); } } + + #[test] + fn escape_and_split_commas_test() { + assert_eq!(escape_and_split_commas("foo".to_string()), ["foo"]); + assert_eq!(escape_and_split_commas("foo,".to_string()), ["foo", ""]); + assert_eq!(escape_and_split_commas("foo,,".to_string()), ["foo,"]); + assert_eq!(escape_and_split_commas("foo,,,".to_string()), ["foo,", ""]); + assert_eq!(escape_and_split_commas("foo,bar".to_string()), ["foo", "bar"]); + assert_eq!(escape_and_split_commas("foo,,bar".to_string()), ["foo,bar"]); + assert_eq!(escape_and_split_commas("foo,,,bar".to_string()), ["foo,", "bar"]); + assert_eq!(escape_and_split_commas(",".to_string()), ["", ""]); + } } From e35c1b4fa60d45028eab26430b9247f44c32b807 Mon Sep 17 00:00:00 2001 From: crowlkats Date: Thu, 5 Sep 2024 10:42:03 +0200 Subject: [PATCH 2/3] fmt --- cli/args/flags.rs | 327 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 246 insertions(+), 81 deletions(-) diff --git a/cli/args/flags.rs b/cli/args/flags.rs index c907dd1c217f8f..615b80e1705a30 100644 --- a/cli/args/flags.rs +++ b/cli/args/flags.rs @@ -1314,16 +1314,16 @@ pub fn flags_from_vec(args: Vec) -> clap::error::Result { "clean" => clean_parse(&mut flags, &mut m), "compile" => compile_parse(&mut flags, &mut m)?, "completions" => completions_parse(&mut flags, &mut m, app), - "coverage" => coverage_parse(&mut flags, &mut m), + "coverage" => coverage_parse(&mut flags, &mut m)?, "doc" => doc_parse(&mut flags, &mut m)?, "eval" => eval_parse(&mut flags, &mut m)?, - "fmt" => fmt_parse(&mut flags, &mut m), + "fmt" => fmt_parse(&mut flags, &mut m)?, "init" => init_parse(&mut flags, &mut m), "info" => info_parse(&mut flags, &mut m)?, "install" => install_parse(&mut flags, &mut m)?, "json_reference" => json_reference_parse(&mut flags, &mut m, app), "jupyter" => jupyter_parse(&mut flags, &mut m), - "lint" => lint_parse(&mut flags, &mut m), + "lint" => lint_parse(&mut flags, &mut m)?, "lsp" => lsp_parse(&mut flags, &mut m), "repl" => repl_parse(&mut flags, &mut m)?, "run" => run_parse(&mut flags, &mut m, app, false)?, @@ -4056,15 +4056,23 @@ fn unstable_args(cfg: UnstableArgsConfig) -> impl IntoIterator { UnstableArgsIter { idx: 0, cfg } } -fn allow_scripts_arg_parse(flags: &mut Flags, matches: &mut ArgMatches) { +fn allow_scripts_arg_parse( + flags: &mut Flags, + matches: &mut ArgMatches, +) -> clap::error::Result<()> { let Some(parts) = matches.remove_many::("allow-scripts") else { - return; + return Ok(()); }; if parts.len() == 0 { flags.allow_scripts = PackagesAllowedScripts::All; } else { - flags.allow_scripts = PackagesAllowedScripts::Some(parts.flat_map(escape_and_split_commas).collect()); + flags.allow_scripts = PackagesAllowedScripts::Some( + parts + .flat_map(flat_escape_split_commas) + .collect::>()?, + ); } + Ok(()) } fn add_parse(flags: &mut Flags, matches: &mut ArgMatches) { @@ -4087,7 +4095,10 @@ fn remove_parse(flags: &mut Flags, matches: &mut ArgMatches) { }); } -fn bench_parse(flags: &mut Flags, matches: &mut ArgMatches) -> clap::error::Result<()> { +fn bench_parse( + flags: &mut Flags, + matches: &mut ArgMatches, +) -> clap::error::Result<()> { flags.type_check_mode = TypeCheckMode::Local; runtime_args_parse(flags, matches, true, false)?; @@ -4099,7 +4110,9 @@ fn bench_parse(flags: &mut Flags, matches: &mut ArgMatches) -> clap::error::Resu let json = matches.get_flag("json"); let ignore = match matches.remove_many::("ignore") { - Some(f) => f.flat_map(escape_and_split_commas).collect(), + Some(f) => f + .flat_map(flat_escape_split_commas) + .collect::>()?, None => vec![], }; @@ -4124,7 +4137,7 @@ fn bench_parse(flags: &mut Flags, matches: &mut ArgMatches) -> clap::error::Resu filter, json, no_run, - watch: watch_arg_parse(matches), + watch: watch_arg_parse(matches)?, }); Ok(()) @@ -4134,17 +4147,23 @@ fn bundle_parse(flags: &mut Flags, _matches: &mut ArgMatches) { flags.subcommand = DenoSubcommand::Bundle; } -fn cache_parse(flags: &mut Flags, matches: &mut ArgMatches) -> clap::error::Result<()> { +fn cache_parse( + flags: &mut Flags, + matches: &mut ArgMatches, +) -> clap::error::Result<()> { compile_args_parse(flags, matches)?; unstable_args_parse(flags, matches, UnstableArgsConfig::ResolutionOnly); frozen_lockfile_arg_parse(flags, matches); - allow_scripts_arg_parse(flags, matches); + allow_scripts_arg_parse(flags, matches)?; let files = matches.remove_many::("file").unwrap().collect(); flags.subcommand = DenoSubcommand::Cache(CacheFlags { files }); Ok(()) } -fn check_parse(flags: &mut Flags, matches: &mut ArgMatches) -> clap::error::Result<()> { +fn check_parse( + flags: &mut Flags, + matches: &mut ArgMatches, +) -> clap::error::Result<()> { flags.type_check_mode = TypeCheckMode::Local; compile_args_without_check_parse(flags, matches)?; unstable_args_parse(flags, matches, UnstableArgsConfig::ResolutionAndRuntime); @@ -4160,7 +4179,10 @@ fn clean_parse(flags: &mut Flags, _matches: &mut ArgMatches) { flags.subcommand = DenoSubcommand::Clean; } -fn compile_parse(flags: &mut Flags, matches: &mut ArgMatches) -> clap::error::Result<()> { +fn compile_parse( + flags: &mut Flags, + matches: &mut ArgMatches, +) -> clap::error::Result<()> { flags.type_check_mode = TypeCheckMode::Local; runtime_args_parse(flags, matches, true, false)?; @@ -4219,13 +4241,18 @@ fn completions_parse( }); } -fn coverage_parse(flags: &mut Flags, matches: &mut ArgMatches) { +fn coverage_parse( + flags: &mut Flags, + matches: &mut ArgMatches, +) -> clap::error::Result<()> { let files = match matches.remove_many::("files") { Some(f) => f.collect(), None => vec!["coverage".to_string()], // default }; let ignore = match matches.remove_many::("ignore") { - Some(f) => f.flat_map(escape_and_split_commas).collect(), + Some(f) => f + .flat_map(flat_escape_split_commas) + .collect::, _>>()?, None => vec![], }; let include = match matches.remove_many::("include") { @@ -4256,9 +4283,13 @@ fn coverage_parse(flags: &mut Flags, matches: &mut ArgMatches) { exclude, r#type, }); + Ok(()) } -fn doc_parse(flags: &mut Flags, matches: &mut ArgMatches) -> clap::error::Result<()> { +fn doc_parse( + flags: &mut Flags, + matches: &mut ArgMatches, +) -> clap::error::Result<()> { unstable_args_parse(flags, matches, UnstableArgsConfig::ResolutionOnly); import_map_arg_parse(flags, matches); reload_arg_parse(flags, matches)?; @@ -4323,7 +4354,10 @@ fn doc_parse(flags: &mut Flags, matches: &mut ArgMatches) -> clap::error::Result Ok(()) } -fn eval_parse(flags: &mut Flags, matches: &mut ArgMatches) -> clap::error::Result<()> { +fn eval_parse( + flags: &mut Flags, + matches: &mut ArgMatches, +) -> clap::error::Result<()> { runtime_args_parse(flags, matches, false, true)?; unstable_args_parse(flags, matches, UnstableArgsConfig::ResolutionAndRuntime); flags.allow_all(); @@ -4339,7 +4373,10 @@ fn eval_parse(flags: &mut Flags, matches: &mut ArgMatches) -> clap::error::Resul Ok(()) } -fn fmt_parse(flags: &mut Flags, matches: &mut ArgMatches) { +fn fmt_parse( + flags: &mut Flags, + matches: &mut ArgMatches, +) -> clap::error::Result<()> { config_args_parse(flags, matches); ext_arg_parse(flags, matches); @@ -4348,7 +4385,9 @@ fn fmt_parse(flags: &mut Flags, matches: &mut ArgMatches) { None => vec![], }; let ignore = match matches.remove_many::("ignore") { - Some(f) => f.flat_map(escape_and_split_commas).collect(), + Some(f) => f + .flat_map(flat_escape_split_commas) + .collect::, _>>()?, None => vec![], }; @@ -4372,12 +4411,13 @@ fn fmt_parse(flags: &mut Flags, matches: &mut ArgMatches) { single_quote, prose_wrap, no_semicolons, - watch: watch_arg_parse(matches), + watch: watch_arg_parse(matches)?, unstable_css, unstable_html, unstable_component, unstable_yaml, }); + Ok(()) } fn init_parse(flags: &mut Flags, matches: &mut ArgMatches) { @@ -4388,7 +4428,10 @@ fn init_parse(flags: &mut Flags, matches: &mut ArgMatches) { }); } -fn info_parse(flags: &mut Flags, matches: &mut ArgMatches) -> clap::error::Result<()> { +fn info_parse( + flags: &mut Flags, + matches: &mut ArgMatches, +) -> clap::error::Result<()> { unstable_args_parse(flags, matches, UnstableArgsConfig::ResolutionOnly); reload_arg_parse(flags, matches)?; config_args_parse(flags, matches); @@ -4409,7 +4452,10 @@ fn info_parse(flags: &mut Flags, matches: &mut ArgMatches) -> clap::error::Resul Ok(()) } -fn install_parse(flags: &mut Flags, matches: &mut ArgMatches) -> clap::error::Result<()> { +fn install_parse( + flags: &mut Flags, + matches: &mut ArgMatches, +) -> clap::error::Result<()> { runtime_args_parse(flags, matches, true, true)?; let global = matches.get_flag("global"); @@ -4434,11 +4480,11 @@ fn install_parse(flags: &mut Flags, matches: &mut ArgMatches) -> clap::error::Re force, }), }); - return; + return Ok(()); } // allow scripts only applies to local install - allow_scripts_arg_parse(flags, matches); + allow_scripts_arg_parse(flags, matches)?; if matches.get_flag("entrypoint") { let entrypoints = matches.remove_many::("cmd").unwrap_or_default(); flags.subcommand = DenoSubcommand::Install(InstallFlags { @@ -4564,7 +4610,10 @@ fn lsp_parse(flags: &mut Flags, _matches: &mut ArgMatches) { flags.subcommand = DenoSubcommand::Lsp; } -fn lint_parse(flags: &mut Flags, matches: &mut ArgMatches) { +fn lint_parse( + flags: &mut Flags, + matches: &mut ArgMatches, +) -> clap::error::Result<()> { unstable_args_parse(flags, matches, UnstableArgsConfig::ResolutionOnly); config_args_parse(flags, matches); @@ -4573,7 +4622,9 @@ fn lint_parse(flags: &mut Flags, matches: &mut ArgMatches) { None => vec![], }; let ignore = match matches.remove_many::("ignore") { - Some(f) => f.flat_map(escape_and_split_commas).collect(), + Some(f) => f + .flat_map(flat_escape_split_commas) + .collect::, _>>()?, None => vec![], }; let fix = matches.get_flag("fix"); @@ -4606,18 +4657,27 @@ fn lint_parse(flags: &mut Flags, matches: &mut ArgMatches) { maybe_rules_exclude, json, compact, - watch: watch_arg_parse(matches), + watch: watch_arg_parse(matches)?, ext, }); + Ok(()) } -fn repl_parse(flags: &mut Flags, matches: &mut ArgMatches) -> clap::error::Result<()> { +fn repl_parse( + flags: &mut Flags, + matches: &mut ArgMatches, +) -> clap::error::Result<()> { runtime_args_parse(flags, matches, true, true)?; unsafely_ignore_certificate_errors_parse(flags, matches); let eval_files = matches .remove_many::("eval-file") - .map(|values| values.flat_map(escape_and_split_commas).collect::>()); + .map(|values| { + values + .flat_map(flat_escape_split_commas) + .collect::, _>>() + }) + .transpose()?; handle_repl_flags( flags, @@ -4646,7 +4706,7 @@ fn run_parse( flags.argv.extend(script_arg); flags.subcommand = DenoSubcommand::Run(RunFlags { script, - watch: watch_arg_parse_with_paths(matches), + watch: watch_arg_parse_with_paths(matches)?, bare, }); } else if bare { @@ -4714,7 +4774,7 @@ fn serve_parse( flags.subcommand = DenoSubcommand::Serve(ServeFlags { script, - watch: watch_arg_parse_with_paths(matches), + watch: watch_arg_parse_with_paths(matches)?, port, host, worker_count, @@ -4764,7 +4824,10 @@ fn parallel_arg_parse(matches: &mut ArgMatches) -> Option { } } -fn test_parse(flags: &mut Flags, matches: &mut ArgMatches) -> clap::error::Result<()> { +fn test_parse( + flags: &mut Flags, + matches: &mut ArgMatches, +) -> clap::error::Result<()> { flags.type_check_mode = TypeCheckMode::Local; runtime_args_parse(flags, matches, true, true)?; // NOTE: `deno test` always uses `--no-prompt`, tests shouldn't ever do @@ -4772,7 +4835,9 @@ fn test_parse(flags: &mut Flags, matches: &mut ArgMatches) -> clap::error::Resul flags.permissions.no_prompt = true; let ignore = match matches.remove_many::("ignore") { - Some(f) => f.flat_map(escape_and_split_commas).collect(), + Some(f) => f + .flat_map(flat_escape_split_commas) + .collect::>()?, None => vec![], }; @@ -4849,7 +4914,7 @@ fn test_parse(flags: &mut Flags, matches: &mut ArgMatches) -> clap::error::Resul permit_no_files, concurrent_jobs, trace_leaks, - watch: watch_arg_parse_with_paths(matches), + watch: watch_arg_parse_with_paths(matches)?, reporter, junit_path, hide_stacktraces, @@ -4904,7 +4969,10 @@ fn publish_parse(flags: &mut Flags, matches: &mut ArgMatches) { }); } -fn compile_args_parse(flags: &mut Flags, matches: &mut ArgMatches) -> clap::error::Result<()> { +fn compile_args_parse( + flags: &mut Flags, + matches: &mut ArgMatches, +) -> clap::error::Result<()> { compile_args_without_check_parse(flags, matches)?; no_check_arg_parse(flags, matches); check_arg_parse(flags, matches); @@ -4927,7 +4995,7 @@ fn compile_args_without_check_parse( Ok(()) } -fn escape_and_split_commas(s: String) -> Vec { +fn escape_and_split_commas(s: String) -> Result, clap::Error> { let mut result = vec![]; let mut current = String::new(); let mut chars = s.chars(); @@ -4938,44 +5006,87 @@ fn escape_and_split_commas(s: String) -> Vec { if next == ',' { current.push(','); } else { + if current.is_empty() { + return Err( + std::io::Error::new( + std::io::ErrorKind::Other, + String::from("Empty values are not allowed"), + ) + .into(), + ); + } + result.push(current.clone()); current.clear(); current.push(next); } } else { - result.push(current.clone()); - current.clear(); + return Err( + std::io::Error::new( + std::io::ErrorKind::Other, + String::from("Empty values are not allowed"), + ) + .into(), + ); } } else { current.push(c); } } + dbg!(¤t); + + if current.is_empty() { + return Err( + std::io::Error::new( + std::io::ErrorKind::Other, + String::from("Empty values are not allowed"), + ) + .into(), + ); + } + result.push(current); - dbg!(&result); + Ok(result) +} - result +fn flat_escape_split_commas(str: String) -> Vec> { + match escape_and_split_commas(str) { + Ok(vec) => vec.into_iter().map(Ok).collect::>(), + Err(e) => vec![Err(e)], + } } -fn permission_args_parse(flags: &mut Flags, matches: &mut ArgMatches) -> clap::error::Result<()> { +fn permission_args_parse( + flags: &mut Flags, + matches: &mut ArgMatches, +) -> clap::error::Result<()> { if let Some(read_wl) = matches.remove_many::("allow-read") { - let read_wl = read_wl.flat_map(escape_and_split_commas).collect::>(); + let read_wl = read_wl + .flat_map(flat_escape_split_commas) + .collect::, _>>()?; flags.permissions.allow_read = Some(read_wl); } if let Some(read_wl) = matches.remove_many::("deny-read") { - let read_wl = read_wl.flat_map(escape_and_split_commas).collect::>(); + let read_wl = read_wl + .flat_map(flat_escape_split_commas) + .collect::, _>>()?; flags.permissions.deny_read = Some(read_wl); } if let Some(write_wl) = matches.remove_many::("allow-write") { - let write_wl = write_wl.flat_map(escape_and_split_commas).collect::>(); + let write_wl = write_wl + .flat_map(flat_escape_split_commas) + .collect::, _>>()?; flags.permissions.allow_write = Some(write_wl); } if let Some(write_wl) = matches.remove_many::("deny-write") { - let write_wl = write_wl.flat_map(escape_and_split_commas).collect::>(); + let write_wl = write_wl + .flat_map(flat_escape_split_commas) + .collect::, _>>()?; flags.permissions.deny_write = Some(write_wl); } @@ -5020,13 +5131,17 @@ fn permission_args_parse(flags: &mut Flags, matches: &mut ArgMatches) -> clap::e } if let Some(ffi_wl) = matches.remove_many::("allow-ffi") { - let ffi_wl = ffi_wl.flat_map(escape_and_split_commas).collect::>(); + let ffi_wl = ffi_wl + .flat_map(flat_escape_split_commas) + .collect::, _>>()?; flags.permissions.allow_ffi = Some(ffi_wl); debug!("ffi allowlist: {:#?}", &flags.permissions.allow_ffi); } if let Some(ffi_wl) = matches.remove_many::("deny-ffi") { - let ffi_wl = ffi_wl.flat_map(escape_and_split_commas).collect::>(); + let ffi_wl = ffi_wl + .flat_map(flat_escape_split_commas) + .collect::, _>>()?; flags.permissions.deny_ffi = Some(ffi_wl); debug!("ffi denylist: {:#?}", &flags.permissions.deny_ffi); } @@ -5097,9 +5212,15 @@ fn env_file_arg_parse(flags: &mut Flags, matches: &mut ArgMatches) { flags.env_file = matches.remove_one::("env-file"); } -fn reload_arg_parse(flags: &mut Flags, matches: &mut ArgMatches) -> clap::error::Result<()> { +fn reload_arg_parse( + flags: &mut Flags, + matches: &mut ArgMatches, +) -> clap::error::Result<()> { if let Some(cache_bl) = matches.remove_many::("reload") { - let raw_cache_blocklist: Vec = cache_bl.flat_map(escape_and_split_commas).map(reload_arg_validate).collect::, _>>()?; + let raw_cache_blocklist: Vec = cache_bl + .flat_map(flat_escape_split_commas) + .map(|s| s.and_then(reload_arg_validate)) + .collect::, _>>()?; if raw_cache_blocklist.is_empty() { flags.reload = true; } else { @@ -5243,59 +5364,88 @@ fn node_modules_and_vendor_dir_arg_parse( fn reload_arg_validate(urlstr: String) -> Result { if urlstr.is_empty() { - return Err(std::io::Error::new(std::io::ErrorKind::Other, String::from("Missing url. Check for extra commas.")).into()) + return Err( + std::io::Error::new( + std::io::ErrorKind::Other, + String::from("Missing url. Check for extra commas."), + ) + .into(), + ); } match Url::from_str(&urlstr) { Ok(_) => Ok(urlstr), - Err(e) => Err(std::io::Error::new(std::io::ErrorKind::Other, e.to_string()).into()), + Err(e) => { + Err(std::io::Error::new(std::io::ErrorKind::Other, e.to_string()).into()) + } } } -fn watch_arg_parse(matches: &mut ArgMatches) -> Option { +fn watch_arg_parse( + matches: &mut ArgMatches, +) -> clap::error::Result> { if matches.get_flag("watch") { - Some(WatchFlags { + Ok(Some(WatchFlags { hmr: false, no_clear_screen: matches.get_flag("no-clear-screen"), exclude: matches .remove_many::("watch-exclude") - .map(|f| f.flat_map(escape_and_split_commas).collect::>()) + .map(|f| { + f.flat_map(flat_escape_split_commas) + .collect::>() + }) + .transpose()? .unwrap_or_default(), - }) + })) } else { - None + Ok(None) } } fn watch_arg_parse_with_paths( matches: &mut ArgMatches, -) -> Option { +) -> clap::error::Result> { if let Some(paths) = matches.remove_many::("watch") { - return Some(WatchFlagsWithPaths { - paths: paths.flat_map(escape_and_split_commas).collect(), + return Ok(Some(WatchFlagsWithPaths { + paths: paths + .flat_map(flat_escape_split_commas) + .collect::, _>>()?, hmr: false, no_clear_screen: matches.get_flag("no-clear-screen"), exclude: matches .remove_many::("watch-exclude") - .map(|f| f.flat_map(escape_and_split_commas).collect::>()) + .map(|f| { + f.flat_map(flat_escape_split_commas) + .collect::, _>>() + }) + .transpose()? .unwrap_or_default(), - }); + })); } if matches.try_contains_id("hmr").is_ok() { - return matches.remove_many::("hmr").map(|paths| { - WatchFlagsWithPaths { - paths: paths.flat_map(escape_and_split_commas).collect(), - hmr: true, - no_clear_screen: matches.get_flag("no-clear-screen"), - exclude: matches - .remove_many::("watch-exclude") - .map(|f| f.flat_map(escape_and_split_commas).collect::>()) - .unwrap_or_default(), - } - }); + return matches + .remove_many::("hmr") + .map(|paths| { + Ok(WatchFlagsWithPaths { + paths: paths + .flat_map(flat_escape_split_commas) + .collect::, _>>()?, + hmr: true, + no_clear_screen: matches.get_flag("no-clear-screen"), + exclude: matches + .remove_many::("watch-exclude") + .map(|f| { + f.flat_map(flat_escape_split_commas) + .collect::, _>>() + }) + .transpose()? + .unwrap_or_default(), + }) + }) + .transpose(); } - None + Ok(None) } fn unstable_args_parse( @@ -10558,13 +10708,28 @@ mod tests { #[test] fn escape_and_split_commas_test() { - assert_eq!(escape_and_split_commas("foo".to_string()), ["foo"]); - assert_eq!(escape_and_split_commas("foo,".to_string()), ["foo", ""]); - assert_eq!(escape_and_split_commas("foo,,".to_string()), ["foo,"]); - assert_eq!(escape_and_split_commas("foo,,,".to_string()), ["foo,", ""]); - assert_eq!(escape_and_split_commas("foo,bar".to_string()), ["foo", "bar"]); - assert_eq!(escape_and_split_commas("foo,,bar".to_string()), ["foo,bar"]); - assert_eq!(escape_and_split_commas("foo,,,bar".to_string()), ["foo,", "bar"]); - assert_eq!(escape_and_split_commas(",".to_string()), ["", ""]); + assert_eq!(escape_and_split_commas("foo".to_string()).unwrap(), ["foo"]); + assert!(escape_and_split_commas("foo,".to_string()).is_err()); + assert_eq!( + escape_and_split_commas("foo,,".to_string()).unwrap(), + ["foo,"] + ); + assert!(escape_and_split_commas("foo,,,".to_string()).is_err()); + assert_eq!( + escape_and_split_commas("foo,,,,".to_string()).unwrap(), + ["foo,,"] + ); + assert_eq!( + escape_and_split_commas("foo,bar".to_string()).unwrap(), + ["foo", "bar"] + ); + assert_eq!( + escape_and_split_commas("foo,,bar".to_string()).unwrap(), + ["foo,bar"] + ); + assert_eq!( + escape_and_split_commas("foo,,,bar".to_string()).unwrap(), + ["foo,", "bar"] + ); } } From 3837a0c63c23c423edfa4ff33f645a903be6bb6f Mon Sep 17 00:00:00 2001 From: crowlkats Date: Thu, 5 Sep 2024 12:22:46 +0200 Subject: [PATCH 3/3] fix --- cli/args/flags.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/cli/args/flags.rs b/cli/args/flags.rs index 615b80e1705a30..d159c1df847e4a 100644 --- a/cli/args/flags.rs +++ b/cli/args/flags.rs @@ -5034,8 +5034,6 @@ fn escape_and_split_commas(s: String) -> Result, clap::Error> { } } - dbg!(¤t); - if current.is_empty() { return Err( std::io::Error::new(