1use crate::css_properties::{CssProperty, CssPropertyType};
3use std::fmt;
4
5#[derive(Debug, Default, PartialEq, PartialOrd, Clone)]
8pub struct Css {
9 pub stylesheets: Vec<Stylesheet>,
13}
14
15impl Css {
16 pub const fn new(stylesheets: Vec<Stylesheet>) -> Self {
17 Self { stylesheets }
18 }
19}
20
21#[derive(Debug, Default, PartialEq, PartialOrd, Clone)]
22pub struct Stylesheet {
23 pub rules: Vec<CssRuleBlock>,
25}
26
27impl Stylesheet {
28 pub const fn new(rules: Vec<CssRuleBlock>) -> Self {
29 Self { rules }
30 }
31}
32
33impl From<Vec<CssRuleBlock>> for Stylesheet {
34 fn from(rules: Vec<CssRuleBlock>) -> Self {
35 Self { rules }
36 }
37}
38
39#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
41pub enum CssDeclaration {
42 Static(CssProperty),
44 Dynamic(DynamicCssProperty),
46}
47
48impl CssDeclaration {
49
50 pub const fn new_static(prop: CssProperty) -> Self {
51 CssDeclaration::Static(prop)
52 }
53
54 pub const fn new_dynamic(prop: DynamicCssProperty) -> Self {
55 CssDeclaration::Dynamic(prop)
56 }
57
58 pub fn get_type(&self) -> CssPropertyType {
60 use self::CssDeclaration::*;
61 match self {
62 Static(s) => s.get_type(),
63 Dynamic(d) => d.default_value.get_type(),
64 }
65 }
66
67 pub fn is_inheritable(&self) -> bool {
70 use self::CssDeclaration::*;
71 match self {
72 Static(s) => s.get_type().is_inheritable(),
73 Dynamic(d) => d.is_inheritable(),
74 }
75 }
76
77 pub fn can_trigger_relayout(&self) -> bool {
80 use self::CssDeclaration::*;
81 match self {
82 Static(s) => s.get_type().can_trigger_relayout(),
83 Dynamic(d) => d.can_trigger_relayout(),
84 }
85 }
86
87 pub fn to_str(&self) -> String {
88 use self::CssDeclaration::*;
89 match self {
90 Static(s) => format!("{:?}", s),
91 Dynamic(d) => format!("var(--{}, {:?})", d.dynamic_id, d.default_value),
92 }
93 }
94}
95
96#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
117pub struct DynamicCssProperty {
118 pub dynamic_id: String,
120 pub default_value: CssProperty,
122}
123
124#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
125pub enum CssPropertyValue<T> {
126 Auto,
127 None,
128 Initial,
129 Inherit,
130 Exact(T),
131}
132
133impl<T: fmt::Debug> fmt::Debug for CssPropertyValue<T> {
134 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
135 use self::CssPropertyValue::*;
136 match self {
137 Auto => write!(f, "auto"),
138 None => write!(f, "none"),
139 Initial => write!(f, "initial"),
140 Inherit => write!(f, "inherit"),
141 Exact(e) => write!(f, "{:?}", e),
142 }
143 }
144}
145
146impl<T: fmt::Display> fmt::Display for CssPropertyValue<T> {
147 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
148 use self::CssPropertyValue::*;
149 match self {
150 Auto => write!(f, "auto"),
151 None => write!(f, "none"),
152 Initial => write!(f, "initial"),
153 Inherit => write!(f, "inherit"),
154 Exact(e) => write!(f, "{}", e),
155 }
156 }
157}
158
159impl<T> From<T> for CssPropertyValue<T> {
160 fn from(c: T) -> Self { CssPropertyValue::Exact(c) }
161}
162
163impl<T> CssPropertyValue<T> {
164
165 #[inline]
167 pub fn map_property<F: Fn(T) -> U, U>(self, map_fn: F) -> CssPropertyValue<U> {
168 match self {
169 CssPropertyValue::Exact(c) => CssPropertyValue::Exact(map_fn(c)),
170 CssPropertyValue::Auto => CssPropertyValue::Auto,
171 CssPropertyValue::None => CssPropertyValue::None,
172 CssPropertyValue::Initial => CssPropertyValue::Initial,
173 CssPropertyValue::Inherit => CssPropertyValue::Inherit,
174 }
175 }
176
177 #[inline]
178 pub fn get_property(&self) -> Option<&T> {
179 match self {
180 CssPropertyValue::Exact(c) => Some(c),
181 _ => None,
182 }
183 }
184
185 #[inline]
186 pub fn get_property_owned(self) -> Option<T> {
187 match self {
188 CssPropertyValue::Exact(c) => Some(c),
189 _ => None,
190 }
191 }
192
193 #[inline]
194 pub fn is_auto(&self) -> bool {
195 match self {
196 CssPropertyValue::Auto => true,
197 _ => false,
198 }
199 }
200
201 #[inline]
202 pub fn is_none(&self) -> bool {
203 match self {
204 CssPropertyValue::None => true,
205 _ => false,
206 }
207 }
208
209 #[inline]
210 pub fn is_initial(&self) -> bool {
211 match self {
212 CssPropertyValue::Initial => true,
213 _ => false,
214 }
215 }
216
217 #[inline]
218 pub fn is_inherit(&self) -> bool {
219 match self {
220 CssPropertyValue::Inherit => true,
221 _ => false,
222 }
223 }
224}
225
226impl<T: Default> CssPropertyValue<T> {
227 #[inline]
228 pub fn get_property_or_default(self) -> Option<T> {
229 match self {
230 CssPropertyValue::Auto | CssPropertyValue::Initial => Some(T::default()),
231 CssPropertyValue::Exact(c) => Some(c),
232 CssPropertyValue::None | CssPropertyValue::Inherit => None,
233 }
234 }
235}
236
237impl<T: Default> Default for CssPropertyValue<T> {
238 #[inline]
239 fn default() -> Self {
240 CssPropertyValue::Exact(T::default())
241 }
242}
243
244impl DynamicCssProperty {
245 pub fn is_inheritable(&self) -> bool {
246 false
250 }
251
252 pub fn can_trigger_relayout(&self) -> bool {
253 self.default_value.get_type().can_trigger_relayout()
254 }
255}
256
257#[derive(Debug, Clone, PartialOrd, PartialEq)]
260pub struct CssRuleBlock {
261 pub path: CssPath,
263 pub declarations: Vec<CssDeclaration>,
266}
267
268impl CssRuleBlock {
269 pub const fn new(path: CssPath, declarations: Vec<CssDeclaration>) -> Self {
270 Self {
271 path,
272 declarations,
273 }
274 }
275}
276
277pub type CssContentGroup<'a> = Vec<&'a CssPathSelector>;
278
279#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
282pub enum NodeTypePath {
283 Body,
284 Div,
285 P,
286 Img,
287 Texture,
288 IFrame,
289}
290
291#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
292pub enum NodeTypePathParseError<'a> {
293 Invalid(&'a str),
294}
295
296impl<'a> fmt::Display for NodeTypePathParseError<'a> {
297 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
298 match &self {
299 NodeTypePathParseError::Invalid(e) => write!(f, "Invalid node type: {}", e),
300 }
301 }
302}
303
304const NODE_TYPE_PATH_MAP: [(NodeTypePath, &'static str); 6] = [
305 (NodeTypePath::Body, "body"),
306 (NodeTypePath::Div, "div"),
307 (NodeTypePath::P, "p"),
308 (NodeTypePath::Img, "img"),
309 (NodeTypePath::Texture, "texture"),
310 (NodeTypePath::IFrame, "iframe"),
311];
312
313impl NodeTypePath {
315 pub fn from_str(css_key: &str) -> Result<Self, NodeTypePathParseError> {
316 NODE_TYPE_PATH_MAP.iter()
317 .find(|(_, k)| css_key == *k)
318 .and_then(|(v, _)| Some(*v))
319 .ok_or(NodeTypePathParseError::Invalid(css_key))
320 }
321}
322
323impl fmt::Display for NodeTypePath {
324 fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
325 let display_string = NODE_TYPE_PATH_MAP.iter()
326 .find(|(v, _)| *self == *v)
327 .and_then(|(_, k)| Some(*k))
328 .unwrap();
329
330 write!(f, "{}", display_string)?;
331 Ok(())
332 }
333}
334
335#[derive(Clone, Hash, Default, PartialEq, Eq, PartialOrd, Ord)]
347pub struct CssPath {
348 pub selectors: Vec<CssPathSelector>,
349}
350
351impl CssPath {
352 pub const fn new(selectors: Vec<CssPathSelector>) -> Self {
353 Self { selectors }
354 }
355}
356
357impl fmt::Display for CssPath {
358 fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
359 for selector in &self.selectors {
360 write!(f, "{}", selector)?;
361 }
362 Ok(())
363 }
364}
365
366impl fmt::Debug for CssPath {
367 fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
368 write!(f, "{}", self)
369 }
370}
371
372#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
373pub enum CssPathSelector {
374 Global,
376 Type(NodeTypePath),
378 Class(String),
380 Id(String),
382 PseudoSelector(CssPathPseudoSelector),
384 DirectChildren,
386 Children,
388}
389
390impl Default for CssPathSelector {
391 fn default() -> Self {
392 CssPathSelector::Global
393 }
394}
395
396impl fmt::Display for CssPathSelector {
397 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
398 use self::CssPathSelector::*;
399 match &self {
400 Global => write!(f, "*"),
401 Type(n) => write!(f, "{}", n),
402 Class(c) => write!(f, ".{}", c),
403 Id(i) => write!(f, "#{}", i),
404 PseudoSelector(p) => write!(f, ":{}", p),
405 DirectChildren => write!(f, ">"),
406 Children => write!(f, " "),
407 }
408 }
409}
410
411#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
412pub enum CssPathPseudoSelector {
413 First,
415 Last,
417 NthChild(CssNthChildSelector),
419 Hover,
421 Active,
423 Focus,
425}
426
427#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
428pub enum CssNthChildSelector {
429 Number(usize),
430 Even,
431 Odd,
432 Pattern { repeat: usize, offset: usize },
433}
434
435impl fmt::Display for CssNthChildSelector {
436 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
437 use self::CssNthChildSelector::*;
438 match &self {
439 Number(u) => write!(f, "{}", u),
440 Even => write!(f, "even"),
441 Odd => write!(f, "odd"),
442 Pattern { repeat, offset } => write!(f, "{}n + {}", repeat, offset),
443 }
444 }
445}
446
447impl fmt::Display for CssPathPseudoSelector {
448 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
449 use self::CssPathPseudoSelector::*;
450 match &self {
451 First => write!(f, "first"),
452 Last => write!(f, "last"),
453 NthChild(u) => write!(f, "nth-child({})", u),
454 Hover => write!(f, "hover"),
455 Active => write!(f, "active"),
456 Focus => write!(f, "focus"),
457 }
458 }
459}
460
461impl Css {
462
463 pub fn empty() -> Self {
465 Default::default()
466 }
467
468 pub fn append(&mut self, css: Self) {
469 for stylesheet in css.stylesheets {
470 self.append_stylesheet(stylesheet);
471 }
472 }
473
474 pub fn append_stylesheet(&mut self, styles: Stylesheet) {
475 self.stylesheets.push(styles);
476 }
477
478 pub fn sort_by_specificity(&mut self) {
479 for stylesheet in &mut self.stylesheets {
480 stylesheet.sort_by_specificity()
481 }
482 }
483
484 pub fn rules<'a>(&'a self) -> RuleIterator<'a> {
485 RuleIterator {
486 current_stylesheet: 0,
487 current_rule: 0,
488 css: self,
489 }
490 }
491}
492
493pub struct RuleIterator<'a> {
494 current_stylesheet: usize,
495 current_rule: usize,
496 css: &'a Css,
497}
498
499impl<'a> Iterator for RuleIterator<'a> {
500 type Item = &'a CssRuleBlock;
501 fn next(&mut self) -> Option<&'a CssRuleBlock> {
502 let current_stylesheet = self.css.stylesheets.get(self.current_stylesheet)?;
503 match current_stylesheet.rules.get(self.current_rule) {
504 Some(s) => {
505 self.current_rule += 1;
506 Some(s)
507 },
508 None => {
509 self.current_rule = 0;
510 self.current_stylesheet += 1;
511 self.next()
512 }
513 }
514 }
515}
516
517
518impl Stylesheet {
519
520 pub fn empty() -> Self {
522 Default::default()
523 }
524
525 pub fn sort_by_specificity(&mut self) {
528 self.rules.sort_by(|a, b| get_specificity(&a.path).cmp(&get_specificity(&b.path)));
529 }
530}
531
532fn get_specificity(path: &CssPath) -> (usize, usize, usize, usize) {
535 let id_count = path.selectors.iter().filter(|x| if let CssPathSelector::Id(_) = x { true } else { false }).count();
536 let class_count = path.selectors.iter().filter(|x| if let CssPathSelector::Class(_) = x { true } else { false }).count();
537 let div_count = path.selectors.iter().filter(|x| if let CssPathSelector::Type(_) = x { true } else { false }).count();
538 (id_count, class_count, div_count, path.selectors.len())
539}
540
541#[test]
542fn test_specificity() {
543 use self::CssPathSelector::*;
544 assert_eq!(get_specificity(&CssPath { selectors: vec![Id("hello".into())] }), (1, 0, 0, 1));
545 assert_eq!(get_specificity(&CssPath { selectors: vec![Class("hello".into())] }), (0, 1, 0, 1));
546 assert_eq!(get_specificity(&CssPath { selectors: vec![Type(NodeTypePath::Div)] }), (0, 0, 1, 1));
547 assert_eq!(get_specificity(&CssPath { selectors: vec![Id("hello".into()), Type(NodeTypePath::Div)] }), (1, 0, 1, 2));
548}
549
550#[test]
552fn test_specificity_sort() {
553 use self::CssPathSelector::*;
554 use crate::NodeTypePath::*;
555
556 let mut input_style = Stylesheet {
557 rules: vec![
558 CssRuleBlock { path: CssPath { selectors: vec![Global] }, declarations: Vec::new() },
560 CssRuleBlock { path: CssPath { selectors: vec![Global, Type(Div), Class("my_class".into()), Id("my_id".into())] }, declarations: Vec::new() },
561 CssRuleBlock { path: CssPath { selectors: vec![Global, Type(Div), Id("my_id".into())] }, declarations: Vec::new() },
562 CssRuleBlock { path: CssPath { selectors: vec![Global, Id("my_id".into())] }, declarations: Vec::new() },
563 CssRuleBlock { path: CssPath { selectors: vec![Type(Div), Class("my_class".into()), Class("specific".into()), Id("my_id".into())] }, declarations: Vec::new() },
564 ],
565 };
566
567 input_style.sort_by_specificity();
568
569 let expected_style = Stylesheet {
570 rules: vec![
571 CssRuleBlock { path: CssPath { selectors: vec![Global] }, declarations: Vec::new() },
573 CssRuleBlock { path: CssPath { selectors: vec![Global, Id("my_id".into())] }, declarations: Vec::new() },
574 CssRuleBlock { path: CssPath { selectors: vec![Global, Type(Div), Id("my_id".into())] }, declarations: Vec::new() },
575 CssRuleBlock { path: CssPath { selectors: vec![Global, Type(Div), Class("my_class".into()), Id("my_id".into())] }, declarations: Vec::new() },
576 CssRuleBlock { path: CssPath { selectors: vec![Type(Div), Class("my_class".into()), Class("specific".into()), Id("my_id".into())] }, declarations: Vec::new() },
577 ],
578 };
579
580 assert_eq!(input_style, expected_style);
581}