2020-01-12 19:56:58 -05:00
use crate ::common ::{ Scope , Symbol } ;
2020-01-14 17:39:19 -05:00
use crate ::utils ::{ devour_whitespace , eat_interpolation , IsWhitespace } ;
2020-01-12 19:56:58 -05:00
use crate ::{ Token , TokenKind } ;
2020-01-06 19:23:52 -05:00
use std ::fmt ::{ self , Display } ;
2020-01-04 22:55:04 -05:00
use std ::iter ::Peekable ;
2020-01-06 17:06:37 -05:00
use std ::slice ::Iter ;
2020-01-11 19:16:59 -05:00
use std ::string ::ToString ;
2020-01-04 22:55:04 -05:00
#[ derive(Clone, Debug, Eq, PartialEq) ]
2020-01-11 14:51:31 -05:00
pub struct Selector ( pub Vec < SelectorKind > ) ;
impl Display for Selector {
fn fmt ( & self , f : & mut fmt ::Formatter < '_ > ) -> fmt ::Result {
let mut iter = self . 0. iter ( ) . peekable ( ) ;
while let Some ( s ) = iter . next ( ) {
match s {
SelectorKind ::Whitespace = > continue ,
SelectorKind ::Attribute ( _ )
| SelectorKind ::Pseudo ( _ )
2020-01-11 19:16:59 -05:00
| SelectorKind ::PseudoParen ( .. )
2020-01-11 14:51:31 -05:00
| SelectorKind ::Class
| SelectorKind ::Id
| SelectorKind ::Universal
| SelectorKind ::Element ( _ ) = > {
write! ( f , " {} " , s ) ? ;
if devour_whitespace ( & mut iter ) {
match iter . peek ( ) {
Some ( SelectorKind ::Attribute ( _ ) )
| Some ( SelectorKind ::Pseudo ( _ ) )
2020-01-11 19:16:59 -05:00
| Some ( SelectorKind ::PseudoParen ( .. ) )
2020-01-11 14:51:31 -05:00
| Some ( SelectorKind ::Class )
| Some ( SelectorKind ::Id )
| Some ( SelectorKind ::Universal )
| Some ( SelectorKind ::Element ( _ ) ) = > {
2020-01-11 16:12:23 -05:00
write! ( f , " " ) ? ;
2020-01-11 14:51:31 -05:00
}
_ = > { }
}
}
}
_ = > write! ( f , " {} " , s ) ? ,
}
}
write! ( f , " " )
}
}
#[ derive(Clone, Debug, Eq, PartialEq) ]
pub enum SelectorKind {
2020-01-04 22:55:04 -05:00
/// An element selector: `button`
Element ( String ) ,
/// An id selector: `#footer`
2020-01-11 14:51:31 -05:00
Id ,
2020-01-04 22:55:04 -05:00
/// A single class selector: `.button-active`
2020-01-11 14:51:31 -05:00
Class ,
2020-01-04 22:55:04 -05:00
/// A universal selector: `*`
Universal ,
/// Multiple unrelated selectors: `button, .active`
2020-01-11 14:51:31 -05:00
Multiple ,
2020-01-04 22:55:04 -05:00
/// Select all immediate children: `ul > li`
2020-01-11 14:51:31 -05:00
ImmediateChild ,
2020-01-04 22:55:04 -05:00
/// Select all elements immediately following: `div + p`
2020-01-11 14:51:31 -05:00
Following ,
2020-01-04 22:55:04 -05:00
/// Select elements preceeded by: `p ~ ul`
2020-01-11 14:51:31 -05:00
Preceding ,
2020-01-04 22:55:04 -05:00
/// Select elements with attribute: `html[lang|=en]`
Attribute ( Attribute ) ,
/// Pseudo selector: `:hover`
Pseudo ( String ) ,
2020-01-11 19:16:59 -05:00
/// Pseudo selector with additional parens: `:any(h1, h2, h3, h4, h5, h6)`
PseudoParen ( String , Vec < TokenKind > ) ,
2020-01-11 14:51:31 -05:00
/// Use the super selector: `&.red`
2020-01-11 16:12:23 -05:00
Super ,
2020-01-05 12:25:03 -05:00
/// Used to signify no selector (when there is no super_selector of a rule)
None ,
2020-01-11 14:51:31 -05:00
Whitespace ,
2020-01-04 22:55:04 -05:00
}
2020-01-12 20:15:27 -05:00
impl IsWhitespace for SelectorKind {
fn is_whitespace ( & self ) -> bool {
self = = & Self ::Whitespace
}
}
impl IsWhitespace for & SelectorKind {
fn is_whitespace ( & self ) -> bool {
self = = & & SelectorKind ::Whitespace
}
}
2020-01-11 14:51:31 -05:00
impl Display for SelectorKind {
2020-01-04 22:55:04 -05:00
fn fmt ( & self , f : & mut fmt ::Formatter < '_ > ) -> fmt ::Result {
match self {
2020-01-11 14:51:31 -05:00
SelectorKind ::Element ( s ) = > write! ( f , " {} " , s ) ,
SelectorKind ::Id = > write! ( f , " # " ) ,
SelectorKind ::Class = > write! ( f , " . " ) ,
SelectorKind ::Universal = > write! ( f , " * " ) ,
SelectorKind ::Whitespace = > write! ( f , " " ) ,
SelectorKind ::Multiple = > write! ( f , " , " ) ,
SelectorKind ::ImmediateChild = > write! ( f , " > " ) ,
SelectorKind ::Following = > write! ( f , " + " ) ,
SelectorKind ::Preceding = > write! ( f , " ~ " ) ,
SelectorKind ::Attribute ( attr ) = > write! ( f , " {} " , attr ) ,
SelectorKind ::Pseudo ( s ) = > write! ( f , " :{} " , s ) ,
2020-01-11 19:16:59 -05:00
SelectorKind ::PseudoParen ( s , toks ) = > write! (
f ,
" :{}({}) " ,
s ,
toks . iter ( ) . map ( ToString ::to_string ) . collect ::< String > ( )
) ,
2020-01-11 16:12:23 -05:00
SelectorKind ::Super | SelectorKind ::None = > write! ( f , " " ) ,
2020-01-11 14:51:31 -05:00
}
}
}
#[ cfg(test) ]
mod test_selector_display {
use super ::* ;
use SelectorKind ::* ;
macro_rules ! test_selector_display {
( $func :ident , Selector ( $tok :tt ) , $output :literal ) = > {
#[ test ]
fn $func ( ) {
assert_eq! ( format! ( " {} " , Selector ( vec ! $tok ) ) , $output ) ;
}
2020-01-04 22:55:04 -05:00
}
}
2020-01-11 14:51:31 -05:00
test_selector_display! ( el , Selector ( ( Element ( " a " . to_string ( ) ) ) ) , " a " ) ;
test_selector_display! (
keeps_one_whitespace ,
Selector ( (
Element ( " a " . to_string ( ) ) ,
Whitespace ,
Element ( " b " . to_string ( ) ) ,
) ) ,
" a b "
) ;
test_selector_display! (
keeps_one_whitespace_with_two ,
Selector ( (
Element ( " a " . to_string ( ) ) ,
Whitespace ,
Whitespace ,
Element ( " c " . to_string ( ) ) ,
Whitespace ,
) ) ,
" a c "
) ;
2020-01-11 16:12:23 -05:00
test_selector_display! (
keeps_one_whitespace_with_three_els ,
Selector ( (
Element ( " a " . to_string ( ) ) ,
Whitespace ,
Element ( " a " . to_string ( ) ) ,
Whitespace ,
Element ( " c " . to_string ( ) ) ,
) ) ,
" a a c "
) ;
2020-01-04 22:55:04 -05:00
}
struct SelectorParser < ' a > {
2020-01-11 14:51:31 -05:00
super_selector : & ' a Selector ,
2020-01-12 17:44:49 -05:00
scope : & ' a Scope ,
2020-01-14 17:39:19 -05:00
selectors : Vec < SelectorKind > ,
2020-01-12 10:54:46 -05:00
}
impl < ' a > SelectorParser < ' a > {
2020-01-14 17:39:19 -05:00
const fn new ( super_selector : & ' a Selector , scope : & ' a Scope ) -> SelectorParser < ' a > {
2020-01-11 14:51:31 -05:00
SelectorParser {
super_selector ,
2020-01-12 17:44:49 -05:00
scope ,
2020-01-14 17:39:19 -05:00
selectors : Vec ::new ( ) ,
2020-01-11 14:51:31 -05:00
}
2020-01-04 22:55:04 -05:00
}
2020-01-14 17:39:19 -05:00
fn all_selectors ( & mut self , tokens : & ' a mut Peekable < Iter < '_ , Token > > ) -> Selector {
self . tokens_to_selectors ( tokens ) ;
// remove trailing whitespace
while let Some ( x ) = self . selectors . pop ( ) {
2020-01-11 16:12:23 -05:00
if x ! = SelectorKind ::Whitespace {
2020-01-14 17:39:19 -05:00
self . selectors . push ( x ) ;
2020-01-11 16:12:23 -05:00
break ;
}
}
2020-01-14 17:39:19 -05:00
Selector ( self . selectors . clone ( ) )
2020-01-11 14:51:31 -05:00
}
2020-01-14 17:39:19 -05:00
fn consume_pseudo_selector ( & mut self , tokens : & '_ mut Peekable < Iter < '_ , Token > > ) {
2020-01-11 19:16:59 -05:00
if let Some ( Token {
kind : TokenKind ::Ident ( s ) ,
..
2020-01-14 17:39:19 -05:00
} ) = tokens . next ( )
2020-01-11 19:16:59 -05:00
{
if let Some ( Token {
kind : TokenKind ::Symbol ( Symbol ::OpenParen ) ,
..
2020-01-14 17:39:19 -05:00
} ) = tokens . peek ( )
2020-01-11 19:16:59 -05:00
{
2020-01-14 17:39:19 -05:00
tokens . next ( ) ;
2020-01-11 19:16:59 -05:00
let mut toks = Vec ::new ( ) ;
2020-01-14 17:39:19 -05:00
while let Some ( Token { kind , .. } ) = tokens . peek ( ) {
2020-01-11 19:16:59 -05:00
if kind = = & TokenKind ::Symbol ( Symbol ::CloseParen ) {
break ;
}
2020-01-14 17:39:19 -05:00
let tok = tokens . next ( ) . unwrap ( ) ;
2020-01-11 19:16:59 -05:00
toks . push ( tok . kind . clone ( ) ) ;
}
2020-01-14 17:39:19 -05:00
tokens . next ( ) ;
self . selectors
. push ( SelectorKind ::PseudoParen ( s . clone ( ) , toks ) )
2020-01-11 19:16:59 -05:00
} else {
2020-01-14 17:39:19 -05:00
self . selectors . push ( SelectorKind ::Pseudo ( s . clone ( ) ) )
2020-01-11 19:16:59 -05:00
}
} else {
todo! ( " expected ident after `:` in selector " )
}
}
2020-01-14 17:39:19 -05:00
fn tokens_to_selectors ( & mut self , tokens : & '_ mut Peekable < Iter < '_ , Token > > ) {
while tokens . peek ( ) . is_some ( ) {
self . consume_selector ( tokens )
}
}
fn consume_selector ( & mut self , tokens : & '_ mut Peekable < Iter < '_ , Token > > ) {
if devour_whitespace ( tokens ) {
2020-01-11 16:12:23 -05:00
if let Some ( & & Token {
kind : TokenKind ::Symbol ( Symbol ::Comma ) ,
..
2020-01-14 17:39:19 -05:00
} ) = tokens . peek ( )
2020-01-11 16:12:23 -05:00
{
2020-01-14 17:39:19 -05:00
tokens . next ( ) ;
self . selectors . push ( SelectorKind ::Multiple ) ;
return ;
2020-01-11 16:12:23 -05:00
}
2020-01-14 17:39:19 -05:00
self . selectors . push ( SelectorKind ::Whitespace ) ;
return ;
2020-01-11 14:51:31 -05:00
}
2020-01-14 17:39:19 -05:00
if let Some ( Token { kind , .. } ) = tokens . next ( ) {
match & kind {
2020-01-18 15:47:51 -05:00
TokenKind ::Ident ( ident ) = > {
self . selectors . push ( SelectorKind ::Element ( ident . clone ( ) ) )
}
2020-01-18 10:26:29 -05:00
TokenKind ::Unit ( u ) = > self . selectors . push ( SelectorKind ::Element ( u . to_string ( ) ) ) ,
2020-01-14 17:39:19 -05:00
TokenKind ::Symbol ( Symbol ::Period ) = > self . selectors . push ( SelectorKind ::Class ) ,
TokenKind ::Symbol ( Symbol ::Hash ) = > self . selectors . push ( SelectorKind ::Id ) ,
TokenKind ::Symbol ( Symbol ::Colon ) = > self . consume_pseudo_selector ( tokens ) ,
TokenKind ::Symbol ( Symbol ::Comma ) = > self . selectors . push ( SelectorKind ::Multiple ) ,
TokenKind ::Symbol ( Symbol ::Gt ) = > self . selectors . push ( SelectorKind ::ImmediateChild ) ,
TokenKind ::Symbol ( Symbol ::Plus ) = > self . selectors . push ( SelectorKind ::Following ) ,
TokenKind ::Symbol ( Symbol ::Tilde ) = > self . selectors . push ( SelectorKind ::Preceding ) ,
TokenKind ::Symbol ( Symbol ::Mul ) = > self . selectors . push ( SelectorKind ::Universal ) ,
TokenKind ::Symbol ( Symbol ::BitAnd ) = > self . selectors . push ( SelectorKind ::Super ) ,
TokenKind ::Interpolation = > self . tokens_to_selectors (
& mut eat_interpolation ( tokens , self . scope ) . iter ( ) . peekable ( ) ,
) ,
TokenKind ::Attribute ( attr ) = > {
self . selectors . push ( SelectorKind ::Attribute ( attr . clone ( ) ) )
}
2020-01-11 14:51:31 -05:00
_ = > todo! ( " unimplemented selector " ) ,
2020-01-14 17:39:19 -05:00
} ;
2020-01-04 22:55:04 -05:00
}
}
}
impl Selector {
2020-01-11 14:51:31 -05:00
pub fn from_tokens < ' a > (
2020-01-14 17:39:19 -05:00
tokens : & ' a mut Peekable < Iter < ' a , Token > > ,
2020-01-11 14:51:31 -05:00
super_selector : & ' a Selector ,
2020-01-12 17:44:49 -05:00
scope : & ' a Scope ,
2020-01-11 14:51:31 -05:00
) -> Selector {
2020-01-14 17:39:19 -05:00
SelectorParser ::new ( super_selector , scope ) . all_selectors ( tokens )
2020-01-04 22:55:04 -05:00
}
2020-01-05 12:25:03 -05:00
pub fn zip ( self , other : Selector ) -> Selector {
2020-01-11 14:51:31 -05:00
if self . 0. is_empty ( ) {
return Selector ( other . 0 ) ;
2020-01-05 12:25:03 -05:00
}
2020-01-11 16:12:23 -05:00
let mut rules : Vec < SelectorKind > = Vec ::with_capacity ( self . 0. len ( ) + other . 0. len ( ) ) ;
2020-01-11 14:51:31 -05:00
let sel1_split : Vec < Vec < SelectorKind > > = self
. 0
. split ( | sel | sel = = & SelectorKind ::Multiple )
. map ( | x | x . to_vec ( ) )
. collect ( ) ;
let sel2_split : Vec < Vec < SelectorKind > > = other
. 0
. split ( | sel | sel = = & SelectorKind ::Multiple )
. map ( | x | x . to_vec ( ) )
. collect ( ) ;
for ( idx , sel1 ) in sel1_split . iter ( ) . enumerate ( ) {
for ( idx2 , sel2 ) in sel2_split . iter ( ) . enumerate ( ) {
2020-01-11 16:12:23 -05:00
let mut this_selector = Vec ::with_capacity ( other . 0. len ( ) ) ;
let mut found_super = false ;
for sel in sel2 {
if sel = = & SelectorKind ::Super {
this_selector . extend ( sel1 . iter ( ) . cloned ( ) ) ;
found_super = true ;
} else {
this_selector . push ( sel . clone ( ) ) ;
}
}
if ! found_super {
rules . extend ( sel1 . iter ( ) . cloned ( ) ) ;
rules . push ( SelectorKind ::Whitespace ) ;
}
rules . extend ( this_selector ) ;
2020-01-11 14:51:31 -05:00
if ! ( idx + 1 = = sel1_split . len ( ) & & idx2 + 1 = = sel2_split . len ( ) ) {
rules . push ( SelectorKind ::Multiple ) ;
}
2020-01-05 12:25:03 -05:00
}
}
2020-01-11 14:51:31 -05:00
Selector ( rules )
2020-01-05 12:25:03 -05:00
}
2020-01-04 22:55:04 -05:00
}
#[ 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 ,
}