|
1 | | -use clap::{ArgGroup, Parser, Subcommand}; |
| 1 | +use clap::{ArgAction, ArgGroup, Parser, Subcommand}; |
2 | 2 | use gobby_code::{commands, config, freshness, output, setup}; |
3 | 3 |
|
4 | 4 | #[derive(Parser)] |
@@ -221,6 +221,45 @@ enum Command { |
221 | 221 | /// Unsupported: use -m/--max-count for indexed grep caps |
222 | 222 | #[arg(long = "limit", hide = true, value_parser = reject_grep_limit)] |
223 | 223 | _unsupported_limit: Option<String>, |
| 224 | + /// Unsupported: use raw rg for filesystem grep |
| 225 | + #[arg(short = 'l', long = "files-with-matches", hide = true, action = ArgAction::SetTrue)] |
| 226 | + unsupported_files_with_matches: bool, |
| 227 | + /// Unsupported: use raw rg for filesystem grep |
| 228 | + #[arg(short = 'L', long = "files-without-match", hide = true, action = ArgAction::SetTrue)] |
| 229 | + unsupported_files_without_match: bool, |
| 230 | + /// Unsupported: use raw rg for filesystem grep |
| 231 | + #[arg(short = 'c', long = "count", hide = true, action = ArgAction::SetTrue)] |
| 232 | + unsupported_count: bool, |
| 233 | + /// Unsupported: use raw rg for filesystem grep |
| 234 | + #[arg(short = 'o', long = "only-matching", hide = true, action = ArgAction::SetTrue)] |
| 235 | + unsupported_only_matching: bool, |
| 236 | + /// Unsupported: use raw rg for filesystem grep |
| 237 | + #[arg(short = 'v', long = "invert-match", hide = true, action = ArgAction::SetTrue)] |
| 238 | + unsupported_invert_match: bool, |
| 239 | + /// Unsupported: use raw rg for filesystem grep |
| 240 | + #[arg(short = 'w', long = "word-regexp", hide = true, action = ArgAction::SetTrue)] |
| 241 | + unsupported_word_regexp: bool, |
| 242 | + /// Unsupported: use raw rg for filesystem grep |
| 243 | + #[arg(short = 'e', long = "regexp", hide = true)] |
| 244 | + unsupported_regexp: Option<String>, |
| 245 | + /// Unsupported: use raw rg for filesystem grep |
| 246 | + #[arg(short = 'r', long = "recursive", hide = true, action = ArgAction::SetTrue)] |
| 247 | + unsupported_recursive: bool, |
| 248 | + /// Unsupported: use raw rg for filesystem grep |
| 249 | + #[arg(short = 't', long = "type", hide = true)] |
| 250 | + unsupported_type: Option<String>, |
| 251 | + /// Unsupported: use raw rg for filesystem grep |
| 252 | + #[arg(short = 'T', long = "type-not", hide = true)] |
| 253 | + unsupported_type_not: Option<String>, |
| 254 | + /// Unsupported: use raw rg for filesystem grep |
| 255 | + #[arg(short = 'P', long = "pcre2", hide = true, action = ArgAction::SetTrue)] |
| 256 | + unsupported_pcre2: bool, |
| 257 | + /// Unsupported: use raw rg for filesystem grep |
| 258 | + #[arg(short = 'U', long = "multiline", hide = true, action = ArgAction::SetTrue)] |
| 259 | + unsupported_multiline: bool, |
| 260 | + /// Unsupported: use --format json for structured indexed grep output |
| 261 | + #[arg(long = "json", hide = true, action = ArgAction::SetTrue)] |
| 262 | + unsupported_json: bool, |
224 | 263 | }, |
225 | 264 |
|
226 | 265 | // ── Symbol Retrieval (works in all modes) ──────────────────────── |
@@ -395,6 +434,55 @@ fn effective_format(explicit_format: Option<output::Format>, command: &Command) |
395 | 434 | }) |
396 | 435 | } |
397 | 436 |
|
| 437 | +fn reject_unsupported_grep_flags(command: &Command) -> anyhow::Result<()> { |
| 438 | + let Command::Grep { |
| 439 | + unsupported_files_with_matches, |
| 440 | + unsupported_files_without_match, |
| 441 | + unsupported_count, |
| 442 | + unsupported_only_matching, |
| 443 | + unsupported_invert_match, |
| 444 | + unsupported_word_regexp, |
| 445 | + unsupported_regexp, |
| 446 | + unsupported_recursive, |
| 447 | + unsupported_type, |
| 448 | + unsupported_type_not, |
| 449 | + unsupported_pcre2, |
| 450 | + unsupported_multiline, |
| 451 | + unsupported_json, |
| 452 | + .. |
| 453 | + } = command |
| 454 | + else { |
| 455 | + return Ok(()); |
| 456 | + }; |
| 457 | + |
| 458 | + let flag = [ |
| 459 | + (*unsupported_files_with_matches).then_some("--files-with-matches"), |
| 460 | + (*unsupported_files_without_match).then_some("--files-without-match"), |
| 461 | + (*unsupported_count).then_some("--count"), |
| 462 | + (*unsupported_only_matching).then_some("--only-matching"), |
| 463 | + (*unsupported_invert_match).then_some("--invert-match"), |
| 464 | + (*unsupported_word_regexp).then_some("--word-regexp"), |
| 465 | + unsupported_regexp.as_ref().map(|_| "--regexp"), |
| 466 | + (*unsupported_recursive).then_some("--recursive"), |
| 467 | + unsupported_type.as_ref().map(|_| "--type"), |
| 468 | + unsupported_type_not.as_ref().map(|_| "--type-not"), |
| 469 | + (*unsupported_pcre2).then_some("--pcre2"), |
| 470 | + (*unsupported_multiline).then_some("--multiline"), |
| 471 | + (*unsupported_json).then_some("--json"), |
| 472 | + ] |
| 473 | + .into_iter() |
| 474 | + .flatten() |
| 475 | + .next(); |
| 476 | + |
| 477 | + if let Some(flag) = flag { |
| 478 | + anyhow::bail!( |
| 479 | + "gcode grep is indexed search; unsupported grep/rg flag `{flag}`. Use raw `rg` for filesystem grep." |
| 480 | + ); |
| 481 | + } |
| 482 | + |
| 483 | + Ok(()) |
| 484 | +} |
| 485 | + |
398 | 486 | fn dispatch_early_command<F>( |
399 | 487 | cli: &Cli, |
400 | 488 | format: output::Format, |
@@ -491,6 +579,7 @@ fn main() -> std::process::ExitCode { |
491 | 579 | fn run() -> anyhow::Result<()> { |
492 | 580 | let cli = Cli::parse(); |
493 | 581 | let format = effective_format(cli.format, &cli.command); |
| 582 | + reject_unsupported_grep_flags(&cli.command)?; |
494 | 583 |
|
495 | 584 | // Commands that must run before Context::resolve() (work on uninitialized projects) |
496 | 585 | if dispatch_early_command(&cli, format, commands::setup::run)? { |
@@ -706,6 +795,19 @@ fn run() -> anyhow::Result<()> { |
706 | 795 | max_count, |
707 | 796 | line_number: _, |
708 | 797 | _unsupported_limit: _, |
| 798 | + unsupported_files_with_matches: _, |
| 799 | + unsupported_files_without_match: _, |
| 800 | + unsupported_count: _, |
| 801 | + unsupported_only_matching: _, |
| 802 | + unsupported_invert_match: _, |
| 803 | + unsupported_word_regexp: _, |
| 804 | + unsupported_regexp: _, |
| 805 | + unsupported_recursive: _, |
| 806 | + unsupported_type: _, |
| 807 | + unsupported_type_not: _, |
| 808 | + unsupported_pcre2: _, |
| 809 | + unsupported_multiline: _, |
| 810 | + unsupported_json: _, |
709 | 811 | } => { |
710 | 812 | ensure_project_fresh(&ctx, cli.no_freshness)?; |
711 | 813 | commands::grep::run( |
@@ -1502,14 +1604,34 @@ mod tests { |
1502 | 1604 |
|
1503 | 1605 | #[test] |
1504 | 1606 | fn parse_grep_unsupported_flag_fails_before_context_resolution() { |
1505 | | - let err = match Cli::try_parse_from(["gcode", "grep", "needle", "--files-with-matches"]) { |
1506 | | - Ok(_) => panic!("unsupported grep flag should fail"), |
1507 | | - Err(err) => err, |
1508 | | - }; |
| 1607 | + let cli = Cli::try_parse_from(["gcode", "grep", "needle", "--files-with-matches"]) |
| 1608 | + .expect("unsupported grep flag parses for contract rejection"); |
| 1609 | + let err = reject_unsupported_grep_flags(&cli.command) |
| 1610 | + .expect_err("unsupported grep flag should fail before context resolution"); |
| 1611 | + |
| 1612 | + assert!( |
| 1613 | + err.to_string().contains("gcode grep is indexed search"), |
| 1614 | + "unexpected error: {err}" |
| 1615 | + ); |
| 1616 | + assert!( |
| 1617 | + err.to_string().contains("--files-with-matches"), |
| 1618 | + "unexpected error: {err}" |
| 1619 | + ); |
| 1620 | + assert!( |
| 1621 | + err.to_string().contains("raw `rg`"), |
| 1622 | + "unexpected error: {err}" |
| 1623 | + ); |
| 1624 | + } |
| 1625 | + |
| 1626 | + #[test] |
| 1627 | + fn parse_grep_unsupported_flag_after_path_fails_with_indexed_search_message() { |
| 1628 | + let cli = Cli::try_parse_from(["gcode", "grep", "needle", "src", "--files-with-matches"]) |
| 1629 | + .expect("unsupported grep flag after path parses for contract rejection"); |
| 1630 | + let err = reject_unsupported_grep_flags(&cli.command) |
| 1631 | + .expect_err("unsupported grep flag after path should fail"); |
1509 | 1632 |
|
1510 | 1633 | assert!( |
1511 | | - err.to_string() |
1512 | | - .contains("unexpected argument '--files-with-matches'"), |
| 1634 | + err.to_string().contains("gcode grep is indexed search"), |
1513 | 1635 | "unexpected error: {err}" |
1514 | 1636 | ); |
1515 | 1637 | } |
|
0 commit comments