1#![doc = "Feature matrix and warning catalog for tokmd-analysis preset execution."]
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq)]
4pub enum PresetKind {
5 Receipt,
6 Estimate,
7 Health,
8 Risk,
9 Supply,
10 Architecture,
11 Topics,
12 Security,
13 Identity,
14 Git,
15 Deep,
16 Fun,
17}
18
19impl PresetKind {
20 pub const fn as_str(self) -> &'static str {
21 match self {
22 Self::Receipt => "receipt",
23 Self::Estimate => "estimate",
24 Self::Health => "health",
25 Self::Risk => "risk",
26 Self::Supply => "supply",
27 Self::Architecture => "architecture",
28 Self::Topics => "topics",
29 Self::Security => "security",
30 Self::Identity => "identity",
31 Self::Git => "git",
32 Self::Deep => "deep",
33 Self::Fun => "fun",
34 }
35 }
36
37 #[allow(clippy::should_implement_trait)]
38 pub fn from_str(value: &str) -> Option<Self> {
39 match value {
40 "receipt" => Some(Self::Receipt),
41 "estimate" => Some(Self::Estimate),
42 "health" => Some(Self::Health),
43 "risk" => Some(Self::Risk),
44 "supply" => Some(Self::Supply),
45 "architecture" => Some(Self::Architecture),
46 "topics" => Some(Self::Topics),
47 "security" => Some(Self::Security),
48 "identity" => Some(Self::Identity),
49 "git" => Some(Self::Git),
50 "deep" => Some(Self::Deep),
51 "fun" => Some(Self::Fun),
52 _ => None,
53 }
54 }
55}
56
57pub const PRESET_KINDS: [PresetKind; 12] = [
58 PresetKind::Receipt,
59 PresetKind::Estimate,
60 PresetKind::Health,
61 PresetKind::Risk,
62 PresetKind::Supply,
63 PresetKind::Architecture,
64 PresetKind::Topics,
65 PresetKind::Security,
66 PresetKind::Identity,
67 PresetKind::Git,
68 PresetKind::Deep,
69 PresetKind::Fun,
70];
71
72impl PresetKind {
73 pub const fn all() -> &'static [PresetKind; 12] {
74 &PRESET_KINDS
75 }
76}
77
78#[derive(Debug, Clone, Copy, PartialEq, Eq)]
79pub struct PresetPlan {
80 pub assets: bool,
81 pub deps: bool,
82 pub todo: bool,
83 pub dup: bool,
84 pub imports: bool,
85 pub git: bool,
86 pub fun: bool,
87 pub archetype: bool,
88 pub topics: bool,
89 pub entropy: bool,
90 pub license: bool,
91 pub complexity: bool,
92 pub api_surface: bool,
93 #[cfg(all(feature = "halstead", feature = "content", feature = "walk"))]
94 pub halstead: bool,
95 #[cfg(feature = "git")]
96 pub churn: bool,
97 #[cfg(feature = "git")]
98 pub fingerprint: bool,
99}
100
101impl PresetPlan {
102 #[cfg_attr(
103 not(all(feature = "halstead", feature = "content", feature = "walk")),
104 allow(unused_mut)
105 )]
106 pub fn needs_files(&self) -> bool {
107 let mut needs = self.assets
108 || self.deps
109 || self.todo
110 || self.dup
111 || self.imports
112 || self.entropy
113 || self.license
114 || self.complexity
115 || self.api_surface;
116 #[cfg(all(feature = "halstead", feature = "content", feature = "walk"))]
117 {
118 needs = needs || self.halstead;
119 }
120 needs
121 }
122}
123
124#[derive(Debug, Clone, Copy)]
125pub struct PresetGridRow {
126 pub preset: PresetKind,
127 pub plan: PresetPlan,
128}
129
130pub const PRESET_GRID: [PresetGridRow; 12] = [
131 PresetGridRow {
132 preset: PresetKind::Receipt,
133 plan: PresetPlan {
134 assets: false,
135 deps: false,
136 todo: false,
137 dup: true,
138 imports: false,
139 git: true,
140 fun: false,
141 archetype: false,
142 topics: false,
143 entropy: false,
144 license: false,
145 complexity: true,
146 api_surface: true,
147 #[cfg(all(feature = "halstead", feature = "content", feature = "walk"))]
148 halstead: false,
149 #[cfg(feature = "git")]
150 churn: false,
151 #[cfg(feature = "git")]
152 fingerprint: false,
153 },
154 },
155 PresetGridRow {
156 preset: PresetKind::Estimate,
157 plan: PresetPlan {
158 assets: false,
159 deps: false,
160 todo: false,
161 dup: true,
162 imports: false,
163 git: true,
164 fun: false,
165 archetype: false,
166 topics: false,
167 entropy: false,
168 license: false,
169 complexity: true,
170 api_surface: true,
171 #[cfg(all(feature = "halstead", feature = "content", feature = "walk"))]
172 halstead: false,
173 #[cfg(feature = "git")]
174 churn: true,
175 #[cfg(feature = "git")]
176 fingerprint: false,
177 },
178 },
179 PresetGridRow {
180 preset: PresetKind::Health,
181 plan: PresetPlan {
182 assets: false,
183 deps: false,
184 todo: true,
185 dup: false,
186 imports: false,
187 git: false,
188 fun: false,
189 archetype: false,
190 topics: false,
191 entropy: false,
192 license: false,
193 complexity: true,
194 api_surface: false,
195 #[cfg(all(feature = "halstead", feature = "content", feature = "walk"))]
196 halstead: true,
197 #[cfg(feature = "git")]
198 churn: false,
199 #[cfg(feature = "git")]
200 fingerprint: false,
201 },
202 },
203 PresetGridRow {
204 preset: PresetKind::Risk,
205 plan: PresetPlan {
206 assets: false,
207 deps: false,
208 todo: false,
209 dup: false,
210 imports: false,
211 git: true,
212 fun: false,
213 archetype: false,
214 topics: false,
215 entropy: false,
216 license: false,
217 complexity: true,
218 api_surface: false,
219 #[cfg(all(feature = "halstead", feature = "content", feature = "walk"))]
220 halstead: true,
221 #[cfg(feature = "git")]
222 churn: false,
223 #[cfg(feature = "git")]
224 fingerprint: false,
225 },
226 },
227 PresetGridRow {
228 preset: PresetKind::Supply,
229 plan: PresetPlan {
230 assets: true,
231 deps: true,
232 todo: false,
233 dup: false,
234 imports: false,
235 git: false,
236 fun: false,
237 archetype: false,
238 topics: false,
239 entropy: false,
240 license: false,
241 complexity: false,
242 api_surface: false,
243 #[cfg(all(feature = "halstead", feature = "content", feature = "walk"))]
244 halstead: false,
245 #[cfg(feature = "git")]
246 churn: false,
247 #[cfg(feature = "git")]
248 fingerprint: false,
249 },
250 },
251 PresetGridRow {
252 preset: PresetKind::Architecture,
253 plan: PresetPlan {
254 assets: false,
255 deps: false,
256 todo: false,
257 dup: false,
258 imports: true,
259 git: false,
260 fun: false,
261 archetype: false,
262 topics: false,
263 entropy: false,
264 license: false,
265 complexity: false,
266 api_surface: true,
267 #[cfg(all(feature = "halstead", feature = "content", feature = "walk"))]
268 halstead: false,
269 #[cfg(feature = "git")]
270 churn: false,
271 #[cfg(feature = "git")]
272 fingerprint: false,
273 },
274 },
275 PresetGridRow {
276 preset: PresetKind::Topics,
277 plan: PresetPlan {
278 assets: false,
279 deps: false,
280 todo: false,
281 dup: false,
282 imports: false,
283 git: false,
284 fun: false,
285 archetype: false,
286 topics: true,
287 entropy: false,
288 license: false,
289 complexity: false,
290 api_surface: false,
291 #[cfg(all(feature = "halstead", feature = "content", feature = "walk"))]
292 halstead: false,
293 #[cfg(feature = "git")]
294 churn: false,
295 #[cfg(feature = "git")]
296 fingerprint: false,
297 },
298 },
299 PresetGridRow {
300 preset: PresetKind::Security,
301 plan: PresetPlan {
302 assets: false,
303 deps: false,
304 todo: false,
305 dup: false,
306 imports: false,
307 git: false,
308 fun: false,
309 archetype: false,
310 topics: false,
311 entropy: true,
312 license: true,
313 complexity: false,
314 api_surface: false,
315 #[cfg(all(feature = "halstead", feature = "content", feature = "walk"))]
316 halstead: false,
317 #[cfg(feature = "git")]
318 churn: false,
319 #[cfg(feature = "git")]
320 fingerprint: false,
321 },
322 },
323 PresetGridRow {
324 preset: PresetKind::Identity,
325 plan: PresetPlan {
326 assets: false,
327 deps: false,
328 todo: false,
329 dup: false,
330 imports: false,
331 git: true,
332 fun: false,
333 archetype: true,
334 topics: false,
335 entropy: false,
336 license: false,
337 complexity: false,
338 api_surface: false,
339 #[cfg(all(feature = "halstead", feature = "content", feature = "walk"))]
340 halstead: false,
341 #[cfg(feature = "git")]
342 churn: false,
343 #[cfg(feature = "git")]
344 fingerprint: true,
345 },
346 },
347 PresetGridRow {
348 preset: PresetKind::Git,
349 plan: PresetPlan {
350 assets: false,
351 deps: false,
352 todo: false,
353 dup: false,
354 imports: false,
355 git: true,
356 fun: false,
357 archetype: false,
358 topics: false,
359 entropy: false,
360 license: false,
361 complexity: false,
362 api_surface: false,
363 #[cfg(all(feature = "halstead", feature = "content", feature = "walk"))]
364 halstead: false,
365 #[cfg(feature = "git")]
366 churn: true,
367 #[cfg(feature = "git")]
368 fingerprint: false,
369 },
370 },
371 PresetGridRow {
372 preset: PresetKind::Deep,
373 plan: PresetPlan {
374 assets: true,
375 deps: true,
376 todo: true,
377 dup: true,
378 imports: true,
379 git: true,
380 fun: false,
381 archetype: true,
382 topics: true,
383 entropy: true,
384 license: true,
385 complexity: true,
386 api_surface: true,
387 #[cfg(all(feature = "halstead", feature = "content", feature = "walk"))]
388 halstead: true,
389 #[cfg(feature = "git")]
390 churn: true,
391 #[cfg(feature = "git")]
392 fingerprint: true,
393 },
394 },
395 PresetGridRow {
396 preset: PresetKind::Fun,
397 plan: PresetPlan {
398 assets: false,
399 deps: false,
400 todo: false,
401 dup: false,
402 imports: false,
403 git: false,
404 fun: true,
405 archetype: false,
406 topics: false,
407 entropy: false,
408 license: false,
409 complexity: false,
410 api_surface: false,
411 #[cfg(all(feature = "halstead", feature = "content", feature = "walk"))]
412 halstead: false,
413 #[cfg(feature = "git")]
414 churn: false,
415 #[cfg(feature = "git")]
416 fingerprint: false,
417 },
418 },
419];
420
421pub fn preset_plan_for(preset: PresetKind) -> PresetPlan {
422 let mut i = 0;
423 while i < PRESET_GRID.len() {
424 if PRESET_GRID[i].preset == preset {
425 return PRESET_GRID[i].plan;
426 }
427 i += 1;
428 }
429 unreachable!();
430}
431
432pub fn preset_plan_for_name(name: &str) -> Option<PresetPlan> {
433 PresetKind::from_str(name).map(preset_plan_for)
434}
435
436#[derive(Debug, Clone, Copy, PartialEq, Eq)]
437pub enum DisabledFeature {
438 FileInventory,
439 TodoScan,
440 DuplicationScan,
441 NearDuplicateScan,
442 ImportScan,
443 GitMetrics,
444 EntropyProfiling,
445 LicenseRadar,
446 ComplexityAnalysis,
447 ApiSurfaceAnalysis,
448 Archetype,
449 Topics,
450 Fun,
451}
452
453impl DisabledFeature {
454 pub const fn warning(self) -> &'static str {
455 match self {
456 Self::FileInventory => "walk feature disabled; skipping file inventory",
457 Self::TodoScan => "content feature disabled; skipping TODO scan",
458 Self::DuplicationScan => "content feature disabled; skipping duplication scan",
459 Self::NearDuplicateScan => "content feature disabled; skipping near-dup scan",
460 Self::ImportScan => "content feature disabled; skipping import scan",
461 Self::GitMetrics => "git feature disabled; skipping git metrics",
462 Self::EntropyProfiling => "content/walk feature disabled; skipping entropy profiling",
463 Self::LicenseRadar => "content/walk feature disabled; skipping license radar",
464 Self::ComplexityAnalysis => {
465 "content/walk feature disabled; skipping complexity analysis"
466 }
467 Self::ApiSurfaceAnalysis => {
468 "content/walk feature disabled; skipping API surface analysis"
469 }
470 Self::Archetype => {
471 "archetype feature is disabled for analysis; set `archetype` feature to include archetype inference"
472 }
473 Self::Topics => {
474 "topics feature is disabled for analysis; set `topics` feature to include topic clouds"
475 }
476 Self::Fun => {
477 "fun feature is disabled for analysis; set `fun` feature to include eco-label output"
478 }
479 }
480 }
481}
482
483#[cfg(test)]
484mod tests {
485 use super::*;
486
487 #[test]
488 fn preset_table_covers_all_presets() {
489 for preset in PresetKind::all() {
490 assert!(PRESET_GRID.iter().any(|row| row.preset == *preset));
491 }
492 assert_eq!(PRESET_GRID.len(), PresetKind::all().len());
493 }
494
495 #[test]
496 fn preset_name_roundtrip_is_stable() {
497 for preset in PresetKind::all() {
498 let parsed = PresetKind::from_str(preset.as_str()).expect("preset should parse");
499 assert_eq!(parsed, *preset);
500 assert_eq!(
501 preset_plan_for_name(preset.as_str()),
502 Some(preset_plan_for(*preset))
503 );
504 }
505 }
506
507 #[test]
508 fn disabled_feature_messages_are_declared() {
509 assert!(!DisabledFeature::FileInventory.warning().is_empty());
510 assert!(!DisabledFeature::TodoScan.warning().is_empty());
511 assert!(!DisabledFeature::DuplicationScan.warning().is_empty());
512 assert!(!DisabledFeature::NearDuplicateScan.warning().is_empty());
513 assert!(!DisabledFeature::ImportScan.warning().is_empty());
514 assert!(!DisabledFeature::GitMetrics.warning().is_empty());
515 assert!(!DisabledFeature::EntropyProfiling.warning().is_empty());
516 assert!(!DisabledFeature::LicenseRadar.warning().is_empty());
517 assert!(!DisabledFeature::ComplexityAnalysis.warning().is_empty());
518 assert!(!DisabledFeature::ApiSurfaceAnalysis.warning().is_empty());
519 assert!(!DisabledFeature::Archetype.warning().is_empty());
520 assert!(!DisabledFeature::Topics.warning().is_empty());
521 assert!(!DisabledFeature::Fun.warning().is_empty());
522 }
523}