use crate::value::Value;
#[derive(Debug, Clone, PartialEq, Default)]
pub struct Stylesheet {
pub rules: Vec<Rule>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct Rule {
pub selectors: Vec<Selector>,
pub declarations: Vec<Declaration>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct Selector {
pub parts: Vec<SimpleSelector>,
pub combinators: Vec<Combinator>,
}
#[derive(Debug, Clone, PartialEq, Default)]
pub struct SimpleSelector {
pub element: Option<String>,
pub classes: Vec<String>,
pub pseudo: Option<PseudoClass>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Combinator {
Descendant,
Child,
AdjacentSibling,
GeneralSibling,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum PseudoClass {
Hover,
Focus,
Active,
Disabled,
Selected,
}
#[derive(Debug, Clone, PartialEq)]
pub struct Declaration {
pub property: String,
pub value: Value,
pub important: bool,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Node<'a> {
pub element: &'a str,
pub classes: &'a [&'a str],
}
impl Selector {
pub fn specificity(&self) -> u32 {
self.parts.iter().map(SimpleSelector::specificity).sum()
}
pub fn matches(
&self,
target: &Node<'_>,
ancestors: &[Node<'_>],
prev_siblings: &[Node<'_>],
state: Option<PseudoClass>,
) -> bool {
let n = self.parts.len();
if n == 0 {
return false;
}
if !self.parts[n - 1].matches_node(target, state) {
return false;
}
if n == 1 {
return true;
}
let mut remaining_ancestors: &[Node<'_>] = ancestors;
let mut remaining_siblings: &[Node<'_>] = prev_siblings;
for i in (0..n - 1).rev() {
let part = &self.parts[i];
let combinator = self.combinators[i];
match combinator {
Combinator::Descendant => {
let pos = remaining_ancestors
.iter()
.rposition(|a| part.matches_node(a, None));
match pos {
Some(idx) => {
remaining_ancestors = &remaining_ancestors[..idx];
}
None => return false,
}
}
Combinator::Child => match remaining_ancestors.last() {
Some(parent) if part.matches_node(parent, None) => {
remaining_ancestors = &remaining_ancestors[..remaining_ancestors.len() - 1];
}
_ => return false,
},
Combinator::AdjacentSibling => match remaining_siblings.last() {
Some(sib) if part.matches_node(sib, None) => {
remaining_siblings = &remaining_siblings[..remaining_siblings.len() - 1];
}
_ => return false,
},
Combinator::GeneralSibling => {
let pos = remaining_siblings
.iter()
.rposition(|s| part.matches_node(s, None));
match pos {
Some(idx) => {
remaining_siblings = &remaining_siblings[..idx];
}
None => return false,
}
}
}
}
true
}
}
impl SimpleSelector {
pub fn specificity(&self) -> u32 {
let classes = (self.classes.len() as u32) * 10;
let pseudo = u32::from(self.pseudo.is_some()) * 10;
let element = u32::from(self.element.is_some());
classes + pseudo + element
}
pub fn matches_node(&self, node: &Node<'_>, state: Option<PseudoClass>) -> bool {
if let Some(want) = &self.element
&& want.as_str() != node.element
{
return false;
}
if !self
.classes
.iter()
.all(|c| node.classes.contains(&c.as_str()))
{
return false;
}
match (self.pseudo, state) {
(None, _) => true,
(Some(want), Some(have)) => want == have,
(Some(_), None) => false,
}
}
}
impl PseudoClass {
pub fn from_ident(ident: &str) -> Option<Self> {
Some(match ident.to_ascii_lowercase().as_str() {
"hover" => Self::Hover,
"focus" => Self::Focus,
"active" => Self::Active,
"disabled" => Self::Disabled,
"selected" => Self::Selected,
_ => return None,
})
}
pub fn as_str(&self) -> &'static str {
match self {
Self::Hover => "hover",
Self::Focus => "focus",
Self::Active => "active",
Self::Disabled => "disabled",
Self::Selected => "selected",
}
}
}