mirror of
https://github.com/danbulant/oxc
synced 2026-05-24 12:21:58 +00:00
refactor(linter): add schemars and serde traits to AllowWarnDeny and RuleCategories (#6119)
This commit is contained in:
parent
40bd919849
commit
82b8f217ab
2 changed files with 151 additions and 10 deletions
|
|
@ -1,10 +1,15 @@
|
||||||
use std::{convert::From, fmt};
|
use std::{
|
||||||
|
convert::From,
|
||||||
|
fmt::{self, Display},
|
||||||
|
};
|
||||||
|
|
||||||
use oxc_diagnostics::{OxcDiagnostic, Severity};
|
use oxc_diagnostics::{OxcDiagnostic, Severity};
|
||||||
use schemars::{schema::SchemaObject, JsonSchema};
|
use schemars::{schema::SchemaObject, JsonSchema};
|
||||||
|
use serde::{de, Deserialize, Serialize};
|
||||||
use serde_json::{Number, Value};
|
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 {
|
pub enum AllowWarnDeny {
|
||||||
Allow, // Off
|
Allow, // Off
|
||||||
Warn, // Warn
|
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 {
|
impl TryFrom<&Number> for AllowWarnDeny {
|
||||||
type Error = OxcDiagnostic;
|
type Error = OxcDiagnostic;
|
||||||
|
|
||||||
fn try_from(value: &Number) -> Result<Self, Self::Error> {
|
fn try_from(value: &Number) -> Result<Self, Self::Error> {
|
||||||
match value.as_i64() {
|
let value = value.as_i64().ok_or_else(|| invalid_int_severity(value))?;
|
||||||
Some(0) => Ok(Self::Allow),
|
Self::try_from(value)
|
||||||
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:?}"#
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,8 @@ use std::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use oxc_semantic::SymbolId;
|
use oxc_semantic::SymbolId;
|
||||||
|
use schemars::JsonSchema;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
context::{ContextHost, LintContext},
|
context::{ContextHost, LintContext},
|
||||||
|
|
@ -61,7 +63,8 @@ pub trait RuleMeta {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Rule categories defined by rust-clippy
|
/// 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 {
|
pub enum RuleCategory {
|
||||||
/// Code that is outright wrong or useless
|
/// Code that is outright wrong or useless
|
||||||
Correctness,
|
Correctness,
|
||||||
|
|
@ -277,6 +280,7 @@ impl RuleWithSeverity {
|
||||||
mod test {
|
mod test {
|
||||||
use markdown::{to_html_with_options, Options};
|
use markdown::{to_html_with_options, Options};
|
||||||
|
|
||||||
|
use super::RuleCategory;
|
||||||
use crate::rules::RULES;
|
use crate::rules::RULES;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -295,4 +299,25 @@ mod test {
|
||||||
assert!(!html.is_empty());
|
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}");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue