serval/
css.rs

1pub mod parser;
2use lazy_static::*;
3
4// use ordered_float::OrderedFloat;
5use std::collections::BTreeSet;
6
7// https://limpet.net/mbrubeck/2014/08/13/toy-layout-engine-3-css.html
8
9// pub type Num = OrderedFloat<f32>;
10// pub type Num = f32;
11
12#[derive(Debug, PartialEq)]
13pub struct Stylesheet {
14    pub rules: Vec<Rule>,
15}
16
17#[derive(Debug, PartialEq)]
18pub struct Rule {
19    // TODO: Assert selectors should br sorted by their specifities
20    // pub selectors: Vec<Selector>,
21    pub selectors: SortedSelectors,
22    pub declarations: Vec<Declaration>,
23}
24
25#[derive(Debug, PartialEq)]
26pub struct SortedSelectors {
27    pub selectors: Vec<Selector>,
28}
29
30impl SortedSelectors {
31    pub fn new(mut selectors: Vec<Selector>) -> SortedSelectors {
32        selectors.sort_by(|a, b| b.specifity().cmp(&a.specifity()));
33        SortedSelectors { selectors }
34    }
35}
36
37#[derive(Debug, Clone, PartialEq)]
38pub enum Selector {
39    Simple(SimpleSelector),
40}
41
42impl Selector {
43    #[cfg(test)]
44    pub(crate) fn tag(tag_name: &str) -> Selector {
45        Selector::Simple(SimpleSelector::tag(tag_name))
46    }
47
48    #[cfg(test)]
49    pub(crate) fn id(id: &str) -> Selector {
50        Selector::Simple(SimpleSelector::id(id))
51    }
52
53    #[cfg(test)]
54    pub(crate) fn class(classes: &[&str]) -> Selector {
55        Selector::Simple(SimpleSelector::class(classes))
56    }
57
58    #[cfg(test)]
59    pub(crate) fn universal() -> Selector {
60        Selector::Simple(Default::default())
61    }
62}
63
64#[derive(Debug, Clone, Default, PartialEq)]
65pub struct SimpleSelector {
66    pub tag_name: Option<String>,
67    pub id: Option<String>,
68    pub classes: BTreeSet<String>,
69}
70
71impl SimpleSelector {
72    #[cfg(test)]
73    pub(crate) fn tag(tag_name: &str) -> SimpleSelector {
74        SimpleSelector {
75            tag_name: Some(tag_name.to_string()),
76            ..Default::default()
77        }
78    }
79
80    #[cfg(test)]
81    pub(crate) fn id(id: &str) -> SimpleSelector {
82        SimpleSelector {
83            id: Some(id.to_string()),
84            ..Default::default()
85        }
86    }
87
88    #[cfg(test)]
89    pub(crate) fn class(classes: &[&str]) -> SimpleSelector {
90        SimpleSelector {
91            classes: classes.iter().map(|s| s.to_string()).collect(),
92            ..Default::default()
93        }
94    }
95
96    #[cfg(test)]
97    pub(crate) fn universal() -> SimpleSelector {
98        Default::default()
99    }
100}
101
102#[derive(Debug, Clone, PartialEq)]
103pub struct Declaration {
104    pub name: String,
105    pub value: Value,
106}
107
108impl Declaration {
109    #[cfg(test)]
110    pub(crate) fn color(rgb: Rgb) -> Declaration {
111        Declaration {
112            name: "color".to_string(),
113            value: Value::color(rgb),
114        }
115    }
116}
117
118#[derive(Debug, Clone, PartialEq)]
119pub enum Value {
120    Keyword(String),
121    Length(f32, Unit),
122    ColorValue(Color),
123}
124
125impl Value {
126    #[cfg(test)]
127    pub(crate) fn color((r, g, b): Rgb) -> Value {
128        Value::ColorValue(Color { r, g, b })
129    }
130
131    pub fn keyword_auto() -> &'static Value {
132        lazy_static! {
133            static ref AUTO: Value = Value::Keyword("auto".to_string());
134        }
135        &AUTO
136    }
137
138    pub fn length_zero() -> &'static Value {
139        lazy_static! {
140            static ref LENGTH_ZERO: Value = Value::Length(0.0, Unit::Px);
141        }
142        &LENGTH_ZERO
143    }
144
145    pub fn to_px(&self) -> f32 {
146        match *self {
147            Value::Length(px, Unit::Px) => px,
148            _ => 0.0,
149        }
150    }
151}
152
153#[derive(Debug, Clone, PartialEq, Eq)]
154pub enum Unit {
155    Px,
156}
157
158pub type Rgb = (u8, u8, u8);
159
160#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)]
161pub struct Color {
162    pub r: u8,
163    pub g: u8,
164    pub b: u8,
165    // pub a: u8, // alpha
166}
167
168pub type Specifity = (usize, usize, usize);
169
170impl Selector {
171    pub fn specifity(&self) -> Specifity {
172        let Selector::Simple(ref simple) = *self;
173        let a = if simple.id.is_some() { 1 } else { 0 };
174        let b = simple.classes.len();
175        let c = if simple.tag_name.is_some() { 1 } else { 0 };
176        (a, b, c)
177    }
178}
179
180#[cfg(test)]
181mod test {
182    use super::*;
183    use maplit::btreeset;
184
185    #[test]
186    fn sorted_selectors_test() {
187        let selectors = vec![
188            Selector::tag("div"),
189            Selector::Simple(Default::default()),
190            Selector::id("foo"),
191            Selector::Simple(SimpleSelector {
192                classes: btreeset! { "class1".to_string(), "class2".to_string() },
193                ..Default::default()
194            }),
195            Selector::Simple(SimpleSelector {
196                tag_name: Some("div".to_string()),
197                classes: btreeset! { "class2".to_string() },
198                ..Default::default()
199            }),
200            Selector::class(&["class1"]),
201        ];
202
203        let sorted_selectors = SortedSelectors::new(selectors.clone());
204        assert_eq!(
205            sorted_selectors.selectors,
206            vec![
207                Selector::id("foo"),
208                Selector::Simple(SimpleSelector {
209                    classes: btreeset! { "class1".to_string(), "class2".to_string() },
210                    ..Default::default()
211                }),
212                Selector::Simple(SimpleSelector {
213                    tag_name: Some("div".to_string()),
214                    classes: btreeset! { "class2".to_string() },
215                    ..Default::default()
216                }),
217                Selector::class(&["class1"]),
218                Selector::tag("div"),
219                Selector::Simple(Default::default()),
220            ]
221        );
222    }
223
224}