|
20 | 20 | use std::ffi::OsStr;
|
21 | 21 | use std::path::{Path, PathBuf};
|
22 | 22 | use std::process::Command;
|
| 23 | +use std::str::FromStr; |
23 | 24 | use std::{fmt, fs, io};
|
24 | 25 |
|
25 | 26 | const MIN_PY_REV: (u32, u32) = (3, 9);
|
@@ -61,25 +62,38 @@ fn check_impl(
|
61 | 62 |
|
62 | 63 | // Split comma-separated args up
|
63 | 64 | let lint_args = match extra_checks {
|
64 |
| - Some(s) => s.strip_prefix("--extra-checks=").unwrap().split(',').collect(), |
| 65 | + Some(s) => s |
| 66 | + .strip_prefix("--extra-checks=") |
| 67 | + .unwrap() |
| 68 | + .split(',') |
| 69 | + .map(|s| { |
| 70 | + if s == "spellcheck:fix" { |
| 71 | + eprintln!("warning: `spellcheck:fix` is no longer valid, use `--extra=check=spellcheck --bless`"); |
| 72 | + } |
| 73 | + (ExtraCheckArg::from_str(s), s) |
| 74 | + }) |
| 75 | + .filter_map(|(res, src)| match res { |
| 76 | + Ok(x) => Some(x), |
| 77 | + Err(err) => { |
| 78 | + eprintln!("warning: bad extra check argument {src:?}: {err:?}"); |
| 79 | + None |
| 80 | + } |
| 81 | + }) |
| 82 | + .collect(), |
65 | 83 | None => vec![],
|
66 | 84 | };
|
67 | 85 |
|
68 |
| - if lint_args.contains(&"spellcheck:fix") { |
69 |
| - return Err(Error::Generic( |
70 |
| - "`spellcheck:fix` is no longer valid, use `--extra=check=spellcheck --bless`" |
71 |
| - .to_string(), |
72 |
| - )); |
| 86 | + macro_rules! extra_check { |
| 87 | + ($lang:ident, $kind:ident) => { |
| 88 | + lint_args.iter().any(|arg| arg.matches(ExtraCheckLang::$lang, ExtraCheckKind::$kind)) |
| 89 | + }; |
73 | 90 | }
|
74 | 91 |
|
75 |
| - let python_all = lint_args.contains(&"py"); |
76 |
| - let python_lint = lint_args.contains(&"py:lint") || python_all; |
77 |
| - let python_fmt = lint_args.contains(&"py:fmt") || python_all; |
78 |
| - let shell_all = lint_args.contains(&"shell"); |
79 |
| - let shell_lint = lint_args.contains(&"shell:lint") || shell_all; |
80 |
| - let cpp_all = lint_args.contains(&"cpp"); |
81 |
| - let cpp_fmt = lint_args.contains(&"cpp:fmt") || cpp_all; |
82 |
| - let spellcheck = lint_args.contains(&"spellcheck"); |
| 92 | + let python_lint = extra_check!(Py, Lint); |
| 93 | + let python_fmt = extra_check!(Py, Fmt); |
| 94 | + let shell_lint = extra_check!(Shell, Lint); |
| 95 | + let cpp_fmt = extra_check!(Cpp, Fmt); |
| 96 | + let spellcheck = extra_check!(Spellcheck, None); |
83 | 97 |
|
84 | 98 | let mut py_path = None;
|
85 | 99 |
|
@@ -638,3 +652,86 @@ impl From<io::Error> for Error {
|
638 | 652 | Self::Io(value)
|
639 | 653 | }
|
640 | 654 | }
|
| 655 | + |
| 656 | +#[derive(Debug)] |
| 657 | +enum ExtraCheckParseError { |
| 658 | + #[allow(dead_code, reason = "shown through Debug")] |
| 659 | + UnknownKind(String), |
| 660 | + #[allow(dead_code)] |
| 661 | + UnknownLang(String), |
| 662 | + /// Too many `:` |
| 663 | + TooManyParts, |
| 664 | + /// Tried to parse the empty string |
| 665 | + Empty, |
| 666 | +} |
| 667 | + |
| 668 | +struct ExtraCheckArg { |
| 669 | + lang: ExtraCheckLang, |
| 670 | + /// None = run all extra checks for the given lang |
| 671 | + kind: Option<ExtraCheckKind>, |
| 672 | +} |
| 673 | + |
| 674 | +impl ExtraCheckArg { |
| 675 | + fn matches(&self, lang: ExtraCheckLang, kind: ExtraCheckKind) -> bool { |
| 676 | + self.lang == lang && self.kind.map(|k| k == kind).unwrap_or(true) |
| 677 | + } |
| 678 | +} |
| 679 | + |
| 680 | +impl FromStr for ExtraCheckArg { |
| 681 | + type Err = ExtraCheckParseError; |
| 682 | + |
| 683 | + fn from_str(s: &str) -> Result<Self, Self::Err> { |
| 684 | + let mut parts = s.split(':'); |
| 685 | + let Some(first) = parts.next() else { |
| 686 | + return Err(ExtraCheckParseError::Empty); |
| 687 | + }; |
| 688 | + let second = parts.next(); |
| 689 | + if parts.next().is_some() { |
| 690 | + return Err(ExtraCheckParseError::TooManyParts); |
| 691 | + } |
| 692 | + Ok(Self { lang: first.parse()?, kind: second.map(|s| s.parse()).transpose()? }) |
| 693 | + } |
| 694 | +} |
| 695 | + |
| 696 | +#[derive(PartialEq, Copy, Clone)] |
| 697 | +enum ExtraCheckLang { |
| 698 | + Py, |
| 699 | + Shell, |
| 700 | + Cpp, |
| 701 | + Spellcheck, |
| 702 | +} |
| 703 | + |
| 704 | +impl FromStr for ExtraCheckLang { |
| 705 | + type Err = ExtraCheckParseError; |
| 706 | + |
| 707 | + fn from_str(s: &str) -> Result<Self, Self::Err> { |
| 708 | + Ok(match s { |
| 709 | + "py" => Self::Py, |
| 710 | + "shell" => Self::Shell, |
| 711 | + "cpp" => Self::Cpp, |
| 712 | + "spellcheck" => Self::Spellcheck, |
| 713 | + _ => return Err(ExtraCheckParseError::UnknownLang(s.to_string())), |
| 714 | + }) |
| 715 | + } |
| 716 | +} |
| 717 | + |
| 718 | +#[derive(PartialEq, Copy, Clone)] |
| 719 | +enum ExtraCheckKind { |
| 720 | + Lint, |
| 721 | + Fmt, |
| 722 | + /// Never parsed, but used as a placeholder for |
| 723 | + /// langs that never have a specific kind. |
| 724 | + None, |
| 725 | +} |
| 726 | + |
| 727 | +impl FromStr for ExtraCheckKind { |
| 728 | + type Err = ExtraCheckParseError; |
| 729 | + |
| 730 | + fn from_str(s: &str) -> Result<Self, Self::Err> { |
| 731 | + Ok(match s { |
| 732 | + "lint" => Self::Lint, |
| 733 | + "fmt" => Self::Fmt, |
| 734 | + _ => return Err(ExtraCheckParseError::UnknownKind(s.to_string())), |
| 735 | + }) |
| 736 | + } |
| 737 | +} |
0 commit comments