refactor(linter): add schemars and serde traits to AllowWarnDeny and RuleCategories (#6119)

This commit is contained in:
DonIsaac 2024-09-29 15:03:43 +00:00
parent 40bd919849
commit 82b8f217ab
2 changed files with 151 additions and 10 deletions

View file

@ -1,10 +1,15 @@
use std::{convert::From, fmt};
use std::{
convert::From,
fmt::{self, Display},
};
use oxc_diagnostics::{OxcDiagnostic, Severity};
use schemars::{schema::SchemaObject, JsonSchema};
use serde::{de, Deserialize, Serialize};
use serde_json::{Number, Value};
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
#[derive(Debug, Clone, Copy, Eq, PartialEq, Serialize)]
#[serde(rename_all = "lowercase")]
pub enum AllowWarnDeny {
Allow, // Off
Warn, // Warn
@ -65,18 +70,89 @@ impl TryFrom<&Value> for AllowWarnDeny {
}
}
fn invalid_int_severity<D: Display>(value: D) -> OxcDiagnostic {
OxcDiagnostic::error(format!(
r#"Failed to parse rule severity, expected one of `0`, `1` or `2`, but got {value}"#
))
}
impl TryFrom<u64> for AllowWarnDeny {
type Error = OxcDiagnostic;
fn try_from(value: u64) -> Result<Self, Self::Error> {
match value {
0 => Ok(Self::Allow),
1 => Ok(Self::Warn),
2 => Ok(Self::Deny),
x => Err(invalid_int_severity(x)),
}
}
}
impl TryFrom<i64> for AllowWarnDeny {
type Error = OxcDiagnostic;
fn try_from(value: i64) -> Result<Self, Self::Error> {
if value < 0 {
return Err(invalid_int_severity("a negative number"));
}
#[allow(clippy::cast_sign_loss)]
Self::try_from(value as u64)
}
}
impl TryFrom<&Number> for AllowWarnDeny {
type Error = OxcDiagnostic;
fn try_from(value: &Number) -> Result<Self, Self::Error> {
match value.as_i64() {
Some(0) => Ok(Self::Allow),
Some(1) => Ok(Self::Warn),
Some(2) => Ok(Self::Deny),
_ => Err(OxcDiagnostic::error(format!(
r#"Failed to parse rule severity, expected one of `0`, `1` or `2`, but got {value:?}"#
))),
let value = value.as_i64().ok_or_else(|| invalid_int_severity(value))?;
Self::try_from(value)
}
}
impl<'de> Deserialize<'de> for AllowWarnDeny {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: de::Deserializer<'de>,
{
struct AllowWarnDenyVisitor;
impl<'de> de::Visitor<'de> for AllowWarnDenyVisitor {
type Value = AllowWarnDeny;
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
"an int between 0 and 2 or a string".fmt(f)
}
fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E>
where
E: de::Error,
{
Self::Value::try_from(v).map_err(de::Error::custom)
}
fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>
where
E: de::Error,
{
Self::Value::try_from(v).map_err(de::Error::custom)
}
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: de::Error,
{
Self::Value::try_from(v).map_err(de::Error::custom)
}
fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
where
E: de::Error,
{
Self::Value::try_from(v.as_str()).map_err(de::Error::custom)
}
}
deserializer.deserialize_any(AllowWarnDenyVisitor)
}
}
@ -128,3 +204,43 @@ impl From<AllowWarnDeny> for Severity {
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_serialize() {
let tests = [
(AllowWarnDeny::Allow, r#""allow""#),
(AllowWarnDeny::Warn, r#""warn""#),
(AllowWarnDeny::Deny, r#""deny""#),
];
for (input, expected) in tests {
assert_eq!(serde_json::to_string(&input).unwrap(), expected);
}
}
#[test]
fn test_deserialize() {
let tests = [
// allow
(r#""allow""#, AllowWarnDeny::Allow),
(r#""off""#, AllowWarnDeny::Allow),
("0", AllowWarnDeny::Allow),
// warn
(r#""warn""#, AllowWarnDeny::Warn),
("1", AllowWarnDeny::Warn),
// deny
(r#""error""#, AllowWarnDeny::Deny),
(r#""deny""#, AllowWarnDeny::Deny),
("2", AllowWarnDeny::Deny),
];
for (input, expected) in tests {
let msg = format!("input: {input}");
let actual: AllowWarnDeny = serde_json::from_str(input).expect(&msg);
assert_eq!(actual, expected);
}
}
}

View file

@ -6,6 +6,8 @@ use std::{
};
use oxc_semantic::SymbolId;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use crate::{
context::{ContextHost, LintContext},
@ -61,7 +63,8 @@ pub trait RuleMeta {
}
/// Rule categories defined by rust-clippy
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "lowercase")]
pub enum RuleCategory {
/// Code that is outright wrong or useless
Correctness,
@ -277,6 +280,7 @@ impl RuleWithSeverity {
mod test {
use markdown::{to_html_with_options, Options};
use super::RuleCategory;
use crate::rules::RULES;
#[test]
@ -295,4 +299,25 @@ mod test {
assert!(!html.is_empty());
}
}
#[test]
fn test_deserialize_rule_category() {
let tests = [
("correctness", RuleCategory::Correctness),
("suspicious", RuleCategory::Suspicious),
("restriction", RuleCategory::Restriction),
("perf", RuleCategory::Perf),
("pedantic", RuleCategory::Pedantic),
("style", RuleCategory::Style),
("nursery", RuleCategory::Nursery),
];
for (input, expected) in tests {
let de: RuleCategory = serde_json::from_str(&format!("{input:?}")).unwrap();
// deserializes to expected value
assert_eq!(de, expected, "{input}");
// try_from on a str produces the same value as deserializing
assert_eq!(de, RuleCategory::try_from(input).unwrap(), "{input}");
}
}
}