use std::collections::HashMap;
use crate::ast::{Node, PseudoClass, Rule, Stylesheet};
use crate::value::Value;
#[derive(Debug, Clone, Default, PartialEq)]
pub struct ResolvedStyle {
pub(crate) properties: HashMap<String, ((usize, usize), Value)>,
}
impl ResolvedStyle {
pub fn get(&self, property: &str) -> Option<&Value> {
self.properties.get(property).map(|(_, v)| v)
}
pub fn len(&self) -> usize {
self.properties.len()
}
pub fn is_empty(&self) -> bool {
self.properties.is_empty()
}
pub fn iter(&self) -> impl Iterator<Item = (&str, &Value)> {
type Entry<'a> = (&'a String, &'a ((usize, usize), Value));
let mut entries: Vec<Entry<'_>> = self.properties.iter().collect();
entries.sort_by(|(ka, (pa, _)), (kb, (pb, _))| pa.cmp(pb).then_with(|| ka.cmp(kb)));
entries.into_iter().map(|(k, (_, v))| (k.as_str(), v))
}
}
type CascadeKey = (bool, u32, usize, usize);
impl Stylesheet {
pub fn resolve(
&self,
target: &Node<'_>,
ancestors: &[Node<'_>],
prev_siblings: &[Node<'_>],
state: Option<PseudoClass>,
) -> ResolvedStyle {
let mut best: HashMap<String, (CascadeKey, Value)> = HashMap::new();
for (rule_idx, rule) in self.rules.iter().enumerate() {
let Some(spec) =
best_matching_specificity(rule, target, ancestors, prev_siblings, state)
else {
continue;
};
for (decl_idx, decl) in rule.declarations.iter().enumerate() {
let key: CascadeKey = (decl.important, spec, rule_idx, decl_idx);
let replace = match best.get(&decl.property) {
Some((existing, _)) => existing <= &key,
None => true,
};
if replace {
best.insert(decl.property.clone(), (key, decl.value.clone()));
}
}
}
ResolvedStyle {
properties: best
.into_iter()
.map(|(k, ((_, _, rule_idx, decl_idx), v))| (k, ((rule_idx, decl_idx), v)))
.collect(),
}
}
}
fn best_matching_specificity(
rule: &Rule,
target: &Node<'_>,
ancestors: &[Node<'_>],
prev_siblings: &[Node<'_>],
state: Option<PseudoClass>,
) -> Option<u32> {
rule.selectors
.iter()
.filter(|s| s.matches(target, ancestors, prev_siblings, state))
.map(|s| s.specificity())
.max()
}