1pub mod a11y;
12pub mod baseline;
13pub mod color;
14pub mod edge;
15pub mod opacity;
16pub mod radius;
17pub mod shadow;
18pub mod sibling;
19pub mod spacing;
20pub mod type_;
21pub mod z;
22
23mod util;
24
25use crate::config::Config;
26use crate::report::{Severity, ViolationSink};
27use crate::snapshot::SnapshotCtx;
28
29#[derive(Debug, Clone, PartialEq, Eq)]
31pub struct RuleMetadata {
32 pub id: String,
34 pub summary: String,
36 pub doc_url: String,
38 pub default_severity: Severity,
40}
41
42impl RuleMetadata {
43 #[must_use]
45 pub fn from_rule(rule: &dyn Rule) -> Self {
46 Self {
47 id: rule.id().to_owned(),
48 summary: rule.summary().to_owned(),
49 doc_url: rule.doc_url(),
50 default_severity: rule.default_severity(),
51 }
52 }
53}
54
55pub trait Rule: Send + Sync {
65 fn id(&self) -> &'static str;
67
68 fn default_severity(&self) -> Severity;
70
71 fn summary(&self) -> &'static str;
73
74 fn doc_url(&self) -> String {
76 let slug = self.id().replace('/', "-");
77 format!("https://plumb.aramhammoudeh.com/rules/{slug}")
78 }
79
80 fn check(&self, ctx: &SnapshotCtx<'_>, config: &Config, sink: &mut ViolationSink<'_>);
82}
83
84#[must_use]
88pub fn register_builtin() -> Vec<Box<dyn Rule>> {
89 vec![
90 Box::new(a11y::touch_target::TouchTarget),
91 Box::new(baseline::rhythm::Rhythm),
92 Box::new(color::contrast_aa::ContrastAa),
93 Box::new(color::palette_conformance::PaletteConformance),
94 Box::new(edge::near_alignment::NearAlignment),
95 Box::new(opacity::scale_conformance::ScaleConformance),
96 Box::new(radius::scale_conformance::ScaleConformance),
97 Box::new(shadow::scale_conformance::ScaleConformance),
98 Box::new(sibling::height_consistency::HeightConsistency),
99 Box::new(sibling::padding_consistency::PaddingConsistency),
100 Box::new(spacing::grid_conformance::GridConformance),
101 Box::new(spacing::scale_conformance::ScaleConformance),
102 Box::new(type_::family_conformance::FamilyConformance),
103 Box::new(type_::scale_conformance::ScaleConformance),
104 Box::new(type_::weight_conformance::WeightConformance),
105 Box::new(z::scale_conformance::ScaleConformance),
106 ]
107}
108
109#[must_use]
111pub fn builtin_rule_metadata() -> Vec<RuleMetadata> {
112 let mut metadata: Vec<RuleMetadata> = register_builtin()
113 .iter()
114 .map(|rule| RuleMetadata::from_rule(rule.as_ref()))
115 .collect();
116 metadata.sort_by(|a, b| a.id.cmp(&b.id));
117 metadata
118}