use std::fmt;
use std::str::FromStr;
use crate::Dialect;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[non_exhaustive]
pub enum Rule {
Syntax,
DuplicateKey,
InvalidNumber,
InvalidCodePoint,
ByteOrderMark,
MissingFinalNewline,
}
impl Rule {
pub const ALL: [Rule; 6] = [
Rule::Syntax,
Rule::DuplicateKey,
Rule::InvalidNumber,
Rule::InvalidCodePoint,
Rule::ByteOrderMark,
Rule::MissingFinalNewline,
];
pub fn id(self) -> &'static str {
match self {
Rule::Syntax => "syntax",
Rule::DuplicateKey => "duplicate-key",
Rule::InvalidNumber => "invalid-number",
Rule::InvalidCodePoint => "invalid-code-point",
Rule::ByteOrderMark => "byte-order-mark",
Rule::MissingFinalNewline => "missing-final-newline",
}
}
pub fn default_severity(self, dialect: Dialect) -> Severity {
match (self, dialect) {
(Rule::Syntax | Rule::DuplicateKey, _)
| (Rule::InvalidNumber | Rule::InvalidCodePoint, Dialect::IJson)
| (Rule::ByteOrderMark, Dialect::JsonLines) => Severity::Error,
(Rule::ByteOrderMark, Dialect::Json | Dialect::IJson)
| (Rule::MissingFinalNewline, Dialect::JsonLines) => Severity::Warning,
_ => Severity::Off,
}
}
}
impl fmt::Display for Rule {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.id())
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct UnknownRule(pub String);
impl fmt::Display for UnknownRule {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let ids: Vec<&str> = Rule::ALL.iter().map(|r| r.id()).collect();
write!(
f,
"unknown rule `{}`; expected one of {}",
self.0,
ids.join(", ")
)
}
}
impl std::error::Error for UnknownRule {}
impl FromStr for Rule {
type Err = UnknownRule;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Rule::ALL
.into_iter()
.find(|r| r.id() == s)
.ok_or_else(|| UnknownRule(s.to_owned()))
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum Severity {
Off,
Warning,
Error,
}
impl Severity {
pub fn name(self) -> &'static str {
match self {
Severity::Off => "off",
Severity::Warning => "warning",
Severity::Error => "error",
}
}
pub(crate) fn is_on(self) -> bool {
self != Severity::Off
}
}
impl fmt::Display for Severity {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.name())
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Config {
dialect: Dialect,
overrides: [Option<Severity>; Rule::ALL.len()],
pub(crate) max_depth: usize,
}
impl Config {
pub const DEFAULT_MAX_DEPTH: usize = 10_000;
pub fn new(dialect: Dialect) -> Self {
Config {
dialect,
overrides: [None; Rule::ALL.len()],
max_depth: Self::DEFAULT_MAX_DEPTH,
}
}
#[must_use]
pub fn max_depth(mut self, depth: usize) -> Self {
self.max_depth = depth;
self
}
#[must_use]
pub fn rule(mut self, rule: Rule, severity: Severity) -> Self {
self.overrides[rule as usize] = Some(severity);
self
}
pub fn dialect(&self) -> Dialect {
self.dialect
}
pub fn severity(&self, rule: Rule) -> Severity {
if rule == Rule::Syntax {
return Severity::Error;
}
self.overrides[rule as usize].unwrap_or_else(|| rule.default_severity(self.dialect))
}
}