1#![forbid(unsafe_code)]
6
7use depguard_types::{Severity, ids};
8
9#[derive(Clone, Copy, Debug)]
10pub struct CheckCatalogEntry {
11 pub id: &'static str,
12 pub codes: &'static [&'static str],
14 pub strict_enabled: bool,
16 pub strict_severity: Severity,
18 pub warn_enabled: bool,
20 pub warn_severity: Severity,
22 pub feature: CheckFeature,
24 pub bdd_feature_file: &'static str,
26}
27
28#[derive(Clone, Copy, Debug)]
29pub struct ProfileCheck {
30 pub id: &'static str,
31 pub enabled: bool,
32 pub severity: Severity,
33}
34
35#[derive(Clone, Copy, Debug)]
36pub enum CheckFeature {
37 NoWildcards,
38 PathRequiresVersion,
39 PathSafety,
40 WorkspaceInheritance,
41 GitRequiresVersion,
42 DevOnlyInNormal,
43 DefaultFeaturesExplicit,
44 NoMultipleVersions,
45 OptionalUnused,
46 YankedVersions,
47}
48
49const CHECK_CATALOG: &[CheckCatalogEntry] = &[
50 CheckCatalogEntry {
51 id: ids::CHECK_DEPS_NO_WILDCARDS,
52 codes: &[ids::CODE_WILDCARD_VERSION],
53 strict_enabled: true,
54 strict_severity: Severity::Error,
55 warn_enabled: true,
56 warn_severity: Severity::Warning,
57 feature: CheckFeature::NoWildcards,
58 bdd_feature_file: "rules_no_wildcards.feature",
59 },
60 CheckCatalogEntry {
61 id: ids::CHECK_DEPS_PATH_REQUIRES_VERSION,
62 codes: &[ids::CODE_PATH_WITHOUT_VERSION],
63 strict_enabled: true,
64 strict_severity: Severity::Error,
65 warn_enabled: true,
66 warn_severity: Severity::Warning,
67 feature: CheckFeature::PathRequiresVersion,
68 bdd_feature_file: "rules_path_requires_version.feature",
69 },
70 CheckCatalogEntry {
71 id: ids::CHECK_DEPS_PATH_SAFETY,
72 codes: &[ids::CODE_ABSOLUTE_PATH, ids::CODE_PARENT_ESCAPE],
73 strict_enabled: true,
74 strict_severity: Severity::Error,
75 warn_enabled: true,
76 warn_severity: Severity::Warning,
77 feature: CheckFeature::PathSafety,
78 bdd_feature_file: "rules_path_safety.feature",
79 },
80 CheckCatalogEntry {
81 id: ids::CHECK_DEPS_WORKSPACE_INHERITANCE,
82 codes: &[ids::CODE_MISSING_WORKSPACE_TRUE],
83 strict_enabled: false,
84 strict_severity: Severity::Error,
85 warn_enabled: false,
86 warn_severity: Severity::Warning,
87 feature: CheckFeature::WorkspaceInheritance,
88 bdd_feature_file: "rules_workspace_inheritance.feature",
89 },
90 CheckCatalogEntry {
91 id: ids::CHECK_DEPS_GIT_REQUIRES_VERSION,
92 codes: &[ids::CODE_GIT_WITHOUT_VERSION],
93 strict_enabled: false,
94 strict_severity: Severity::Error,
95 warn_enabled: false,
96 warn_severity: Severity::Warning,
97 feature: CheckFeature::GitRequiresVersion,
98 bdd_feature_file: "checks.feature",
99 },
100 CheckCatalogEntry {
101 id: ids::CHECK_DEPS_DEFAULT_FEATURES_EXPLICIT,
102 codes: &[ids::CODE_DEFAULT_FEATURES_IMPLICIT],
103 strict_enabled: false,
104 strict_severity: Severity::Warning,
105 warn_enabled: false,
106 warn_severity: Severity::Warning,
107 feature: CheckFeature::DefaultFeaturesExplicit,
108 bdd_feature_file: "checks.feature",
109 },
110 CheckCatalogEntry {
111 id: ids::CHECK_DEPS_NO_MULTIPLE_VERSIONS,
112 codes: &[ids::CODE_DUPLICATE_DIFFERENT_VERSIONS],
113 strict_enabled: false,
114 strict_severity: Severity::Warning,
115 warn_enabled: false,
116 warn_severity: Severity::Warning,
117 feature: CheckFeature::NoMultipleVersions,
118 bdd_feature_file: "checks.feature",
119 },
120 CheckCatalogEntry {
121 id: ids::CHECK_DEPS_OPTIONAL_UNUSED,
122 codes: &[ids::CODE_OPTIONAL_NOT_IN_FEATURES],
123 strict_enabled: false,
124 strict_severity: Severity::Warning,
125 warn_enabled: false,
126 warn_severity: Severity::Warning,
127 feature: CheckFeature::OptionalUnused,
128 bdd_feature_file: "checks.feature",
129 },
130 CheckCatalogEntry {
131 id: ids::CHECK_DEPS_DEV_ONLY_IN_NORMAL,
132 codes: &[ids::CODE_DEV_DEP_IN_NORMAL],
133 strict_enabled: false,
134 strict_severity: Severity::Warning,
135 warn_enabled: false,
136 warn_severity: Severity::Warning,
137 feature: CheckFeature::DevOnlyInNormal,
138 bdd_feature_file: "checks.feature",
139 },
140 CheckCatalogEntry {
141 id: ids::CHECK_DEPS_YANKED_VERSIONS,
142 codes: &[ids::CODE_VERSION_YANKED],
143 strict_enabled: false,
144 strict_severity: Severity::Error,
145 warn_enabled: false,
146 warn_severity: Severity::Error,
147 feature: CheckFeature::YankedVersions,
148 bdd_feature_file: "roadmap.feature",
149 },
150];
151
152impl CheckFeature {
153 pub const fn cargo_feature(self) -> &'static str {
154 match self {
155 Self::NoWildcards => "check-no-wildcards",
156 Self::PathRequiresVersion => "check-path-requires-version",
157 Self::PathSafety => "check-path-safety",
158 Self::WorkspaceInheritance => "check-workspace-inheritance",
159 Self::GitRequiresVersion => "check-git-requires-version",
160 Self::DevOnlyInNormal => "check-dev-only-in-normal",
161 Self::DefaultFeaturesExplicit => "check-default-features-explicit",
162 Self::NoMultipleVersions => "check-no-multiple-versions",
163 Self::OptionalUnused => "check-optional-unused",
164 Self::YankedVersions => "check-yanked-versions",
165 }
166 }
167
168 pub const fn is_enabled(self) -> bool {
169 match self {
170 Self::NoWildcards => cfg!(feature = "check-no-wildcards"),
171 Self::PathRequiresVersion => cfg!(feature = "check-path-requires-version"),
172 Self::PathSafety => cfg!(feature = "check-path-safety"),
173 Self::WorkspaceInheritance => cfg!(feature = "check-workspace-inheritance"),
174 Self::GitRequiresVersion => cfg!(feature = "check-git-requires-version"),
175 Self::DevOnlyInNormal => cfg!(feature = "check-dev-only-in-normal"),
176 Self::DefaultFeaturesExplicit => cfg!(feature = "check-default-features-explicit"),
177 Self::NoMultipleVersions => cfg!(feature = "check-no-multiple-versions"),
178 Self::OptionalUnused => cfg!(feature = "check-optional-unused"),
179 Self::YankedVersions => cfg!(feature = "check-yanked-versions"),
180 }
181 }
182}
183
184pub fn catalog() -> &'static [CheckCatalogEntry] {
185 CHECK_CATALOG
186}
187
188pub fn is_known_check_id(check_id: &str) -> bool {
189 CHECK_CATALOG.iter().any(|entry| entry.id == check_id)
190}
191
192pub fn all_check_ids() -> Vec<&'static str> {
193 CHECK_CATALOG.iter().map(|entry| entry.id).collect()
194}
195
196pub fn all_codes() -> Vec<&'static str> {
197 CHECK_CATALOG
198 .iter()
199 .flat_map(|entry| entry.codes.iter().copied())
200 .collect()
201}
202
203pub fn is_check_available(check_id: &str) -> bool {
204 entry(check_id).is_some_and(|entry| entry.feature.is_enabled())
205}
206
207pub fn entry(check_id: &str) -> Option<&'static CheckCatalogEntry> {
208 CHECK_CATALOG.iter().find(|entry| entry.id == check_id)
209}
210
211pub fn feature_name(check_id: &str) -> Option<&'static str> {
212 entry(check_id).map(|entry| entry.feature.cargo_feature())
213}
214
215pub fn bdd_feature_file(check_id: &str) -> Option<&'static str> {
216 entry(check_id).map(|entry| entry.bdd_feature_file)
217}
218
219pub fn checks_for_profile(profile: &str) -> Vec<ProfileCheck> {
220 let profile_is_warnish = matches!(profile, "warn" | "team" | "compat" | "oss");
221
222 CHECK_CATALOG
223 .iter()
224 .map(|entry| {
225 if profile_is_warnish {
226 ProfileCheck {
227 id: entry.id,
228 enabled: entry.warn_enabled,
229 severity: entry.warn_severity,
230 }
231 } else {
232 ProfileCheck {
233 id: entry.id,
234 enabled: entry.strict_enabled,
235 severity: entry.strict_severity,
236 }
237 }
238 })
239 .collect()
240}
241
242#[cfg(test)]
243mod tests {
244 use super::*;
245
246 #[test]
247 fn all_catalog_ids_have_explanations() {
248 for entry in catalog() {
249 assert!(
250 depguard_types::explain::lookup_explanation(entry.id).is_some(),
251 "check id {} has explanation",
252 entry.id
253 );
254 }
255 }
256
257 #[test]
258 fn strict_and_warn_profiles_cover_all_checks() {
259 let strict = checks_for_profile("strict");
260 let warn = checks_for_profile("warn");
261 assert_eq!(strict.len(), catalog().len());
262 assert_eq!(warn.len(), catalog().len());
263 }
264
265 #[test]
266 fn check_catalog_entries_have_bdd_feature_file() {
267 for entry in catalog() {
268 assert!(
269 !entry.bdd_feature_file.is_empty(),
270 "{} must define a BDD feature file",
271 entry.id
272 );
273 }
274 }
275
276 #[test]
277 fn check_features_default_to_enabled() {
278 for entry in catalog() {
279 assert!(
280 entry.feature.is_enabled(),
281 "{} feature should be enabled",
282 entry.id
283 );
284 }
285 }
286}