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