|
1 | 1 | use anyhow::Result; |
2 | | -use clap::{Parser, error::ErrorKind}; |
| 2 | +use clap::{CommandFactory, Parser, error::ErrorKind}; |
3 | 3 | use log::LevelFilter; |
4 | 4 | use std::{env, io::Write, path::Path}; |
5 | 5 |
|
@@ -134,11 +134,19 @@ fn parse_cli(raw_args: Vec<String>) -> Result<cli::Cli, clap::Error> { |
134 | 134 | } |
135 | 135 |
|
136 | 136 | fn should_default_to_build(err: &clap::Error, args: &[String]) -> bool { |
| 137 | + let first_non_global = first_non_global_arg(args); |
| 138 | + |
137 | 139 | match err.kind() { |
138 | | - ErrorKind::MissingSubcommand | ErrorKind::DisplayHelpOnMissingArgumentOrSubcommand => true, |
139 | | - ErrorKind::UnknownArgument | ErrorKind::InvalidSubcommand => { |
140 | | - args.iter().skip(1).any(|arg| !is_global_flag(arg)) |
| 140 | + ErrorKind::MissingSubcommand | ErrorKind::DisplayHelpOnMissingArgumentOrSubcommand => { |
| 141 | + match first_non_global { |
| 142 | + Some(arg) => !is_known_subcommand(arg), |
| 143 | + None => true, |
| 144 | + } |
141 | 145 | } |
| 146 | + ErrorKind::UnknownArgument | ErrorKind::InvalidSubcommand => match first_non_global { |
| 147 | + Some(arg) if is_known_subcommand(arg) => false, |
| 148 | + _ => true, |
| 149 | + }, |
142 | 150 | _ => false, |
143 | 151 | } |
144 | 152 | } |
@@ -174,6 +182,19 @@ fn is_global_flag(arg: &str) -> bool { |
174 | 182 | ) |
175 | 183 | } |
176 | 184 |
|
| 185 | +fn first_non_global_arg(args: &[String]) -> Option<&str> { |
| 186 | + args.iter() |
| 187 | + .skip(1) |
| 188 | + .find(|arg| !is_global_flag(arg)) |
| 189 | + .map(|s| s.as_str()) |
| 190 | +} |
| 191 | + |
| 192 | +fn is_known_subcommand(arg: &str) -> bool { |
| 193 | + cli::Cli::command().get_subcommands().any(|subcommand| { |
| 194 | + subcommand.get_name() == arg || subcommand.get_all_aliases().any(|alias| alias == arg) |
| 195 | + }) |
| 196 | +} |
| 197 | + |
177 | 198 | #[cfg(test)] |
178 | 199 | mod tests { |
179 | 200 | use super::*; |
@@ -209,6 +230,12 @@ mod tests { |
209 | 230 | assert!(matches!(cli.command, cli::Command::Watch(_))); |
210 | 231 | } |
211 | 232 |
|
| 233 | + #[test] |
| 234 | + fn invalid_option_for_subcommand_does_not_fallback() { |
| 235 | + let err = parse(&["rescript", "watch", "--no-timing"]).expect_err("expected watch parse failure"); |
| 236 | + assert_eq!(err.kind(), ErrorKind::UnknownArgument); |
| 237 | + } |
| 238 | + |
212 | 239 | #[test] |
213 | 240 | fn help_flag_does_not_default_to_build() { |
214 | 241 | let err = parse(&["rescript", "--help"]).expect_err("expected clap help error"); |
|
0 commit comments