swc_css_lints/rules/
color_hex_length.rs1use serde::{Deserialize, Serialize};
2use swc_css_ast::*;
3use swc_css_visit::{Visit, VisitWith};
4
5use crate::rule::{visitor_rule, LintRule, LintRuleContext};
6
7pub type ColorHexLengthConfig = Option<HexForm>;
8
9#[derive(Debug, Clone, Serialize, Deserialize)]
10#[serde(rename_all = "camelCase")]
11pub enum HexForm {
12 Long,
13 Short,
14}
15
16impl Default for HexForm {
17 fn default() -> Self {
18 Self::Long
19 }
20}
21
22pub fn color_hex_length(ctx: LintRuleContext<ColorHexLengthConfig>) -> Box<dyn LintRule> {
23 let form = ctx.config().clone().unwrap_or_default();
24 visitor_rule(ctx.reaction(), ColorHexLength { ctx, form })
25}
26
27#[derive(Debug, Default)]
28struct ColorHexLength {
29 ctx: LintRuleContext<ColorHexLengthConfig>,
30 form: HexForm,
31}
32
33impl ColorHexLength {
34 fn build_message(&self, actual: &str, expected: &str) -> String {
35 format!("Hex color value '#{actual}' should be written into: '#{expected}'.")
36 }
37}
38
39impl Visit for ColorHexLength {
40 fn visit_hex_color(&mut self, hex_color: &HexColor) {
41 match self.form {
42 HexForm::Long => {
43 if let Some(lengthened) = lengthen(&hex_color.value) {
44 let message = self.build_message(&hex_color.value, &lengthened);
45 self.ctx.report(hex_color, message);
46 }
47 }
48 HexForm::Short => {
49 if let Some(shortened) = shorten(&hex_color.value) {
50 let message = self.build_message(&hex_color.value, &shortened);
51 self.ctx.report(hex_color, message);
52 }
53 }
54 }
55
56 hex_color.visit_children_with(self);
57 }
58}
59
60fn shorten(hex: &str) -> Option<String> {
61 let chars = hex.chars().collect::<Vec<_>>();
62 match &*chars {
63 [c1, c2, c3, c4, c5, c6] if c1 == c2 && c3 == c4 && c5 == c6 => {
64 Some(format!("{c1}{c3}{c5}"))
65 }
66 [c1, c2, c3, c4, c5, c6, c7, c8] if c1 == c2 && c3 == c4 && c5 == c6 && c7 == c8 => {
67 Some(format!("{c1}{c3}{c5}{c7}"))
68 }
69 _ => None,
70 }
71}
72
73fn lengthen(hex: &str) -> Option<String> {
74 let chars = hex.chars().collect::<Vec<_>>();
75 match &*chars {
76 [c1, c2, c3] => Some(format!("{c1}{c1}{c2}{c2}{c3}{c3}")),
77 [c1, c2, c3, c4] => Some(format!("{c1}{c1}{c2}{c2}{c3}{c3}{c4}{c4}")),
78 _ => None,
79 }
80}