use crate::config::{RuleConfig, Severity};
use serde::Deserialize;
#[derive(Debug, Deserialize)]
pub struct TomlConfig {
pub baseline: BaselineSection,
#[serde(default)]
pub rule: Vec<TomlRule>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct ScopedPreset {
#[serde(deserialize_with = "string_or_vec")]
pub preset: Vec<String>,
pub path: String,
#[serde(default)]
pub exclude_rules: Vec<String>,
}
fn string_or_vec<'de, D>(deserializer: D) -> Result<Vec<String>, D::Error>
where
D: serde::Deserializer<'de>,
{
use serde::de;
struct StringOrVec;
impl<'de> de::Visitor<'de> for StringOrVec {
type Value = Vec<String>;
fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
f.write_str("a string or array of strings")
}
fn visit_str<E: de::Error>(self, v: &str) -> Result<Self::Value, E> {
Ok(vec![v.to_owned()])
}
fn visit_seq<A: de::SeqAccess<'de>>(self, mut seq: A) -> Result<Self::Value, A::Error> {
let mut v = Vec::new();
while let Some(s) = seq.next_element()? {
v.push(s);
}
Ok(v)
}
}
deserializer.deserialize_any(StringOrVec)
}
#[derive(Debug, Deserialize)]
pub struct BaselineSection {
#[allow(dead_code)]
pub name: Option<String>,
#[serde(default)]
pub include: Vec<String>,
#[serde(default)]
pub exclude: Vec<String>,
#[serde(default)]
pub extends: Vec<String>,
#[serde(default)]
pub plugins: Vec<String>,
#[serde(default)]
pub scoped: Vec<ScopedPreset>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct TomlRule {
pub id: String,
#[serde(rename = "type")]
pub rule_type: String,
#[serde(default = "default_severity")]
pub severity: String,
pub glob: Option<String>,
#[serde(default)]
pub message: String,
pub suggest: Option<String>,
#[serde(default)]
pub allowed_classes: Vec<String>,
#[serde(default)]
pub token_map: Vec<String>,
pub pattern: Option<String>,
pub max_count: Option<usize>,
#[serde(default)]
pub packages: Vec<String>,
#[serde(default)]
pub regex: bool,
pub manifest: Option<String>,
#[serde(default)]
pub exclude_glob: Vec<String>,
pub file_contains: Option<String>,
pub file_not_contains: Option<String>,
#[serde(default)]
pub required_files: Vec<String>,
#[serde(default)]
pub forbidden_files: Vec<String>,
pub condition_pattern: Option<String>,
#[serde(default)]
pub skip_strings: bool,
}
fn default_severity() -> String {
"warning".into()
}
impl Default for TomlRule {
fn default() -> Self {
Self {
id: String::new(),
rule_type: String::new(),
severity: default_severity(),
glob: None,
message: String::new(),
suggest: None,
allowed_classes: Vec::new(),
token_map: Vec::new(),
pattern: None,
max_count: None,
packages: Vec::new(),
regex: false,
manifest: None,
exclude_glob: Vec::new(),
file_contains: None,
file_not_contains: None,
required_files: Vec::new(),
forbidden_files: Vec::new(),
condition_pattern: None,
skip_strings: false,
}
}
}
impl TomlRule {
pub fn to_rule_config(&self) -> RuleConfig {
let severity = match self.severity.to_lowercase().as_str() {
"error" => Severity::Error,
_ => Severity::Warning,
};
RuleConfig {
id: self.id.clone(),
severity,
message: self.message.clone(),
suggest: self.suggest.clone(),
glob: self.glob.clone(),
allowed_classes: self.allowed_classes.clone(),
token_map: self.token_map.clone(),
pattern: self.pattern.clone(),
max_count: self.max_count,
packages: self.packages.clone(),
regex: self.regex,
manifest: self.manifest.clone(),
exclude_glob: self.exclude_glob.clone(),
file_contains: self.file_contains.clone(),
file_not_contains: self.file_not_contains.clone(),
required_files: self.required_files.clone(),
forbidden_files: self.forbidden_files.clone(),
condition_pattern: self.condition_pattern.clone(),
skip_strings: self.skip_strings,
}
}
}