Skip to main content

tokmd_analysis_grid/
lib.rs

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}