refactor: Move ALL from RuleCodePrefix to RuleSelector

This commit is contained in:
Martin Fischer 2023-01-24 11:50:41 +01:00 committed by Charlie Marsh
parent abc9810e2b
commit 28018442f6
4 changed files with 129 additions and 42 deletions

View File

@ -1188,13 +1188,13 @@
"RuleSelector": { "RuleSelector": {
"type": "string", "type": "string",
"enum": [ "enum": [
"ALL",
"A", "A",
"A0", "A0",
"A00", "A00",
"A001", "A001",
"A002", "A002",
"A003", "A003",
"ALL",
"ANN", "ANN",
"ANN0", "ANN0",
"ANN00", "ANN00",

View File

@ -5,8 +5,6 @@ use proc_macro2::Span;
use quote::quote; use quote::quote;
use syn::Ident; use syn::Ident;
const ALL: &str = "ALL";
/// A hash map from deprecated `RuleSelector` to latest /// A hash map from deprecated `RuleSelector` to latest
/// `RuleSelector`. /// `RuleSelector`.
pub static PREFIX_REDIRECTS: Lazy<HashMap<&'static str, &'static str>> = Lazy::new(|| { pub static PREFIX_REDIRECTS: Lazy<HashMap<&'static str, &'static str>> = Lazy::new(|| {
@ -116,7 +114,6 @@ pub fn expand<'a>(
all_codes.insert(code_str); all_codes.insert(code_str);
} }
prefix_to_codes.insert(ALL.to_string(), all_codes);
prefix_to_codes.insert("PL".to_string(), pl_codes); prefix_to_codes.insert("PL".to_string(), pl_codes);
// Add any prefix aliases (e.g., "U" to "UP"). // Add any prefix aliases (e.g., "U" to "UP").
@ -148,6 +145,7 @@ pub fn expand<'a>(
quote! { quote! {
#[derive( #[derive(
::strum_macros::EnumIter,
::strum_macros::EnumString, ::strum_macros::EnumString,
::strum_macros::AsRefStr, ::strum_macros::AsRefStr,
Debug, Debug,
@ -158,7 +156,6 @@ pub fn expand<'a>(
Clone, Clone,
::serde::Serialize, ::serde::Serialize,
::serde::Deserialize, ::serde::Deserialize,
::schemars::JsonSchema,
)] )]
pub enum #prefix_ident { pub enum #prefix_ident {
#(#prefix_variants,)* #(#prefix_variants,)*
@ -207,27 +204,21 @@ fn generate_impls<'a>(
let specificity_match_arms = prefix_to_codes.keys().map(|prefix_str| { let specificity_match_arms = prefix_to_codes.keys().map(|prefix_str| {
let prefix = Ident::new(prefix_str, Span::call_site()); let prefix = Ident::new(prefix_str, Span::call_site());
if prefix_str == ALL { let mut num_numeric = prefix_str.chars().filter(|char| char.is_numeric()).count();
quote! { if prefix_str != "PL" && prefix_str.starts_with("PL") {
#prefix_ident::#prefix => Specificity::All, num_numeric += 1;
} }
} else { let suffix_len = match num_numeric {
let mut num_numeric = prefix_str.chars().filter(|char| char.is_numeric()).count(); 0 => quote! { Specificity::Linter },
if prefix_str != "PL" && prefix_str.starts_with("PL") { 1 => quote! { Specificity::Code1Char },
num_numeric += 1; 2 => quote! { Specificity::Code2Chars },
} 3 => quote! { Specificity::Code3Chars },
let suffix_len = match num_numeric { 4 => quote! { Specificity::Code4Chars },
0 => quote! { Specificity::Linter }, 5 => quote! { Specificity::Code5Chars },
1 => quote! { Specificity::Code1Char }, _ => panic!("Invalid prefix: {prefix}"),
2 => quote! { Specificity::Code2Chars }, };
3 => quote! { Specificity::Code3Chars }, quote! {
4 => quote! { Specificity::Code4Chars }, #prefix_ident::#prefix => #suffix_len,
5 => quote! { Specificity::Code5Chars },
_ => panic!("Invalid prefix: {prefix}"),
};
quote! {
#prefix_ident::#prefix => #suffix_len,
}
} }
}); });

View File

@ -1,33 +1,120 @@
use std::str::FromStr; use std::str::FromStr;
use schemars::_serde_json::Value;
use schemars::schema::{InstanceType, Schema, SchemaObject};
use schemars::JsonSchema; use schemars::JsonSchema;
use serde::de::{self, Visitor};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use strum::IntoEnumIterator;
use crate::registry::{Rule, RuleCodePrefix}; use crate::registry::{Rule, RuleCodePrefix, RuleIter};
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct RuleSelector(RuleCodePrefix); pub enum RuleSelector {
/// All rules
All,
Prefix(RuleCodePrefix),
}
impl FromStr for RuleSelector { impl FromStr for RuleSelector {
type Err = strum::ParseError; type Err = ParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> { fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(Self(RuleCodePrefix::from_str(s)?)) if s == "ALL" {
Ok(Self::All)
} else {
Ok(Self::Prefix(
RuleCodePrefix::from_str(s).map_err(|_| ParseError::Unknown(s.to_string()))?,
))
}
}
}
#[derive(Debug, thiserror::Error)]
pub enum ParseError {
#[error("Unknown rule selector `{0}`")]
// TODO(martin): tell the user how to discover rule codes via the CLI once such a command is
// implemented (but that should of course be done only in ruff_cli and not here)
Unknown(String),
}
impl Serialize for RuleSelector {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
match self {
RuleSelector::All => serializer.serialize_str("ALL"),
RuleSelector::Prefix(prefix) => prefix.serialize(serializer),
}
}
}
impl<'de> Deserialize<'de> for RuleSelector {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
// We are not simply doing:
// let s: &str = Deserialize::deserialize(deserializer)?;
// FromStr::from_str(s).map_err(de::Error::custom)
// here because the toml crate apparently doesn't support that
// (as of toml v0.6.0 running `cargo test` failed with the above two lines)
deserializer.deserialize_str(SelectorVisitor)
}
}
struct SelectorVisitor;
impl Visitor<'_> for SelectorVisitor {
type Value = RuleSelector;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str(
"expected a string code identifying a linter or specific rule, or a partial rule code \
or ALL to refer to all rules",
)
}
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: de::Error,
{
FromStr::from_str(v).map_err(de::Error::custom)
} }
} }
impl From<RuleCodePrefix> for RuleSelector { impl From<RuleCodePrefix> for RuleSelector {
fn from(prefix: RuleCodePrefix) -> Self { fn from(prefix: RuleCodePrefix) -> Self {
Self(prefix) Self::Prefix(prefix)
} }
} }
impl IntoIterator for &RuleSelector { impl IntoIterator for &RuleSelector {
type IntoIter = ::std::vec::IntoIter<Self::Item>; type IntoIter = RuleSelectorIter;
type Item = Rule; type Item = Rule;
fn into_iter(self) -> Self::IntoIter { fn into_iter(self) -> Self::IntoIter {
self.0.into_iter() match self {
RuleSelector::All => RuleSelectorIter::All(Rule::iter()),
RuleSelector::Prefix(prefix) => RuleSelectorIter::Prefix(prefix.into_iter()),
}
}
}
pub enum RuleSelectorIter {
All(RuleIter),
Prefix(std::vec::IntoIter<Rule>),
}
impl Iterator for RuleSelectorIter {
type Item = Rule;
fn next(&mut self) -> Option<Self::Item> {
match self {
RuleSelectorIter::All(iter) => iter.next(),
RuleSelectorIter::Prefix(iter) => iter.next(),
}
} }
} }
@ -37,7 +124,7 @@ impl IntoIterator for &RuleSelector {
// RuleSelector` (see https://github.com/rust-lang/rust/issues/67792). // RuleSelector` (see https://github.com/rust-lang/rust/issues/67792).
// TODO(martin): Remove once RuleSelector is an enum with Linter & Rule variants // TODO(martin): Remove once RuleSelector is an enum with Linter & Rule variants
pub(crate) const fn prefix_to_selector(prefix: RuleCodePrefix) -> RuleSelector { pub(crate) const fn prefix_to_selector(prefix: RuleCodePrefix) -> RuleSelector {
RuleSelector(prefix) RuleSelector::Prefix(prefix)
} }
impl JsonSchema for RuleSelector { impl JsonSchema for RuleSelector {
@ -45,14 +132,26 @@ impl JsonSchema for RuleSelector {
"RuleSelector".to_string() "RuleSelector".to_string()
} }
fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema { fn json_schema(_gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
<RuleCodePrefix as JsonSchema>::json_schema(gen) Schema::Object(SchemaObject {
instance_type: Some(InstanceType::String.into()),
enum_values: Some(
std::iter::once("ALL".to_string())
.chain(RuleCodePrefix::iter().map(|s| s.as_ref().to_string()))
.map(Value::String)
.collect(),
),
..SchemaObject::default()
})
} }
} }
impl RuleSelector { impl RuleSelector {
pub(crate) fn specificity(&self) -> Specificity { pub(crate) fn specificity(&self) -> Specificity {
self.0.specificity() match self {
RuleSelector::All => Specificity::All,
RuleSelector::Prefix(prefix) => prefix.specificity(),
}
} }
} }

View File

@ -239,10 +239,7 @@ impl From<&Configuration> for RuleTable {
let mut rules = RuleTable::empty(); let mut rules = RuleTable::empty();
let fixable = resolve_codes([RuleCodeSpec { let fixable = resolve_codes([RuleCodeSpec {
select: config select: config.fixable.as_deref().unwrap_or(&[RuleSelector::All]),
.fixable
.as_deref()
.unwrap_or(&[crate::registry::RuleCodePrefix::ALL.into()]),
ignore: config.unfixable.as_deref().unwrap_or_default(), ignore: config.unfixable.as_deref().unwrap_or_default(),
}]); }]);