Skip to main content

fallow_output/
audit_render.rs

1use crate::CssAnalyticsSummary;
2
3#[derive(Clone, Copy, Debug, Eq, PartialEq)]
4pub enum AuditDisplaySeverity {
5    Off,
6    Warn,
7    Error,
8}
9
10#[derive(Clone, Copy, Debug, Eq, PartialEq)]
11pub enum AuditDisplayGate {
12    NewOnly,
13    All,
14}
15
16#[derive(Clone, Copy, Debug, Eq, PartialEq)]
17pub struct AuditStylingContextLabelInput<'a> {
18    pub severity: AuditDisplaySeverity,
19    pub rule: &'a str,
20    pub base_state: Option<&'a str>,
21    pub gate: AuditDisplayGate,
22}
23
24pub fn styling_candidate_count(summary: &CssAnalyticsSummary) -> u32 {
25    [
26        summary.tailwind_arbitrary_values,
27        summary.duplicate_declaration_blocks,
28        summary.unreferenced_css_classes,
29        summary.unused_theme_tokens,
30        summary.unused_font_faces,
31        summary.unused_property_registrations,
32        summary.unused_layers,
33        summary.scoped_unused_classes,
34        summary.keyframes_unreferenced,
35        summary.keyframes_undefined,
36        summary.unresolved_class_references,
37    ]
38    .into_iter()
39    .fold(0u32, u32::saturating_add)
40}
41
42pub fn styling_audit_context_label(input: AuditStylingContextLabelInput<'_>) -> String {
43    let severity_label = match input.severity {
44        AuditDisplaySeverity::Off => "off",
45        AuditDisplaySeverity::Warn => "warn",
46        AuditDisplaySeverity::Error => "error",
47    };
48    let prefix = match (input.severity, input.gate, input.base_state) {
49        (AuditDisplaySeverity::Error, AuditDisplayGate::NewOnly, Some(state))
50            if state.starts_with("inherited") =>
51        {
52            "not gated"
53        }
54        (AuditDisplaySeverity::Error, _, _) => "gated",
55        _ => "advisory",
56    };
57    match input.base_state {
58        Some(state) => format!("({prefix}: {}={severity_label}, {state})", input.rule),
59        None => format!("({prefix}: {}={severity_label})", input.rule),
60    }
61}
62
63#[cfg(test)]
64mod tests {
65    use super::*;
66
67    #[test]
68    fn styling_audit_context_label_explains_gate_state() {
69        assert_eq!(
70            styling_audit_context_label(AuditStylingContextLabelInput {
71                severity: AuditDisplaySeverity::Error,
72                rule: "rules.css-selector-complexity",
73                base_state: Some("introduced design-system drift since HEAD"),
74                gate: AuditDisplayGate::NewOnly,
75            }),
76            "(gated: rules.css-selector-complexity=error, introduced design-system drift since HEAD)"
77        );
78        assert_eq!(
79            styling_audit_context_label(AuditStylingContextLabelInput {
80                severity: AuditDisplaySeverity::Error,
81                rule: "rules.css-selector-complexity",
82                base_state: Some("inherited styling debt from HEAD"),
83                gate: AuditDisplayGate::NewOnly,
84            }),
85            "(not gated: rules.css-selector-complexity=error, inherited styling debt from HEAD)"
86        );
87        assert_eq!(
88            styling_audit_context_label(AuditStylingContextLabelInput {
89                severity: AuditDisplaySeverity::Warn,
90                rule: "rules.css-selector-complexity",
91                base_state: None,
92                gate: AuditDisplayGate::All,
93            }),
94            "(advisory: rules.css-selector-complexity=warn)"
95        );
96    }
97
98    #[test]
99    fn styling_candidate_count_saturates_descriptive_css_counts() {
100        let summary = CssAnalyticsSummary {
101            tailwind_arbitrary_values: 2,
102            duplicate_declaration_blocks: 3,
103            unresolved_class_references: 5,
104            ..CssAnalyticsSummary::default()
105        };
106
107        assert_eq!(styling_candidate_count(&summary), 10);
108    }
109}