1use std::fmt;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
7pub enum Severity {
8 Info,
10 Warning,
12 Error,
14}
15
16impl fmt::Display for Severity {
17 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
18 match self {
19 Severity::Info => write!(f, "info"),
20 Severity::Warning => write!(f, "warning"),
21 Severity::Error => write!(f, "error"),
22 }
23 }
24}
25
26impl Severity {
27 pub fn parse(s: &str) -> Option<Self> {
29 match s.to_lowercase().as_str() {
30 "info" => Some(Severity::Info),
31 "warning" | "warn" => Some(Severity::Warning),
32 "error" | "err" => Some(Severity::Error),
33 _ => None,
34 }
35 }
36}
37
38#[derive(Debug, Clone, PartialEq, Eq)]
43pub enum RuleKind {
44 UnusedTag,
50 UndefinedTag,
52 EmptyBlock,
54 UnusedAoi,
56 UnusedDataType,
58 CyclomaticComplexity,
60 DeepNesting,
62
63 FloatEquality,
71 TimeEquality,
73 DivisionByZero,
75 MagicNumber,
77 TimerNoReset,
79 RecursiveCall,
81 LoopVarModified,
83 TooManyParameters,
85 ExcessiveGlobals,
87
88 ContinueUsed,
91 ExitUsed,
93 IfWithoutElse,
95 CaseWithoutElse,
97 ReturnInMiddle,
99
100 NameTooShort,
103 NameTooLong,
105
106 AoiNoDescription,
109 TagNoDescription,
111 RoutineNoDescription,
113 ProgramNoDescription,
115 TaskWatchdogDisabled,
117 ExcessiveTaskRate,
119 AliasChain,
121 LargeArray,
123}
124
125impl RuleKind {
126 pub fn code(&self) -> &'static str {
128 match self {
129 RuleKind::UnusedTag => "S0001",
131 RuleKind::UndefinedTag => "S0002",
132 RuleKind::EmptyBlock => "S0003",
133 RuleKind::UnusedAoi => "S0004",
134 RuleKind::UnusedDataType => "S0005",
135 RuleKind::FloatEquality => "C0010",
137 RuleKind::TimeEquality => "C0011",
138 RuleKind::DivisionByZero => "C0014",
139 RuleKind::MagicNumber => "C0015",
140 RuleKind::TimerNoReset => "C0016",
141 RuleKind::RecursiveCall => "C0031",
142 RuleKind::LoopVarModified => "C0032",
143 RuleKind::TooManyParameters => "C0050",
144 RuleKind::ExcessiveGlobals => "C0060",
145 RuleKind::ContinueUsed => "S0020",
147 RuleKind::ExitUsed => "S0021",
148 RuleKind::IfWithoutElse => "S0022",
149 RuleKind::CaseWithoutElse => "S0023",
150 RuleKind::ReturnInMiddle => "S0025",
151 RuleKind::CyclomaticComplexity => "M0001",
153 RuleKind::DeepNesting => "M0003",
154 RuleKind::NameTooShort => "N0006",
156 RuleKind::NameTooLong => "N0007",
157 RuleKind::AoiNoDescription => "X0001",
159 RuleKind::TagNoDescription => "X0002",
160 RuleKind::RoutineNoDescription => "X0003",
161 RuleKind::ProgramNoDescription => "X0004",
162 RuleKind::TaskWatchdogDisabled => "X0006",
163 RuleKind::ExcessiveTaskRate => "X0007",
164 RuleKind::AliasChain => "X0009",
165 RuleKind::LargeArray => "X0010",
166 }
167 }
168
169 pub fn name(&self) -> &'static str {
171 match self {
172 RuleKind::UnusedTag => "unused-tag",
174 RuleKind::UndefinedTag => "undefined-tag",
175 RuleKind::EmptyBlock => "empty-block",
176 RuleKind::UnusedAoi => "unused-aoi",
177 RuleKind::UnusedDataType => "unused-datatype",
178 RuleKind::FloatEquality => "float-equality",
179 RuleKind::TimeEquality => "time-equality",
180 RuleKind::DivisionByZero => "division-by-zero",
181 RuleKind::MagicNumber => "magic-number",
182 RuleKind::TimerNoReset => "timer-no-reset",
183 RuleKind::RecursiveCall => "recursive-call",
184 RuleKind::LoopVarModified => "loop-var-modified",
185 RuleKind::TooManyParameters => "too-many-parameters",
186 RuleKind::ExcessiveGlobals => "excessive-globals",
187 RuleKind::ContinueUsed => "continue-used",
189 RuleKind::ExitUsed => "exit-used",
190 RuleKind::IfWithoutElse => "if-without-else",
191 RuleKind::CaseWithoutElse => "case-without-else",
192 RuleKind::ReturnInMiddle => "return-in-middle",
193 RuleKind::CyclomaticComplexity => "cyclomatic-complexity",
195 RuleKind::DeepNesting => "deep-nesting",
196 RuleKind::NameTooShort => "name-too-short",
198 RuleKind::NameTooLong => "name-too-long",
199 RuleKind::AoiNoDescription => "aoi-no-description",
201 RuleKind::TagNoDescription => "tag-no-description",
202 RuleKind::RoutineNoDescription => "routine-no-description",
203 RuleKind::ProgramNoDescription => "program-no-description",
204 RuleKind::TaskWatchdogDisabled => "task-watchdog-disabled",
205 RuleKind::ExcessiveTaskRate => "excessive-task-rate",
206 RuleKind::AliasChain => "alias-chain",
207 RuleKind::LargeArray => "large-array",
208 }
209 }
210}
211
212impl fmt::Display for RuleKind {
213 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
214 write!(f, "{}", self.code())
215 }
216}
217
218#[derive(Debug, Clone)]
220pub struct Rule {
221 pub kind: RuleKind,
223 pub severity: Severity,
225 pub location: String,
227 pub identifier: String,
229 pub message: String,
231}
232
233impl Rule {
234 pub fn new(
236 kind: RuleKind,
237 severity: Severity,
238 location: impl Into<String>,
239 identifier: impl Into<String>,
240 message: impl Into<String>,
241 ) -> Self {
242 Self {
243 kind,
244 severity,
245 location: location.into(),
246 identifier: identifier.into(),
247 message: message.into(),
248 }
249 }
250}
251
252impl fmt::Display for Rule {
253 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
254 write!(
255 f,
256 "[{}] {}: {} - {} ({})",
257 self.severity, self.kind, self.location, self.message, self.identifier
258 )
259 }
260}
261
262#[derive(Debug, Clone, Default)]
264pub struct Report {
265 pub rules: Vec<Rule>,
267 pub source_file: Option<String>,
269}
270
271impl Report {
272 pub fn new() -> Self {
274 Self::default()
275 }
276
277 pub fn add(&mut self, rule: Rule) {
279 self.rules.push(rule);
280 }
281
282 pub fn filter_by_severity(&self, min_severity: Severity) -> Vec<&Rule> {
284 self.rules
285 .iter()
286 .filter(|s| s.severity >= min_severity)
287 .collect()
288 }
289
290 pub fn is_empty(&self) -> bool {
292 self.rules.is_empty()
293 }
294
295 pub fn len(&self) -> usize {
297 self.rules.len()
298 }
299}