fallow_output/
audit_render.rs1use 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}