use crate::common::Symbol; use crate::{Token, TokenKind}; use std::fmt::{self, Display}; use std::iter::Peekable; use std::slice::Iter; #[derive(Clone, Debug, Eq, PartialEq)] pub enum Selector { /// An element selector: `button` Element(String), /// An id selector: `#footer` Id(String), /// A single class selector: `.button-active` Class(String), /// A universal selector: `*` Universal, /// A simple child selector: `ul li` Descendant(Box, Box), /// And selector: `button.active` And(Box, Box), /// Multiple unrelated selectors: `button, .active` Multiple(Box, Box), /// Select all immediate children: `ul > li` ImmediateChild(Box, Box), /// Select all elements immediately following: `div + p` Following(Box, Box), /// Select elements preceeded by: `p ~ ul` Preceding(Box, Box), /// Select elements with attribute: `html[lang|=en]` Attribute(Attribute), /// Pseudo selector: `:hover` Pseudo(String), /// Used to signify no selector (when there is no super_selector of a rule) None, } impl Display for Selector { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Selector::Element(s) => write!(f, "{}", s), Selector::Id(s) => write!(f, "#{}", s), Selector::Class(s) => write!(f, ".{}", s), Selector::Universal => write!(f, "*"), Selector::Descendant(lhs, rhs) => write!(f, "{} {}", lhs, rhs), Selector::And(lhs, rhs) => write!(f, "{}{}", lhs, rhs), Selector::Multiple(lhs, rhs) => write!(f, "{}, {}", lhs, rhs), Selector::ImmediateChild(lhs, rhs) => write!(f, "{} > {}", lhs, rhs), Selector::Following(lhs, rhs) => write!(f, "{} + {}", lhs, rhs), Selector::Preceding(lhs, rhs) => write!(f, "{} ~ {}", lhs, rhs), Selector::Attribute(attr) => write!(f, "{}", attr), Selector::Pseudo(s) => write!(f, ":{}", s), Selector::None => write!(f, ""), } } } struct SelectorParser<'a> { tokens: Peekable>, } impl<'a> SelectorParser<'a> { const fn new(tokens: Peekable>) -> SelectorParser<'a> { SelectorParser { tokens } } fn all_selectors(&mut self) -> Selector { self.devour_whitespace(); let left = self .consume_selector() .expect("expected left handed selector"); let whitespace: bool = self.devour_whitespace(); match self.tokens.peek() { Some(tok) => match tok.kind { TokenKind::Ident(_) => { return Selector::Descendant(Box::new(left), Box::new(self.all_selectors())) } TokenKind::Symbol(Symbol::Plus) => { self.tokens.next(); self.devour_whitespace(); return Selector::Following(Box::new(left), Box::new(self.all_selectors())); } TokenKind::Symbol(Symbol::Tilde) => { self.tokens.next(); self.devour_whitespace(); return Selector::Preceding(Box::new(left), Box::new(self.all_selectors())); } TokenKind::Symbol(Symbol::Comma) => { self.tokens.next(); self.devour_whitespace(); return Selector::Multiple(Box::new(left), Box::new(self.all_selectors())); } TokenKind::Symbol(Symbol::Gt) => { self.tokens.next(); self.devour_whitespace(); return Selector::ImmediateChild( Box::new(left), Box::new(self.all_selectors()), ); } TokenKind::Symbol(Symbol::Colon) | TokenKind::Symbol(Symbol::Period) | TokenKind::Symbol(Symbol::Mul) | TokenKind::Selector(_) | TokenKind::Symbol(Symbol::Hash) => { if whitespace { return Selector::Descendant( Box::new(left), Box::new(self.all_selectors()), ); } else { return Selector::And(Box::new(left), Box::new(self.all_selectors())); } } TokenKind::Symbol(Symbol::Lt) => {} _ => todo!(), }, None => return left, } todo!() } fn devour_whitespace(&mut self) -> bool { let mut found_whitespace = false; while let Some(tok) = self.tokens.peek() { match tok.kind { TokenKind::Whitespace(_) => { self.tokens.next(); found_whitespace = true; } _ => break, } } found_whitespace } fn consume_selector(&mut self) -> Option { if let Some(tok) = self.tokens.next() { let selector = match &tok.kind { TokenKind::Symbol(Symbol::Period) => { match self.tokens.next().expect("expected ident after `.`").kind { TokenKind::Ident(ref tok) => Selector::Class(tok.clone()), _ => todo!("there should normally be an ident after `.`"), } } TokenKind::Symbol(Symbol::Mul) => Selector::Universal, TokenKind::Symbol(Symbol::Hash) => { match &self.tokens.next().expect("expected ident after `#`").kind { TokenKind::Ident(ref tok) => Selector::Id(tok.clone()), _ => todo!("there should normally be an ident after `#`"), } } TokenKind::Symbol(Symbol::Colon) => { match self.tokens.next().expect("expected ident after `:`").kind { TokenKind::Ident(ref tok) => Selector::Pseudo(tok.clone()), _ => todo!("there should normally be an ident after `:`"), } } TokenKind::Ident(tok) => Selector::Element(tok.clone()), TokenKind::Selector(sel) => sel.clone(), _ => todo!(), }; Some(selector) } else { None } } } impl Selector { pub fn from_tokens(tokens: Peekable>) -> Selector { SelectorParser::new(tokens).all_selectors() } pub fn zip(self, other: Selector) -> Selector { if let Selector::Multiple(lhs, rhs) = other { return Selector::Multiple(Box::new(self.clone().zip(*lhs)), Box::new(self.zip(*rhs))); } match self { Selector::Multiple(lhs, rhs) => { Selector::Multiple(Box::new(lhs.zip(other.clone())), Box::new(rhs.zip(other))) } Selector::None => other, _ => Selector::Descendant(Box::new(self), Box::new(other)), } } } #[derive(Clone, Debug, Eq, PartialEq)] pub struct Attribute { pub attr: String, pub value: String, pub case_sensitive: bool, pub kind: AttributeKind, } impl Display for Attribute { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if self.case_sensitive { match self.kind { AttributeKind::Any => write!(f, "[{}]", self.attr), AttributeKind::Equals => write!(f, "[{}={}]", self.attr, self.value), AttributeKind::InList => write!(f, "[{}~={}]", self.attr, self.value), AttributeKind::BeginsWithHyphenOrExact => { write!(f, "[{}|={}]", self.attr, self.value) } AttributeKind::StartsWith => write!(f, "[{}^={}]", self.attr, self.value), AttributeKind::EndsWith => write!(f, "[{}$={}]", self.attr, self.value), AttributeKind::Contains => write!(f, "[{}*={}]", self.attr, self.value), } } else { match self.kind { AttributeKind::Any => write!(f, "[{} i]", self.attr), AttributeKind::Equals => write!(f, "[{}={} i]", self.attr, self.value), AttributeKind::InList => write!(f, "[{}~={} i]", self.attr, self.value), AttributeKind::BeginsWithHyphenOrExact => { write!(f, "[{}|={} i]", self.attr, self.value) } AttributeKind::StartsWith => write!(f, "[{}^={} i]", self.attr, self.value), AttributeKind::EndsWith => write!(f, "[{}$={} i]", self.attr, self.value), AttributeKind::Contains => write!(f, "[{}*={} i]", self.attr, self.value), } } } } #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub enum AttributeKind { /// [attr] /// Represents elements with an attribute name of `attr` Any, /// [attr=value] /// Represents elements with an attribute name of `attr` whose value is exactly `value` Equals, /// [attr~=value] /// Represents elements with an attribute name of `attr` whose value is a whitespace-separated list of words, one of which is exactly `value` InList, /// [attr|=value] /// Represents elements with an attribute name of `attr` whose value can be exactly value or can begin with `value` immediately followed by a hyphen (`-`) BeginsWithHyphenOrExact, /// [attr^=value] StartsWith, /// [attr$=value] EndsWith, /// [attr*=value] /// Represents elements with an attribute name of `attr` whose value contains at least one occurrence of `value` within the string Contains, }