1use std::{collections::HashMap, fmt::Display, fs, path::Path, str::FromStr, sync::Arc};
2
3use bumpalo::{Bump, collections::Vec as BumpVec};
4use cssparser::{Parser, ParserInput, RuleBodyParser};
5use log::error;
6use parking_lot::{RwLock, RwLockReadGuard};
7use qfilter::Filter;
8use smallvec::SmallVec;
9
10use crate::{
11 css::{self, parser::*, properties::*, style::*},
12 interner::StrId,
13 prelude::*,
14 tree::Node,
15 util::ResourceInfo,
16};
17
18#[derive(Debug, Clone, PartialEq, Eq)]
19pub(crate) enum Selector {
20 Class(StrId),
22
23 Wildcard,
25
26 Descendant,
28
29 Child,
31
32 Hover,
34
35 Focus,
37
38 Active,
40
41 Disabled,
43
44 Enabled,
46}
47
48impl Display for Selector {
49 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
50 match self {
51 Selector::Class(value) => write!(f, ".{}", value),
52 Selector::Wildcard => f.write_str("*"),
53 Selector::Descendant => f.write_str(" "),
54 Selector::Child => f.write_str(" > "),
55 Selector::Hover => f.write_str(":hover"),
56 Selector::Focus => f.write_str(":focus"),
57 Selector::Active => f.write_str(":active"),
58 Selector::Disabled => f.write_str(":disabled"),
59 Selector::Enabled => f.write_str(":enabled"),
60 }
61 }
62}
63
64#[derive(Debug, Clone)]
65pub(crate) struct Rule {
66 pub selectors: Vec<Selector>,
67 pub properties: Arc<SmallVec<[Property; 2]>>,
68 pub specificity: u32,
69 pub has_pseudos: bool,
70 pub variables: Vec<(Arc<str>, Arc<str>)>,
71}
72
73impl Eq for Rule {}
74
75impl PartialEq for Rule {
76 fn eq(&self, other: &Self) -> bool {
77 self.specificity == other.specificity
78 && self.selectors == other.selectors
79 && self.properties.as_ref() == other.properties.as_ref()
80 && self.variables == other.variables
81 }
82}
83
84impl Display for Rule {
85 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
86 for selector in &self.selectors {
87 write!(f, "{selector}")?;
88 }
89
90 f.write_str(" {")?;
91
92 for property in self.properties.iter() {
93 write!(f, "\t{property}")?;
94 }
95
96 f.write_str("}\n")
97 }
98}
99
100#[derive(Debug, Default, Clone)]
101pub(crate) struct StylesheetInner {
102 pub info: Option<ResourceInfo>,
103 pub rules: Vec<Rule>,
104
105 pub wildcard: Vec<usize>,
107
108 pub index: HashMap<StrId, Vec<usize>>,
110}
111
112#[derive(Default, Clone)]
114pub struct Stylesheet {
115 pub(crate) inner: Arc<RwLock<StylesheetInner>>,
116}
117
118impl Eq for Stylesheet {}
119impl PartialEq for Stylesheet {
120 fn eq(&self, other: &Self) -> bool {
121 let self_inner = self.inner.read();
122 let other_inner = other.inner.read();
123
124 self_inner.rules == other_inner.rules
125 }
126}
127
128impl std::fmt::Debug for Stylesheet {
129 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
130 f.debug_struct("Stylesheet").finish_non_exhaustive()
131 }
132}
133
134impl Display for Stylesheet {
135 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
136 let inner = self.inner.read();
137
138 for rule in &inner.rules {
139 writeln!(f, "{rule}")?;
140 }
141
142 Ok(())
143 }
144}
145
146impl FromStr for Stylesheet {
147 type Err = ();
148
149 fn from_str(css: &str) -> Result<Self, Self::Err> {
150 let mut rules = Vec::new();
151 let mut wildcard = Vec::new();
152 let mut index = HashMap::new();
153 Self::parse(css, None, &mut rules, &mut wildcard, &mut index);
154
155 Ok(Self {
156 inner: Arc::new(RwLock::new(StylesheetInner {
157 info: None,
158 rules,
159 wildcard,
160 index,
161 })),
162 })
163 }
164}
165
166impl Stylesheet {
167 pub fn from_file(path: impl AsRef<Path>) -> Result<Stylesheet, std::io::Error> {
169 let path_buf = path.as_ref().canonicalize()?;
170 let css = fs::read_to_string(&path_buf)?;
171 let mut rules = Vec::new();
172 let mut wildcard = Vec::new();
173 let mut index = HashMap::new();
174 Self::parse(&css, Some(&path_buf), &mut rules, &mut wildcard, &mut index);
175
176 let info = Some(ResourceInfo {
177 last_modified: fs::metadata(&path_buf)?.modified()?,
178 path: path_buf.clone(),
179 });
180
181 Ok(Self {
182 inner: Arc::new(RwLock::new(StylesheetInner { info, rules, wildcard, index })),
183 })
184 }
185
186 pub(crate) fn reload(&mut self) -> Result<bool, std::io::Error> {
187 let mut write_guard = self.inner.write();
188 let inner = &mut *write_guard;
189
190 if let Some(ref mut resource_info) = inner.info {
191 let current_modified_time = fs::metadata(&resource_info.path)?.modified()?;
192
193 if current_modified_time > resource_info.last_modified {
194 let new_css = fs::read_to_string(&resource_info.path)?;
196 Self::parse(&new_css, Some(&resource_info.path), &mut inner.rules, &mut inner.wildcard, &mut inner.index);
197
198 resource_info.last_modified = current_modified_time;
199
200 return Ok(true);
201 }
202 }
203
204 Ok(false)
205 }
206
207 fn parse(css: &str, file_name: Option<&Path>, rules: &mut Vec<Rule>, wildcard: &mut Vec<usize>, index: &mut HashMap<StrId, Vec<usize>>) {
208 rules.clear();
209 wildcard.clear();
210 index.clear();
211
212 let mut input = ParserInput::new(css);
213 let mut parser = Parser::new(&mut input);
214 let mut rp = RulesParser { file_name };
215
216 for result in RuleBodyParser::new(&mut parser, &mut rp) {
217 match result {
218 Ok(parsed_rules) => {
219 for rule in parsed_rules {
220 rules.push(rule);
221 }
222 }
223 Err((error, css)) => {
224 let msg = format_args!("Failed to parse CSS rule: `{}`", css.lines().next().unwrap_or(""));
225 css::log_error(msg, error.location, file_name);
226 }
227 }
228 }
229
230 rules.sort_by_key(|r| r.specificity);
232
233 for (idx, rule) in rules.iter().enumerate() {
235 let mut indexed = false;
236
237 for selector in rule.selectors.iter().rev() {
238 match selector {
239 Selector::Hover | Selector::Focus | Selector::Active | Selector::Disabled | Selector::Enabled => {
241 continue;
242 }
243
244 Selector::Wildcard | Selector::Child | Selector::Descendant => {
248 wildcard.push(idx);
249 indexed = true;
250 break;
251 }
252 Selector::Class(class_id) => {
253 index.entry(*class_id).or_default().push(idx);
254 indexed = true;
255 break;
256 }
257 }
258 }
259
260 if !indexed {
262 wildcard.push(idx);
263 }
264 }
265 }
266}
267
268#[derive(Copy, Clone)]
269enum PseudoKind {
270 Hover,
271 Focus,
272 Active,
273 Enabled, }
275
276#[derive(Debug)]
277pub(crate) struct VariableContext {
278 css_vars: HashMap<Arc<str>, Vec<Arc<str>>>,
280 scope_history: Vec<(usize, Vec<Arc<str>>)>,
282}
283
284impl VariableContext {
285 fn new() -> Self {
286 Self {
287 css_vars: HashMap::with_capacity(64),
288 scope_history: Vec::with_capacity(32),
289 }
290 }
291
292 fn clear(&mut self) {
294 self.css_vars.clear();
295 self.scope_history.clear();
296 }
297
298 pub fn get(&self, name: &str) -> Option<&str> {
300 self.css_vars.get(name).and_then(|stack| stack.last()).map(|v| &**v)
301 }
302
303 fn push_vars(&mut self, node_idx: usize, vars: &[(Arc<str>, Arc<str>)]) {
305 if vars.is_empty() {
306 return;
307 }
308
309 let mut keys_added = Vec::with_capacity(vars.len());
310
311 for (name, value) in vars {
312 self.css_vars.entry(Arc::clone(name)).or_default().push(value.clone());
313 keys_added.push(name.clone());
314 }
315
316 self.scope_history.push((node_idx, keys_added));
317 }
318
319 fn pop_vars(&mut self) {
321 if let Some((_, keys)) = self.scope_history.pop() {
322 for key in keys {
323 if let Some(stack) = self.css_vars.get_mut(&key) {
324 stack.pop();
325 if stack.is_empty() {
326 self.css_vars.remove(&key);
327 }
328 }
329 }
330 }
331 }
332
333 fn prune(&mut self, current_parent_idx: usize) {
335 while let Some((scope_node_idx, _)) = self.scope_history.last() {
336 if *scope_node_idx > current_parent_idx {
337 self.pop_vars();
338 } else {
339 break;
340 }
341 }
342 }
343}
344
345pub(crate) fn style_pre_pass<S, H>(temp: &Bump, tree: &mut Ui<S, H>, ancestor_classes: &mut Filter) {
347 debug_assert_eq!(tree.var_scope_cache.len(), 0, "Expected empty var_scope_cache before pre-pass.");
348 debug_assert_eq!(tree.style_flags.len(), 0, "Expected empty style_flags before pre-pass.");
349
350 ancestor_classes.clear();
351
352 let mut active_sheets: BumpVec<(usize, Stylesheet)> = BumpVec::new_in(temp);
353 let mut rules_list: BumpVec<usize> = BumpVec::new_in(temp);
354 let mut var_delta: HashMap<Arc<str>, Arc<str>> = HashMap::with_capacity(16);
355 let mut pseudos: Vec<(PseudoKind, usize)> = Vec::with_capacity(32);
356
357 for idx in 0..tree.nodes.len() {
358 tree.style_flags.push(0);
359
360 update_sheets(&tree.nodes, idx, &mut active_sheets);
361 update_ancestors(&tree.nodes, idx, ancestor_classes);
362
363 var_delta.clear();
364
365 for (_, (_, sheet)) in active_sheets.iter().enumerate().rev() {
367 rules_list.clear();
368
369 let inner = sheet.inner.read();
370
371 rules_list.extend_from_slice(&inner.wildcard);
372 for &class_id in tree.nodes[idx].classes.iter() {
373 if let Some(indexes) = inner.index.get(&class_id) {
374 rules_list.extend_from_slice(indexes);
375 }
376 }
377
378 rules_list.sort_unstable();
379 rules_list.dedup();
380
381 for &rule_idx in rules_list.iter() {
382 let rule = &inner.rules[rule_idx];
383
384 if !rule.has_pseudos {
385 if rule_matches_node(rule, &tree.nodes, idx, None, None, &[], ancestor_classes, None) {
387 for (name, value) in &rule.variables {
388 var_delta.insert(Arc::clone(name), Arc::clone(value));
390 }
391 }
392 } else {
393 let start = pseudos.len();
395
396 let matched = rule_matches_node(rule, &tree.nodes, idx, None, None, &[], ancestor_classes, Some(&mut pseudos));
397 if !matched {
398 pseudos.truncate(start);
399 continue;
400 }
401
402 for &(kind, pseudo_node_idx) in &pseudos[start..] {
404 let bit = match kind {
405 PseudoKind::Hover => css::HOVER_DIRTY,
406 PseudoKind::Focus => css::FOCUS_DIRTY,
407 PseudoKind::Active => css::ACTIVE_DIRTY,
408 PseudoKind::Enabled => css::ENABLED_DIRTY,
409 };
410
411 tree.style_flags[pseudo_node_idx] |= bit;
412 }
413
414 pseudos.truncate(start);
415 }
416 }
417 }
418
419 tree.var_scope_cache
420 .push(var_delta.iter().map(|(k, v)| (Arc::clone(k), Arc::clone(v))).collect());
421 }
422}
423
424pub(crate) fn style_pass<S, H>(
428 temp: &Bump,
429 tree: &mut Ui<S, H>,
430 state: &S,
431 focused_node: Option<NodeId>,
432 active_node: Option<NodeId>,
433 hot_nodes: &[usize],
434 ancestor_classes: &mut Filter,
435) -> bool {
436 tree.merge_dirty_roots();
437 if tree.nodes.is_empty() || tree.dirty_roots.is_empty() {
438 return false;
439 }
440
441 let Ui {
443 nodes,
444 style_cache,
445 dirty_roots,
446 var_scope_cache,
447 on_style_deps,
448 ..
449 } = tree;
450
451 let len = nodes.len();
452
453 let mut active_sheets: BumpVec<(usize, Stylesheet)> = BumpVec::new_in(temp);
454 let mut candidate_rules: BumpVec<usize> = BumpVec::new_in(temp);
455 let mut matched_rules: BumpVec<(usize, usize)> = BumpVec::new_in(temp);
456 let mut var_ctx = VariableContext::new();
457 let mut path: BumpVec<usize> = BumpVec::new_in(temp);
458 let mut scratch = ApplyScratch::default();
459 let mut affects_layout = false;
460
461 let mut dirty_i: usize = 0;
462 while dirty_i < dirty_roots.len() {
463 let root_idx = dirty_roots[dirty_i];
464 let mut idx: usize = root_idx;
465 let mut region_end = idx + nodes[idx].subtree_size + 1;
466
467 active_sheets.clear();
468 ancestor_classes.clear();
469 var_ctx.clear();
470 candidate_rules.clear();
471
472 path.clear();
474 let mut curr = idx;
475 while curr != usize::MAX {
476 path.push(curr);
478 curr = nodes[curr].parent;
479 }
480
481 for &node_idx in path.iter().rev() {
483 if let Some(sheet) = &nodes[node_idx].style_sheet {
484 active_sheets.push((node_idx, sheet.clone()));
485 }
486
487 if node_idx < var_scope_cache.len() {
488 var_ctx.push_vars(node_idx, &var_scope_cache[node_idx]);
489 }
490
491 for (_, (_, sheet)) in active_sheets.iter().enumerate().rev() {
493 candidate_rules.clear();
494
495 let inner = sheet.inner.read();
496
497 candidate_rules.extend_from_slice(&inner.wildcard);
498 for &class_id in nodes[node_idx].classes.iter() {
499 if let Some(indexes) = inner.index.get(&class_id) {
500 candidate_rules.extend_from_slice(indexes);
501 }
502 }
503
504 candidate_rules.sort_unstable();
505 candidate_rules.dedup();
506
507 for &rule_idx in candidate_rules.iter() {
508 let rule = &inner.rules[rule_idx];
509
510 if !rule.has_pseudos || rule.variables.is_empty() {
511 continue;
512 }
513
514 if rule_matches_node(rule, nodes, node_idx, focused_node, active_node, hot_nodes, ancestor_classes, None) {
515 var_ctx.push_vars(node_idx, &rule.variables);
516 }
517 }
518 }
519
520 if node_idx != idx {
522 for &class_id in nodes[node_idx].classes.iter() {
523 if let Err(error) = ancestor_classes.insert_duplicated(class_id) {
524 error!("Selector matching: {error}");
525 }
526 }
527 }
528 }
529
530 let mut first_in_region = true;
532 loop {
533 while dirty_i < dirty_roots.len() && dirty_roots[dirty_i] < region_end {
536 let i = dirty_roots[dirty_i];
537 dirty_i += 1;
538
539 let subtree_end = i + nodes[i].subtree_size + 1;
540 region_end = region_end.max(subtree_end);
541 }
542 if idx >= region_end || idx >= len {
543 break;
544 }
545
546 if !first_in_region {
547 update_sheets(nodes, idx, &mut active_sheets);
549 update_ancestors(nodes, idx, ancestor_classes);
550
551 let parent_idx = nodes[idx].parent;
552 var_ctx.prune(parent_idx);
553 if idx < var_scope_cache.len() {
554 var_ctx.push_vars(idx, &var_scope_cache[idx]);
555 }
556 }
557
558 let subtree_end = idx + nodes[idx].subtree_size + 1;
559
560 let parent_style_snapshot = (idx != 0).then(|| style_cache[nodes[idx].parent].clone());
561 let parent_style_opt = parent_style_snapshot.as_ref();
562
563 let mut new_style = if let Some(parent_style) = parent_style_opt {
564 Style {
565 color: parent_style.color,
566 font_width: parent_style.font_width,
567 font_size: parent_style.font_size,
568 font_style: parent_style.font_style,
569 font_family: parent_style.font_family.clone(),
570 font_weight: parent_style.font_weight,
571 text_shadow: parent_style.text_shadow.clone(),
572 letter_spacing: parent_style.letter_spacing,
573 word_spacing: parent_style.word_spacing,
574 line_height: parent_style.line_height,
575 ..Default::default()
576 }
577 } else {
578 Style::default()
579 };
580
581 matched_rules.clear();
583
584 let sheet_inners: SmallVec<[RwLockReadGuard<'_, StylesheetInner>; 8]> = active_sheets.iter().map(|(_, sheet)| sheet.inner.read()).collect();
586
587 for (sheet_stack_idx, _) in active_sheets.iter().enumerate().rev() {
588 candidate_rules.clear();
589
590 let inner = &sheet_inners[sheet_stack_idx];
591
592 candidate_rules.extend_from_slice(&inner.wildcard);
593 for &class_id in nodes[idx].classes.iter() {
594 if let Some(indexes) = inner.index.get(&class_id) {
595 candidate_rules.extend_from_slice(indexes);
596 }
597 }
598
599 candidate_rules.sort_unstable();
600 candidate_rules.dedup();
601
602 for &rule_idx in candidate_rules.iter() {
603 let rule = &inner.rules[rule_idx];
604
605 if rule_matches_node(rule, nodes, idx, focused_node, active_node, hot_nodes, ancestor_classes, None) {
606 matched_rules.push((sheet_stack_idx, rule_idx));
607
608 if rule.has_pseudos && !rule.variables.is_empty() {
609 var_ctx.push_vars(idx, &rule.variables);
610 }
611 }
612 }
613 }
614
615 for phase in 0..2 {
617 for &(sheet_stack_idx, rule_idx) in matched_rules.iter() {
618 let inner = &sheet_inners[sheet_stack_idx];
619 let rule = &inner.rules[rule_idx];
620
621 for property in rule.properties.iter() {
622 let is_color = matches!(property, Property::Color(_));
624 match phase {
625 0 if !is_color => continue,
626 1 if is_color => continue,
627 _ => {}
628 }
629 if phase == 1 {
630 affects_layout |= property.affects_layout();
631 }
632 if let Err(e) = property.apply(&mut scratch, &mut new_style, parent_style_opt, &var_ctx) {
633 css::log_error(&e, e.location, inner.info.as_ref().map(|i| i.path.as_path()));
634 }
635 }
636 }
637 }
638
639 let mut callback_ran = false;
641 if let Some(callback) = nodes[idx].style_callback.as_deref() {
642 let prev_layout_style = new_style.get_layout_style();
643
644 let base = on_style_deps.remove(&idx).unwrap_or_default();
645 let deps = base.cleared().read_scope(|| {
646 callback(state, &mut new_style);
647 });
648 on_style_deps.insert(idx, deps);
649
650 affects_layout |= new_style.get_layout_style() != prev_layout_style;
651 callback_ran = true;
652 }
653
654 style_cache[idx] = new_style;
656
657 if callback_ran {
658 region_end = region_end.max(subtree_end);
659 }
660
661 idx += 1;
662 first_in_region = false;
663 }
664 }
665
666 dirty_roots.clear();
667 affects_layout
668}
669
670fn update_sheets<S, H>(nodes: &[Node<S, H>], idx: usize, active_sheets: &mut BumpVec<(usize, Stylesheet)>) {
671 while active_sheets.pop_if(|(style_idx, _)| *style_idx > nodes[idx].parent).is_some() {}
673
674 if let Some(stylesheet) = &nodes[idx].style_sheet {
675 active_sheets.push((idx, stylesheet.clone()));
676 }
677}
678
679fn update_ancestors<S, H>(nodes: &[Node<S, H>], idx: usize, ancestor_classes: &mut Filter) {
680 if idx != 0 {
681 let parent = nodes[idx].parent;
682 let prev_parent = nodes[idx - 1].parent;
683
684 if parent == prev_parent {
686 return;
687 }
688
689 if parent > prev_parent || prev_parent == usize::MAX {
691 for &class_id in nodes[parent].classes.iter() {
693 if let Err(error) = ancestor_classes.insert_duplicated(class_id) {
694 error!("Selector matching: {error}");
695 }
696 }
697
698 } else {
700 let mut curr = prev_parent;
702 while curr != parent {
703 for &class_id in nodes[curr].classes.iter() {
704 ancestor_classes.remove(class_id);
705 }
706 curr = nodes[curr].parent;
707 }
708 }
709 }
710}
711
712#[allow(clippy::too_many_arguments)]
716fn rule_matches_node<S, H>(
717 rule: &Rule,
718 nodes: &[Node<S, H>],
719 idx: usize,
720 focused_node: Option<NodeId>,
721 active_node: Option<NodeId>,
722 hot_nodes: &[usize],
723 ancestor_classes: &Filter,
724 mut pseudo_out: Option<&mut Vec<(PseudoKind, usize)>>,
725) -> bool {
726 let mut cmp_node = idx;
727 let mut is_first = true;
728 let mut prev_class = false;
729 let mut prev_child = false;
730
731 'selector: for selector in rule.selectors.iter().rev() {
732 'node: while cmp_node != usize::MAX {
733 match selector {
734 Selector::Class(rule_class_id) => {
735 if nodes[cmp_node].classes.contains(rule_class_id) {
736 is_first = false;
737 prev_class = true;
738 prev_child = false;
739 continue 'selector;
740 } else if is_first || prev_class || prev_child {
741 return false;
742 }
743
744 if !ancestor_classes.contains(rule_class_id) {
745 return false;
746 }
747
748 is_first = false;
749 prev_class = true;
750 prev_child = false;
751
752 cmp_node = nodes[cmp_node].parent;
753 continue 'node;
754 }
755
756 Selector::Wildcard => {
757 is_first = false;
758 prev_class = false;
759 prev_child = false;
760 continue 'selector;
761 }
762
763 Selector::Child => {
764 prev_class = false;
765 prev_child = true;
766 cmp_node = nodes[cmp_node].parent;
767 continue 'selector;
768 }
769
770 Selector::Descendant => {
771 prev_child = false;
772 prev_class = false;
773 cmp_node = nodes[cmp_node].parent;
774 continue 'selector;
775 }
776 Selector::Hover => {
777 if let Some(out) = pseudo_out.as_deref_mut() {
778 out.push((PseudoKind::Hover, cmp_node));
779 prev_child = false;
780 prev_class = false;
781 continue 'selector;
782 } else if hot_nodes.contains(&cmp_node) {
783 prev_child = false;
784 prev_class = false;
785 continue 'selector;
786 } else {
787 return false;
788 }
789 }
790 Selector::Focus => {
791 if let Some(out) = pseudo_out.as_deref_mut() {
792 out.push((PseudoKind::Focus, cmp_node));
793 prev_child = false;
794 prev_class = false;
795 continue 'selector;
796 } else if let (Some(cmp_nid), Some(focus_nid)) = (nodes[cmp_node].nid, focused_node)
797 && cmp_nid == focus_nid
798 {
799 prev_child = false;
800 prev_class = false;
801 continue 'selector;
802 } else {
803 return false;
804 }
805 }
806 Selector::Active => {
807 if let Some(out) = pseudo_out.as_deref_mut() {
808 out.push((PseudoKind::Active, cmp_node));
809 prev_child = false;
810 prev_class = false;
811 continue 'selector;
812 } else if let (Some(cmp_nid), Some(active_nid)) = (nodes[cmp_node].nid, active_node)
813 && cmp_nid == active_nid
814 {
815 prev_child = false;
816 prev_class = false;
817 continue 'selector;
818 } else {
819 return false;
820 }
821 }
822 Selector::Enabled | Selector::Disabled => {
823 if let Some(out) = pseudo_out.as_deref_mut() {
824 out.push((PseudoKind::Enabled, cmp_node));
825 prev_child = false;
826 prev_class = false;
827 continue 'selector;
828 } else {
829 let enabled = nodes[cmp_node].enabled.get().unwrap_or(true);
830 let want_enabled = matches!(selector, Selector::Enabled);
831 if enabled == want_enabled {
832 prev_child = false;
833 prev_class = false;
834 continue 'selector;
835 }
836 return false;
837 }
838 }
839 }
840 }
841 return false;
842 }
843 true
844}
845
846#[cfg(feature = "serde")]
847impl serde::Serialize for Stylesheet {
848 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
849 where
850 S: serde::Serializer,
851 {
852 use serde::ser::SerializeMap;
853
854 let inner = self.inner.read();
855
856 let mut map = serializer.serialize_map(Some(1))?;
857 if let Some(info) = &inner.info {
858 let path = info.path.to_string_lossy();
859 map.serialize_entry("path", path.as_ref())?;
860 } else {
861 let data = self.to_string();
862 map.serialize_entry("css", data.as_str())?;
863 }
864 map.end()
865 }
866}
867
868#[cfg(feature = "serde")]
869impl<'de> serde::Deserialize<'de> for Stylesheet {
870 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
871 where
872 D: serde::Deserializer<'de>,
873 {
874 use serde::de::{self, MapAccess, Visitor};
875
876 struct StylesheetVisitor;
877
878 impl<'de> Visitor<'de> for StylesheetVisitor {
879 type Value = Stylesheet;
880
881 fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
882 f.write_str("a stylesheet string or filename")
883 }
884
885 fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
886 where
887 A: MapAccess<'de>,
888 {
889 let mut path: Option<String> = None;
890 let mut css: Option<String> = None;
891
892 while let Some(key) = map.next_key::<&str>()? {
893 match key {
894 "path" => {
895 if path.is_some() {
896 return Err(de::Error::duplicate_field("path"));
897 }
898 path = Some(map.next_value()?);
899 }
900 "css" => {
901 if css.is_some() {
902 return Err(de::Error::duplicate_field("css"));
903 }
904 css = Some(map.next_value()?);
905 }
906 other => {
907 let _ = map.next_value::<de::IgnoredAny>()?;
909 return Err(de::Error::unknown_field(other, &["path", "css"]));
910 }
911 }
912 }
913
914 match (path, css) {
915 (Some(_), Some(_)) => Err(de::Error::custom("expected exactly one of \"path\" or \"css\"")),
916 (Some(p), None) => Stylesheet::from_file(p).map_err(de::Error::custom),
917 (None, Some(c)) => Stylesheet::from_str(&c).map_err(|_| de::Error::custom("failed to parse stylesheet CSS")),
918 (None, None) => Err(de::Error::custom("missing \"path\" or \"css\"")),
919 }
920 }
921 }
922
923 deserializer.deserialize_map(StylesheetVisitor)
924 }
925}