use std::fmt;
use std::str::FromStr;
use strum::IntoEnumIterator;
use strum_macros::EnumIter;
#[derive(Debug, EnumIter, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum Linter {
Air,
Era,
Fast,
Ytt,
Ann,
Async,
S,
Ble,
Fbt,
B,
A,
Com,
C4,
Cpy,
Dtz,
T10,
Dj,
Em,
Exe,
Fix,
Fa,
Int,
Isc,
Icn,
Log,
G,
Inp,
Pie,
T20,
Pyi,
Pt,
Q,
Rse,
Ret,
Slf,
Sim,
Slot,
Tid,
Td,
Tc,
Arg,
Pth,
Fly,
I,
C90,
Npy,
Pd,
N,
Perf,
E,
W,
Doc,
D,
F,
Pgh,
Plc,
Ple,
Plr,
Plw,
Up,
Furb,
Ruf,
Try,
}
impl fmt::Display for Linter {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let caps = format!("{:?}", self).to_uppercase();
write!(f, "{}", caps)
}
}
#[derive(Debug, PartialEq, Eq)]
pub struct ParseLinterError;
impl FromStr for Linter {
type Err = ParseLinterError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut linter_vector: Vec<Linter> = Linter::iter().collect();
linter_vector.sort_by_key(|item| std::cmp::Reverse(format!("{}", item).len()));
for linter in linter_vector {
let name = format!("{}", linter);
if s.starts_with(&name) {
return Ok(linter);
}
}
Err(ParseLinterError)
}
}
impl Linter {
pub fn name(&self) -> &str {
match *self {
Linter::Air => "airflow",
Linter::Era => "eradicate",
Linter::Fast => "fastapi",
Linter::Ytt => "flake8-2020",
Linter::Ann => "flake8-annotations",
Linter::Async => "flake8-async",
Linter::S => "flake8-bandit",
Linter::Ble => "flake8-blind-except",
Linter::Fbt => "flake8-boolean-trap",
Linter::B => "flake8-bugbear",
Linter::A => "flake8-builtins",
Linter::Com => "flake8-commas",
Linter::C4 => "flake8-comprehensions",
Linter::Cpy => "flake8-copyright",
Linter::Dtz => "flake8-datetimez",
Linter::T10 => "flake8-debugger",
Linter::Dj => "flake8-django",
Linter::Em => "flake8-errmsg",
Linter::Exe => "flake8-executable",
Linter::Fix => "flake8-fixme",
Linter::Fa => "flake8-future-annotations",
Linter::Int => "flake8-gettext",
Linter::Isc => "flake8-implicit-str-concat",
Linter::Icn => "flake8-import-conventions",
Linter::Log => "flake8-logging",
Linter::G => "flake8-logging-format",
Linter::Inp => "flake8-no-pep420",
Linter::Pie => "flake8-pie",
Linter::T20 => "flake8-print",
Linter::Pyi => "flake8-pyi",
Linter::Pt => "flake8-pytest-style",
Linter::Q => "flake8-quotes",
Linter::Rse => "flake8-raise",
Linter::Ret => "flake8-return",
Linter::Slf => "flake8-self",
Linter::Sim => "flake8-simplify",
Linter::Slot => "flake8-slots",
Linter::Tid => "flake8-tidy-imports",
Linter::Td => "flake8-todos",
Linter::Tc => "flake8-type-checking",
Linter::Arg => "flake8-unused-arguments",
Linter::Pth => "flake8-use-pathlib",
Linter::Fly => "flynt",
Linter::I => "isort",
Linter::C90 => "mccabe",
Linter::Npy => "numpy-specific-rules",
Linter::Pd => "pandas-vet",
Linter::N => "pep8-naming",
Linter::Perf => "perflint",
Linter::E => "error",
Linter::W => "warning",
Linter::Doc => "pydoclint",
Linter::D => "pydocstyle",
Linter::F => "pyflakes",
Linter::Pgh => "pygrep-hooks",
Linter::Plc => "convention",
Linter::Ple => "error",
Linter::Plr => "refactor",
Linter::Plw => "warning",
Linter::Up => "pyupgrade",
Linter::Furb => "refurb",
Linter::Ruf => "ruff-specific-rules",
Linter::Try => "tryceratops",
}
}
pub fn generate_rule_url(&self) -> String {
format!(
"https://docs.astral.sh/ruff/rules/#{}-{}",
self.name(),
self
)
.to_lowercase()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn t100() {
let str_code = "T100";
let code = Linter::from_str(str_code).unwrap();
assert_eq!(code.to_string(), "T10");
}
#[test]
fn t203() {
let str_code = "T203";
let code = Linter::from_str(str_code).unwrap();
assert_eq!(code.to_string(), "T20");
}
#[test]
fn q100() {
let str_code = "Q100";
let code = Linter::from_str(str_code).unwrap();
assert_eq!(code.to_string(), "Q");
}
#[test]
fn plw0108() {
let str_code = "PLW0108";
let code = Linter::from_str(str_code).unwrap();
assert_eq!(code.to_string(), "PLW");
}
#[test]
#[should_panic = "ParseLinterError"]
fn invalid() {
Linter::from_str("invalid").unwrap();
}
#[test]
#[should_panic = "ParseLinterError"]
fn invalid_numeric() {
Linter::from_str("42").unwrap();
}
}