1use std::str::FromStr;
2
3use ori_graphics::Color;
4use pest::{error::Error, iterators::Pair, Parser};
5use pest_derive::Parser;
6
7use crate::{
8 StyleAttribute, StyleAttributeKey, StyleAttributeValue, StyleClasses, StyleElement, StyleRule,
9 StyleSelector, StyleSelectors, StyleStates, StyleTransition, Stylesheet, Unit,
10};
11
12#[derive(Parser)]
13#[grammar = "style/grammar.pest"]
14pub struct StyleParser;
15
16pub type StyleParseError = Error<Rule>;
17pub type SelectorParseError = Error<Rule>;
18
19fn parse_number(pair: Pair<'_, Rule>) -> f32 {
20 pair.as_str().parse().unwrap()
21}
22
23fn parse_unit(pair: Pair<'_, Rule>) -> Unit {
24 let mut pairs = pair.into_inner();
25
26 let number_pair = pairs.next().unwrap();
27 let number = parse_number(number_pair);
28
29 match pairs.next().as_ref().map(Pair::as_rule) {
30 Some(Rule::Px) | None => Unit::Px(number),
31 Some(Rule::Pt) => Unit::Pt(number),
32 Some(Rule::Pc) => Unit::Pc(number),
33 Some(Rule::Vw) => Unit::Vw(number),
34 Some(Rule::Vh) => Unit::Vh(number),
35 Some(Rule::Em) => Unit::Em(number),
36 _ => unreachable!(),
37 }
38}
39
40fn parse_color(pair: Pair<'_, Rule>) -> Color {
41 let pair = pair.into_inner().next().unwrap();
42
43 match pair.as_rule() {
44 Rule::HexColor => Color::hex(pair.as_str()),
45 Rule::RgbColor => {
46 let mut iter = pair.into_inner();
47
48 let r = iter.next().unwrap().as_str().parse().unwrap();
49 let g = iter.next().unwrap().as_str().parse().unwrap();
50 let b = iter.next().unwrap().as_str().parse().unwrap();
51
52 Color::rgb(r, g, b)
53 }
54 Rule::RgbaColor => {
55 let mut iter = pair.into_inner();
56
57 let r = iter.next().unwrap().as_str().parse().unwrap();
58 let g = iter.next().unwrap().as_str().parse().unwrap();
59 let b = iter.next().unwrap().as_str().parse().unwrap();
60 let a = iter.next().unwrap().as_str().parse().unwrap();
61
62 Color::rgba(r, g, b, a)
63 }
64 _ => unreachable!(),
65 }
66}
67
68fn parse_transition(pair: Option<Pair<'_, Rule>>) -> Option<StyleTransition> {
69 Some(StyleTransition::new(parse_number(pair?)))
70}
71
72fn parse_value(pair: Pair<'_, Rule>) -> (StyleAttributeValue, Option<StyleTransition>) {
73 let mut pairs = pair.into_inner();
74
75 let value = pairs.next().unwrap();
76 let transition = parse_transition(pairs.next());
77 let value = match value.as_rule() {
78 Rule::String => {
79 let value = &value.as_str()[1..value.as_str().len() - 1];
80 StyleAttributeValue::String(value.to_string())
81 }
82 Rule::Enum => StyleAttributeValue::Enum(value.as_str().to_string()),
83 Rule::Unit => StyleAttributeValue::Unit(parse_unit(value)),
84 Rule::Color => StyleAttributeValue::Color(parse_color(value)),
85 _ => unreachable!(),
86 };
87
88 (value, transition)
89}
90
91fn parse_class(pair: Pair<'_, Rule>) -> String {
92 pair.into_inner().as_str().to_string()
93}
94
95fn parse_state(pair: Pair<'_, Rule>) -> String {
96 pair.into_inner().as_str().to_string()
97}
98
99fn parse_selector(pair: Pair<'_, Rule>) -> StyleSelector {
100 let mut pairs = pair.into_inner();
101
102 let mut element_pairs = pairs.next().unwrap().into_inner();
103 let element_pair = element_pairs.next().unwrap();
104 let element = match element_pair.as_rule() {
105 Rule::Identifier => Some(StyleElement::new(element_pair.as_str())),
106 Rule::Wildcard => None,
107 _ => unreachable!(),
108 };
109
110 let mut selector = StyleSelector {
111 element,
112 classes: StyleClasses::new(),
113 states: StyleStates::new(),
114 };
115
116 for pair in element_pairs {
117 match pair.as_rule() {
118 Rule::State => {
119 selector.states.push(parse_state(pair));
120 }
121 _ => unreachable!(),
122 }
123 }
124
125 for pair in pairs {
126 match pair.as_rule() {
127 Rule::Class => {
128 selector.classes.push(parse_class(pair));
129 }
130 _ => unreachable!(),
131 }
132 }
133
134 selector
135}
136
137fn parse_selectors(pair: Pair<'_, Rule>) -> StyleSelectors {
138 let mut selectors = StyleSelectors::new();
139
140 for pair in pair.into_inner() {
141 match pair.as_rule() {
142 Rule::Selector => {
143 selectors.push(parse_selector(pair));
144 }
145 _ => unreachable!(),
146 }
147 }
148
149 selectors
150}
151
152fn parse_attribute(pair: Pair<'_, Rule>) -> StyleAttribute {
153 let mut iter = pair.into_inner();
154
155 let key = iter.next().unwrap().as_str();
156 let (value, transition) = parse_value(iter.next().unwrap());
157
158 StyleAttribute {
159 key: StyleAttributeKey::new(key),
160 value,
161 transition,
162 }
163}
164
165fn parse_style_rule(pair: Pair<'_, Rule>) -> StyleRule {
166 let mut iter = pair.into_inner();
167
168 let selector = parse_selectors(iter.next().unwrap());
169 let mut rule = StyleRule::new(selector);
170
171 for pair in iter {
172 match pair.as_rule() {
173 Rule::Attribute => {
174 rule.attributes.add(parse_attribute(pair));
175 }
176 _ => unreachable!(),
177 }
178 }
179
180 rule
181}
182
183fn parse_style(input: &str) -> Result<Stylesheet, Error<Rule>> {
184 let pairs = StyleParser::parse(Rule::Style, input)?.next().unwrap();
185 let mut style = Stylesheet::new();
186
187 for pair in pairs.into_inner() {
188 match pair.as_rule() {
189 Rule::StyleRule => {
190 style.add_rule(parse_style_rule(pair));
191 }
192 Rule::EOI => break,
193 _ => unreachable!(),
194 }
195 }
196
197 Ok(style)
198}
199
200impl FromStr for Stylesheet {
201 type Err = StyleParseError;
202
203 fn from_str(input: &str) -> Result<Self, Self::Err> {
204 parse_style(input)
205 }
206}
207
208impl FromStr for StyleSelectors {
209 type Err = SelectorParseError;
210
211 fn from_str(input: &str) -> Result<Self, Self::Err> {
212 let pair = StyleParser::parse(Rule::Selector, input)?.next().unwrap();
213 Ok(parse_selectors(pair))
214 }
215}
216
217#[cfg(test)]
218mod tests {
219 use super::*;
220
221 #[test]
222 fn it_works() {
223 parse_style(include_str!("test.css")).unwrap();
224 }
225}