1use crate::config::{RuleConfig, Severity};
2use serde::Deserialize;
3
4#[derive(Debug, Deserialize)]
6pub struct TomlConfig {
7 pub baseline: BaselineSection,
8 #[serde(default)]
9 pub rule: Vec<TomlRule>,
10}
11
12#[derive(Debug, Clone, Deserialize)]
14pub struct ScopedPreset {
15 #[serde(deserialize_with = "string_or_vec")]
16 pub preset: Vec<String>,
17 pub path: String,
18 #[serde(default)]
19 pub exclude_rules: Vec<String>,
20}
21
22fn string_or_vec<'de, D>(deserializer: D) -> Result<Vec<String>, D::Error>
24where
25 D: serde::Deserializer<'de>,
26{
27 use serde::de;
28
29 struct StringOrVec;
30
31 impl<'de> de::Visitor<'de> for StringOrVec {
32 type Value = Vec<String>;
33
34 fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
35 f.write_str("a string or array of strings")
36 }
37
38 fn visit_str<E: de::Error>(self, v: &str) -> Result<Self::Value, E> {
39 Ok(vec![v.to_owned()])
40 }
41
42 fn visit_seq<A: de::SeqAccess<'de>>(self, mut seq: A) -> Result<Self::Value, A::Error> {
43 let mut v = Vec::new();
44 while let Some(s) = seq.next_element()? {
45 v.push(s);
46 }
47 Ok(v)
48 }
49 }
50
51 deserializer.deserialize_any(StringOrVec)
52}
53
54#[derive(Debug, Deserialize)]
56pub struct BaselineSection {
57 #[allow(dead_code)]
58 pub name: Option<String>,
59 #[serde(default)]
60 pub include: Vec<String>,
61 #[serde(default)]
62 pub exclude: Vec<String>,
63 #[serde(default)]
64 pub extends: Vec<String>,
65 #[serde(default)]
67 pub plugins: Vec<String>,
68 #[serde(default)]
70 pub scoped: Vec<ScopedPreset>,
71}
72
73#[derive(Debug, Clone, Deserialize)]
75pub struct TomlRule {
76 pub id: String,
77 #[serde(rename = "type")]
78 pub rule_type: String,
79 #[serde(default = "default_severity")]
80 pub severity: String,
81 pub glob: Option<String>,
82 #[serde(default)]
83 pub message: String,
84 pub suggest: Option<String>,
85 #[serde(default)]
86 pub allowed_classes: Vec<String>,
87 #[serde(default)]
88 pub token_map: Vec<String>,
89 pub pattern: Option<String>,
90 pub max_count: Option<usize>,
91 #[serde(default)]
92 pub packages: Vec<String>,
93 #[serde(default)]
94 pub regex: bool,
95 pub manifest: Option<String>,
96 #[serde(default)]
97 pub exclude_glob: Vec<String>,
98 pub file_contains: Option<String>,
99 pub file_not_contains: Option<String>,
100 #[serde(default)]
101 pub required_files: Vec<String>,
102 #[serde(default)]
103 pub forbidden_files: Vec<String>,
104 pub condition_pattern: Option<String>,
105 #[serde(default)]
106 pub skip_strings: bool,
107}
108
109fn default_severity() -> String {
110 "warning".into()
111}
112
113impl Default for TomlRule {
114 fn default() -> Self {
115 Self {
116 id: String::new(),
117 rule_type: String::new(),
118 severity: default_severity(),
119 glob: None,
120 message: String::new(),
121 suggest: None,
122 allowed_classes: Vec::new(),
123 token_map: Vec::new(),
124 pattern: None,
125 max_count: None,
126 packages: Vec::new(),
127 regex: false,
128 manifest: None,
129 exclude_glob: Vec::new(),
130 file_contains: None,
131 file_not_contains: None,
132 required_files: Vec::new(),
133 forbidden_files: Vec::new(),
134 condition_pattern: None,
135 skip_strings: false,
136 }
137 }
138}
139
140impl TomlRule {
141 pub fn to_rule_config(&self) -> RuleConfig {
143 let severity = match self.severity.to_lowercase().as_str() {
144 "error" => Severity::Error,
145 _ => Severity::Warning,
146 };
147
148 RuleConfig {
149 id: self.id.clone(),
150 severity,
151 message: self.message.clone(),
152 suggest: self.suggest.clone(),
153 glob: self.glob.clone(),
154 allowed_classes: self.allowed_classes.clone(),
155 token_map: self.token_map.clone(),
156 pattern: self.pattern.clone(),
157 max_count: self.max_count,
158 packages: self.packages.clone(),
159 regex: self.regex,
160 manifest: self.manifest.clone(),
161 exclude_glob: self.exclude_glob.clone(),
162 file_contains: self.file_contains.clone(),
163 file_not_contains: self.file_not_contains.clone(),
164 required_files: self.required_files.clone(),
165 forbidden_files: self.forbidden_files.clone(),
166 condition_pattern: self.condition_pattern.clone(),
167 skip_strings: self.skip_strings,
168 }
169 }
170}