1use std::collections::BTreeMap;
2
3use crate::{AstCtx, FileCtx, Fix, TomlCtx, Violation, languages::workspace::WorkspaceCtx};
4
5#[derive(Debug, serde::Deserialize, serde::Serialize)]
7pub struct ConfigRule {
8 pub name: String,
9 pub description: String,
10 pub severity: String,
11 pub category: String,
12 pub fixable: bool,
13 pub enabled: bool,
14 #[serde(default, skip_serializing_if = "Vec::is_empty")]
15 pub ignore: Vec<String>,
16 #[serde(default, skip_serializing_if = "Option::is_none")]
17 pub params: Option<BTreeMap<String, ConfigValue>>,
18}
19
20#[derive(Debug, serde::Deserialize, serde::Serialize)]
22#[serde(transparent)]
23pub struct ConfigValue(serde_json::Value);
24
25impl From<serde_json::Value> for ConfigValue {
26 fn from(value: serde_json::Value) -> Self {
27 Self(value)
28 }
29}
30
31impl PartialEq<i32> for ConfigValue {
32 fn eq(&self, other: &i32) -> bool {
33 self.0.as_i64() == Some(i64::from(*other))
34 }
35}
36
37#[derive(Clone, Copy, Debug, Eq, PartialEq)]
39pub enum RuleKind {
41 RustLine,
42 RustAst,
43 RustWorkspace,
44 Workspace,
45 Toml,
46}
47
48impl RuleKind {
49 #[must_use]
50 pub fn as_str(&self) -> &'static str {
51 match self {
52 RuleKind::RustLine => "rust-line",
53 RuleKind::RustAst => "rust-ast",
54 RuleKind::RustWorkspace => "rust-workspace",
55 RuleKind::Workspace => "workspace",
56 RuleKind::Toml => "toml",
57 }
58 }
59}
60
61impl std::fmt::Display for RuleKind {
62 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
63 f.write_str(self.as_str())
64 }
65}
66
67#[derive(Debug)]
69pub struct RuleMeta {
71 pub name: &'static str,
72 pub description: &'static str,
73 pub justification: &'static str,
74 pub severity: Severity,
75 pub kind: RuleKind,
76 pub examples: &'static [Example],
77 pub params: &'static [RuleParam],
78 pub fixable: bool,
79}
80
81#[must_use]
83#[doc(alias = "lint registry", alias = "tidy rules")]
84pub fn all_rules() -> Vec<RuleMeta> {
85 let mut rules: Vec<RuleMeta> = inventory::iter::<Rule>
86 .into_iter()
87 .map(|rule| rule.info.to_meta(rule.check.kind(), rule.fix.is_some()))
88 .collect();
89
90 rules.sort_by_key(|r| r.name);
91
92 rules
93}
94
95#[derive(Debug)]
97pub struct Example {
98 pub label: &'static str,
99 pub code: &'static str,
100 pub pass: bool,
101}
102
103#[derive(Clone, Copy, Debug, Eq, PartialEq)]
104pub enum ParamType {
106 Int,
107 StringArray,
108}
109
110#[derive(Clone, Debug)]
111pub enum ParamDefault {
113 Int(i64),
114 StringArray(&'static [&'static str]),
115}
116
117#[derive(Debug)]
118pub struct RuleParam {
119 pub name: &'static str,
120 pub param_type: ParamType,
121 pub default: ParamDefault,
122}
123
124#[derive(Clone, Copy, Debug, Eq, PartialEq)]
125pub enum Severity {
127 Low,
128 Medium,
129 High,
130}
131
132impl Severity {
133 #[must_use]
134 pub fn as_str(&self) -> &'static str {
135 match self {
136 Severity::Low => "low",
137 Severity::Medium => "medium",
138 Severity::High => "high",
139 }
140 }
141}
142
143impl std::fmt::Display for Severity {
144 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
145 f.write_str(self.as_str())
146 }
147}
148
149#[derive(Debug)]
151pub(crate) struct RuleInfo {
153 pub name: &'static str,
154 pub description: &'static str,
155 pub justification: &'static str,
156 pub severity: Severity,
157 pub examples: &'static [Example],
158 pub params: &'static [RuleParam],
159}
160
161impl RuleInfo {
162 fn to_meta(&self, kind: RuleKind, fixable: bool) -> RuleMeta {
163 RuleMeta {
164 name: self.name,
165 description: self.description,
166 justification: self.justification,
167 severity: self.severity,
168 kind,
169 examples: self.examples,
170 params: self.params,
171 fixable,
172 }
173 }
174}
175
176#[derive(Clone, Copy, Debug)]
178pub(crate) enum RuleCheck {
179 RustLine(fn(&FileCtx<'_>) -> Vec<Violation>),
180 RustAst(fn(&AstCtx<'_>) -> Vec<Violation>),
181 RustWorkspace(fn(&WorkspaceCtx<'_>) -> Vec<Violation>),
182 Workspace(fn(&WorkspaceCtx<'_>) -> Vec<Violation>),
183 Toml(fn(&TomlCtx<'_>) -> Vec<Violation>),
184}
185
186impl RuleCheck {
187 pub(crate) const fn kind(self) -> RuleKind {
188 match self {
189 Self::RustLine(_) => RuleKind::RustLine,
190 Self::RustAst(_) => RuleKind::RustAst,
191 Self::RustWorkspace(_) => RuleKind::RustWorkspace,
192 Self::Workspace(_) => RuleKind::Workspace,
193 Self::Toml(_) => RuleKind::Toml,
194 }
195 }
196
197 pub(crate) const fn extensions(self) -> &'static [&'static str] {
198 match self {
199 Self::RustLine(_) | Self::RustAst(_) | Self::RustWorkspace(_) => &["rs"],
200 Self::Workspace(_) => &["rs", "toml"],
201 Self::Toml(_) => &["toml"],
202 }
203 }
204}
205
206#[derive(Clone, Copy, Debug)]
208pub(crate) enum RuleFix {
209 RustLine(fn(&FileCtx<'_>, &Violation) -> Option<Fix>),
210 RustAst(fn(&AstCtx<'_>, &Violation) -> Option<Fix>),
211 RustAstTree(fn(&AstCtx<'_>, &[Violation]) -> Option<String>),
212 Toml(fn(&TomlCtx<'_>, &Violation) -> Option<Fix>),
213}
214
215#[derive(Debug)]
217pub(crate) struct Rule {
218 pub info: RuleInfo,
219 pub check: RuleCheck,
220 pub fix: Option<RuleFix>,
221}
222
223inventory::collect!(Rule);