grass/src/selector.rs

250 lines
9.8 KiB
Rust
Raw Normal View History

use crate::common::Symbol;
use crate::{Token, TokenKind};
use std::iter::Peekable;
use std::{
fmt::{self, Display},
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<Selector>, Box<Selector>),
/// And selector: `button.active`
And(Box<Selector>, Box<Selector>),
/// Multiple unrelated selectors: `button, .active`
Multiple(Box<Selector>, Box<Selector>),
/// Select all immediate children: `ul > li`
ImmediateChild(Box<Selector>, Box<Selector>),
/// Select all elements immediately following: `div + p`
Following(Box<Selector>, Box<Selector>),
/// Select elements preceeded by: `p ~ ul`
Preceding(Box<Selector>, Box<Selector>),
/// 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<Iter<'a, Token>>,
}
impl<'a> SelectorParser<'a> {
const fn new(tokens: Peekable<Iter<'a, Token>>) -> 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<Selector> {
if let Some(tok) = self.tokens.next() {
2020-01-05 20:55:58 -05:00
let selector = match &tok.kind {
2020-01-06 00:41:15 -05:00
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,
2020-01-06 00:41:15 -05:00
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) => {
2020-01-06 00:41:15 -05:00
match self.tokens.next().expect("expected ident after `:`").kind {
2020-01-05 20:55:58 -05:00
TokenKind::Ident(ref tok) => Selector::Pseudo(tok.clone()),
_ => todo!("there should normally be an ident after `:`"),
}
}
2020-01-05 20:55:58 -05:00
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<Iter<Token>>) -> 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,
}