2020-03-01 14:53:52 -05:00
use std ::fmt ::{ self , Display , Write } ;
use std ::iter ::Peekable ;
use std ::string ::ToString ;
2020-02-17 07:18:54 -05:00
use crate ::error ::SassResult ;
2020-03-01 12:03:14 -05:00
use crate ::lexer ::Lexer ;
2020-03-17 20:13:53 -04:00
use crate ::scope ::Scope ;
2020-01-26 18:43:07 -05:00
use crate ::utils ::{
2020-03-29 13:28:17 -04:00
devour_whitespace , devour_whitespace_or_comment , eat_ident , eat_ident_no_interpolation ,
parse_interpolation , parse_quoted_string , IsWhitespace ,
2020-01-26 18:43:07 -05:00
} ;
2020-03-29 13:28:17 -04:00
use crate ::Token ;
2020-01-04 22:55:04 -05:00
#[ derive(Clone, Debug, Eq, PartialEq) ]
2020-01-20 13:15:47 -05:00
pub ( crate ) struct Selector ( pub Vec < SelectorKind > ) ;
2020-01-11 14:51:31 -05:00
2020-02-22 10:25:30 -05:00
impl Selector {
2020-02-22 17:57:13 -05:00
pub const fn new ( ) -> Selector {
2020-02-22 10:25:30 -05:00
Selector ( Vec ::new ( ) )
}
}
2020-01-11 14:51:31 -05:00
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-02-02 15:06:46 -05:00
| SelectorKind ::PseudoElement ( _ )
2020-01-11 19:16:59 -05:00
| SelectorKind ::PseudoParen ( .. )
2020-01-11 14:51:31 -05:00
| SelectorKind ::Class
| SelectorKind ::Id
| SelectorKind ::Universal
2020-01-26 11:17:48 -05:00
| SelectorKind ::InterpolatedSuper
2020-01-11 14:51:31 -05:00
| SelectorKind ::Element ( _ ) = > {
write! ( f , " {} " , s ) ? ;
if devour_whitespace ( & mut iter ) {
match iter . peek ( ) {
Some ( SelectorKind ::Attribute ( _ ) )
| Some ( SelectorKind ::Pseudo ( _ ) )
2020-02-02 15:06:46 -05:00
| Some ( SelectorKind ::PseudoElement ( _ ) )
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 )
2020-01-26 11:17:48 -05:00
| Some ( SelectorKind ::InterpolatedSuper )
2020-01-11 14:51:31 -05:00
| Some ( SelectorKind ::Element ( _ ) ) = > {
2020-01-11 16:12:23 -05:00
write! ( f , " " ) ? ;
2020-01-11 14:51:31 -05:00
}
_ = > { }
}
}
}
2020-01-26 19:16:26 -05:00
SelectorKind ::Multiple = > {
devour_whitespace ( & mut iter ) ;
while let Some ( sel ) = iter . peek ( ) {
if sel ! = & & SelectorKind ::Multiple {
2020-02-22 17:26:30 -05:00
write! ( f , " , " ) ? ;
2020-02-22 18:33:42 -05:00
if sel = = & & SelectorKind ::Newline {
iter . next ( ) ;
f . write_char ( '\n' ) ? ;
} else {
2020-02-22 17:26:30 -05:00
f . write_char ( ' ' ) ? ;
}
2020-01-26 19:16:26 -05:00
break ;
}
iter . next ( ) ;
devour_whitespace ( & mut iter ) ;
}
2020-02-22 18:33:42 -05:00
while let Some ( sel ) = iter . peek ( ) {
if sel ! = & & SelectorKind ::Multiple
& & sel ! = & & SelectorKind ::Newline
& & ! sel . is_whitespace ( )
{
break ;
}
iter . next ( ) ;
}
2020-01-26 19:16:26 -05:00
}
2020-01-11 14:51:31 -05:00
_ = > write! ( f , " {} " , s ) ? ,
}
}
write! ( f , " " )
}
}
#[ derive(Clone, Debug, Eq, PartialEq) ]
2020-01-20 13:15:47 -05:00
pub ( crate ) 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-02-22 17:26:30 -05:00
/// Newline (significant if after `SelectorKind::Multiple`)
Newline ,
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-02-02 15:06:46 -05:00
/// Pseudo element selector: `::before`
PseudoElement ( String ) ,
2020-01-11 19:16:59 -05:00
/// Pseudo selector with additional parens: `:any(h1, h2, h3, h4, h5, h6)`
2020-02-02 15:21:47 -05:00
PseudoParen ( String , String ) ,
2020-01-11 14:51:31 -05:00
/// Use the super selector: `&.red`
2020-01-11 16:12:23 -05:00
Super ,
2020-01-26 11:07:57 -05:00
/// Super selector in an interpolated context: `a #{&}`
InterpolatedSuper ,
2020-01-29 20:02:02 -05:00
/// Placeholder selector: `%alert`
Placeholder ,
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 , " , " ) ,
2020-02-22 17:26:30 -05:00
SelectorKind ::Newline = > writeln! ( f ) ,
2020-01-11 14:51:31 -05:00
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-02-02 15:06:46 -05:00
SelectorKind ::PseudoElement ( s ) = > write! ( f , " ::{} " , s ) ,
2020-02-02 18:01:09 -05:00
SelectorKind ::PseudoParen ( s , val ) = > write! ( f , " :{}({}) " , s , val ) ,
2020-02-22 17:26:30 -05:00
SelectorKind ::Super | SelectorKind ::InterpolatedSuper = > write! ( f , " " ) ,
2020-01-29 20:02:02 -05:00
SelectorKind ::Placeholder = > write! ( f , " % " ) ,
2020-01-11 14:51:31 -05:00
}
}
}
2020-01-04 22:55:04 -05:00
struct SelectorParser < ' a > {
2020-01-12 17:44:49 -05:00
scope : & ' a Scope ,
2020-03-01 12:03:14 -05:00
super_selector : & ' a Selector ,
2020-01-14 17:39:19 -05:00
selectors : Vec < SelectorKind > ,
2020-01-26 11:07:57 -05:00
is_interpolated : bool ,
2020-01-12 10:54:46 -05:00
}
impl < ' a > SelectorParser < ' a > {
2020-03-01 12:03:14 -05:00
const fn new ( scope : & ' a Scope , super_selector : & ' a Selector ) -> SelectorParser < ' a > {
2020-01-11 14:51:31 -05:00
SelectorParser {
2020-01-12 17:44:49 -05:00
scope ,
2020-03-01 12:03:14 -05:00
super_selector ,
2020-01-14 17:39:19 -05:00
selectors : Vec ::new ( ) ,
2020-01-26 11:07:57 -05:00
is_interpolated : false ,
2020-01-11 14:51:31 -05:00
}
2020-01-04 22:55:04 -05:00
}
2020-03-01 12:03:14 -05:00
fn all_selectors < I : Iterator < Item = Token > > (
mut self ,
tokens : & ' a mut Peekable < I > ,
) -> SassResult < Selector > {
2020-02-17 07:18:54 -05:00
self . tokens_to_selectors ( tokens ) ? ;
2020-01-14 17:39:19 -05:00
// 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-02-17 07:18:54 -05:00
Ok ( Selector ( self . selectors ) )
2020-01-11 14:51:31 -05:00
}
2020-03-01 12:03:14 -05:00
fn consume_pseudo_selector < I : Iterator < Item = Token > > (
2020-02-17 09:40:30 -05:00
& mut self ,
2020-03-01 12:03:14 -05:00
tokens : & '_ mut Peekable < I > ,
2020-02-17 09:40:30 -05:00
) -> SassResult < ( ) > {
2020-02-02 15:06:46 -05:00
if let Some ( tok ) = tokens . next ( ) {
match tok . kind {
2020-03-29 13:28:17 -04:00
v @ 'a' ..= 'z' | v @ 'A' ..= 'Z' | v @ '-' | v @ '_' = > {
let s = format! (
" {}{} " ,
v ,
2020-03-30 17:06:23 -04:00
eat_ident ( tokens , self . scope , self . super_selector ) ?
2020-03-29 13:28:17 -04:00
) ;
if let Some ( Token { kind : ' ( ' , .. } ) = tokens . peek ( ) {
2020-02-02 15:06:46 -05:00
tokens . next ( ) ;
2020-03-29 13:28:17 -04:00
devour_whitespace ( tokens ) ;
2020-02-02 15:21:47 -05:00
let mut toks = String ::new ( ) ;
2020-02-02 15:06:46 -05:00
while let Some ( Token { kind , .. } ) = tokens . peek ( ) {
2020-03-29 13:28:17 -04:00
if kind = = & ')' {
2020-02-02 15:21:47 -05:00
tokens . next ( ) ;
2020-02-02 15:06:46 -05:00
break ;
}
let tok = tokens . next ( ) . unwrap ( ) ;
2020-02-02 15:21:47 -05:00
toks . push_str ( & tok . kind . to_string ( ) ) ;
if devour_whitespace ( tokens ) {
toks . push ( ' ' ) ;
}
2020-02-02 15:06:46 -05:00
}
2020-02-02 18:01:09 -05:00
self . selectors
. push ( SelectorKind ::PseudoParen ( s , toks . trim_end ( ) . to_owned ( ) ) )
2020-02-02 15:06:46 -05:00
} else {
self . selectors . push ( SelectorKind ::Pseudo ( s ) )
2020-01-11 19:16:59 -05:00
}
}
2020-03-29 13:28:17 -04:00
':' = > {
2020-03-30 17:06:23 -04:00
let s = eat_ident ( tokens , self . scope , self . super_selector ) ? ;
2020-03-29 13:28:17 -04:00
self . selectors . push ( SelectorKind ::PseudoElement ( s ) )
2020-02-02 15:06:46 -05:00
}
2020-02-17 09:40:30 -05:00
_ = > return Err ( " Expected identifier. " . into ( ) ) ,
2020-01-11 19:16:59 -05:00
}
}
2020-02-17 09:40:30 -05:00
Ok ( ( ) )
2020-01-11 19:16:59 -05:00
}
2020-03-01 12:03:14 -05:00
fn tokens_to_selectors < I : Iterator < Item = Token > > (
& mut self ,
tokens : & '_ mut Peekable < I > ,
) -> SassResult < ( ) > {
2020-01-14 17:39:19 -05:00
while tokens . peek ( ) . is_some ( ) {
2020-02-17 07:18:54 -05:00
self . consume_selector ( tokens ) ? ;
2020-01-14 17:39:19 -05:00
}
2020-02-17 07:18:54 -05:00
Ok ( ( ) )
2020-01-14 17:39:19 -05:00
}
2020-03-01 12:03:14 -05:00
fn consume_selector < I : Iterator < Item = Token > > (
& mut self ,
tokens : & '_ mut Peekable < I > ,
) -> SassResult < ( ) > {
2020-03-29 13:28:17 -04:00
if devour_whitespace_or_comment ( tokens ) ? {
if let Some ( Token { kind : ' , ' , .. } ) = tokens . peek ( ) {
2020-01-14 17:39:19 -05:00
tokens . next ( ) ;
self . selectors . push ( SelectorKind ::Multiple ) ;
2020-02-17 07:18:54 -05:00
return Ok ( ( ) ) ;
2020-01-11 16:12:23 -05:00
}
2020-01-14 17:39:19 -05:00
self . selectors . push ( SelectorKind ::Whitespace ) ;
2020-02-17 07:18:54 -05:00
return Ok ( ( ) ) ;
2020-01-11 14:51:31 -05:00
}
2020-03-30 00:18:37 -04:00
if let Some ( Token { kind , .. } ) = tokens . peek ( ) {
2020-01-18 20:24:28 -05:00
match kind {
2020-03-30 00:18:37 -04:00
'a' ..= 'z' | 'A' ..= 'Z' | '-' | '_' | '0' ..= '9' | '\\' = > {
let s = eat_ident_no_interpolation ( tokens ) ? ;
2020-03-29 13:28:17 -04:00
self . selectors . push ( SelectorKind ::Element ( s ) )
}
2020-03-30 00:18:37 -04:00
'.' = > {
tokens . next ( ) ;
self . selectors . push ( SelectorKind ::Class )
}
2020-03-29 13:28:17 -04:00
'#' = > {
2020-03-30 00:18:37 -04:00
tokens . next ( ) ;
2020-03-30 02:30:44 -04:00
if tokens . peek ( ) . is_some ( ) & & tokens . peek ( ) . unwrap ( ) . kind = = '{' {
2020-03-29 13:28:17 -04:00
tokens . next ( ) ;
self . is_interpolated = true ;
self . tokens_to_selectors (
& mut Lexer ::new (
& parse_interpolation ( tokens , self . scope , self . super_selector ) ?
. to_string ( ) ,
)
. peekable ( ) ,
) ? ;
self . is_interpolated = false ;
} else {
self . selectors . push ( SelectorKind ::Id )
}
2020-02-14 11:52:31 -05:00
}
2020-03-30 00:18:37 -04:00
':' = > {
tokens . next ( ) ;
self . consume_pseudo_selector ( tokens ) ?
}
2020-03-29 13:28:17 -04:00
',' = > {
2020-03-30 00:18:37 -04:00
tokens . next ( ) ;
2020-02-22 17:26:30 -05:00
self . selectors . push ( SelectorKind ::Multiple ) ;
2020-03-29 13:28:17 -04:00
if tokens . peek ( ) . unwrap ( ) . kind = = '\n' {
2020-02-22 17:26:30 -05:00
self . selectors . push ( SelectorKind ::Newline ) ;
devour_whitespace ( tokens ) ;
}
}
2020-03-30 00:18:37 -04:00
'>' = > {
tokens . next ( ) ;
self . selectors . push ( SelectorKind ::ImmediateChild )
}
'+' = > {
tokens . next ( ) ;
self . selectors . push ( SelectorKind ::Following )
}
'~' = > {
tokens . next ( ) ;
self . selectors . push ( SelectorKind ::Preceding )
}
'*' = > {
tokens . next ( ) ;
self . selectors . push ( SelectorKind ::Universal )
}
'%' = > {
tokens . next ( ) ;
self . selectors . push ( SelectorKind ::Placeholder )
}
2020-03-29 13:28:17 -04:00
'&' = > self . selectors . push ( if self . is_interpolated {
2020-03-30 00:18:37 -04:00
tokens . next ( ) ;
2020-01-26 11:07:57 -05:00
SelectorKind ::InterpolatedSuper
} else {
2020-03-30 00:18:37 -04:00
tokens . next ( ) ;
2020-01-26 11:07:57 -05:00
SelectorKind ::Super
} ) ,
2020-03-30 00:18:37 -04:00
'[' = > {
tokens . next ( ) ;
self . selectors . push ( Attribute ::from_tokens (
tokens ,
self . scope ,
self . super_selector ,
) ? )
}
2020-04-02 02:14:59 -04:00
c if c . is_control ( ) = > {
return Err ( " expected selector. " . into ( ) ) ;
}
2020-04-02 02:23:48 -04:00
'`' = > return Err ( " expected selector. " . into ( ) ) ,
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
}
2020-02-17 07:18:54 -05:00
Ok ( ( ) )
2020-01-04 22:55:04 -05:00
}
}
impl Selector {
2020-03-01 12:03:14 -05:00
pub fn from_tokens < ' a , I : Iterator < Item = Token > > (
tokens : & ' a mut Peekable < I > ,
2020-01-12 17:44:49 -05:00
scope : & ' a Scope ,
2020-03-01 12:03:14 -05:00
super_selector : & ' a Selector ,
2020-02-17 07:18:54 -05:00
) -> SassResult < Selector > {
2020-03-01 12:03:14 -05:00
SelectorParser ::new ( scope , super_selector ) . all_selectors ( tokens )
2020-01-04 22:55:04 -05:00
}
2020-01-05 12:25:03 -05:00
2020-01-18 21:05:26 -05:00
pub fn zip ( & self , other : & Selector ) -> Selector {
2020-01-11 14:51:31 -05:00
if self . 0. is_empty ( ) {
2020-01-18 21:05:26 -05:00
return Selector ( other . 0. clone ( ) ) ;
2020-02-22 15:34:32 -05:00
} else if other . 0. is_empty ( ) {
return self . clone ( ) ;
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-18 21:05:26 -05:00
let sel1_split : Vec < & [ SelectorKind ] > =
self . 0. split ( | sel | sel = = & SelectorKind ::Multiple ) . collect ( ) ;
let sel2_split : Vec < & [ SelectorKind ] > = other
2020-01-11 14:51:31 -05:00
. 0
. split ( | sel | sel = = & SelectorKind ::Multiple )
. collect ( ) ;
2020-01-18 21:05:26 -05:00
let len1 = sel1_split . len ( ) ;
let len2 = sel2_split . len ( ) ;
for ( idx , sel1 ) in sel1_split . into_iter ( ) . enumerate ( ) {
2020-01-11 14:51:31 -05:00
for ( idx2 , sel2 ) in sel2_split . iter ( ) . enumerate ( ) {
2020-01-18 21:05:26 -05:00
let mut this_selector : Vec < SelectorKind > = Vec ::with_capacity ( other . 0. len ( ) ) ;
2020-01-11 16:12:23 -05:00
let mut found_super = false ;
2020-01-18 21:05:26 -05:00
for sel in * sel2 {
2020-01-11 16:12:23 -05:00
if sel = = & SelectorKind ::Super {
2020-01-18 21:05:26 -05:00
this_selector . extend ( sel1 . to_vec ( ) ) ;
2020-01-11 16:12:23 -05:00
found_super = true ;
2020-01-26 11:07:57 -05:00
} else if sel = = & SelectorKind ::InterpolatedSuper {
this_selector . extend ( sel1 . to_vec ( ) ) ;
2020-01-11 16:12:23 -05:00
} else {
this_selector . push ( sel . clone ( ) ) ;
}
}
if ! found_super {
2020-01-18 21:05:26 -05:00
rules . extend ( sel1 . to_vec ( ) ) ;
2020-01-11 16:12:23 -05:00
rules . push ( SelectorKind ::Whitespace ) ;
}
rules . extend ( this_selector ) ;
2020-01-18 21:05:26 -05:00
if ! ( idx + 1 = = len1 & & idx2 + 1 = = len2 ) {
2020-01-11 14:51:31 -05:00
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-02-29 17:23:17 -05:00
pub fn remove_placeholders ( self ) -> Selector {
let mut selectors = Vec ::with_capacity ( self . 0. len ( ) ) ;
let mut temp_sels = Vec ::new ( ) ;
let mut found_placeholder = false ;
for sel in self . 0 {
match sel {
SelectorKind ::Placeholder = > found_placeholder = true ,
SelectorKind ::Multiple = > {
temp_sels . push ( SelectorKind ::Multiple ) ;
if ! found_placeholder {
selectors . extend ( temp_sels . clone ( ) ) ;
}
temp_sels . clear ( ) ;
found_placeholder = false ;
}
_ = > temp_sels . push ( sel ) ,
}
}
if ! found_placeholder {
selectors . extend ( temp_sels ) ;
}
Selector ( selectors )
}
pub fn is_empty ( & self ) -> bool {
self . 0. is_empty ( )
}
2020-01-04 22:55:04 -05:00
}
#[ derive(Clone, Debug, Eq, PartialEq) ]
2020-01-26 11:47:00 -05:00
pub ( crate ) struct Attribute {
2020-01-04 22:55:04 -05:00
pub attr : String ,
pub value : String ,
2020-02-24 15:07:18 -05:00
pub modifier : String ,
2020-01-04 22:55:04 -05:00
pub kind : AttributeKind ,
}
2020-02-24 15:07:18 -05:00
impl Attribute {
2020-03-01 12:03:14 -05:00
pub fn from_tokens < I : Iterator < Item = Token > > (
toks : & mut Peekable < I > ,
2020-02-24 15:07:18 -05:00
scope : & Scope ,
2020-03-01 12:03:14 -05:00
super_selector : & Selector ,
2020-02-24 15:07:18 -05:00
) -> SassResult < SelectorKind > {
devour_whitespace ( toks ) ;
let attr = if let Some ( t ) = toks . next ( ) {
match t . kind {
2020-03-29 13:28:17 -04:00
v @ 'a' ..= 'z' | v @ 'A' ..= 'Z' | v @ '-' | v @ '_' = > {
format! ( " {} {} " , v , eat_ident ( toks , scope , super_selector ) ? )
2020-02-24 15:07:18 -05:00
}
2020-03-29 13:28:17 -04:00
'#' if toks . next ( ) . unwrap ( ) . kind = = '{' = > {
2020-03-01 12:03:14 -05:00
parse_interpolation ( toks , scope , super_selector ) ? . to_string ( )
}
2020-03-29 13:28:17 -04:00
q @ '"' | q @ '\'' = > {
parse_quoted_string ( toks , scope , q , super_selector ) ? . to_string ( )
2020-02-24 15:07:18 -05:00
}
_ = > return Err ( " Expected identifier. " . into ( ) ) ,
}
} else {
todo! ( )
} ;
2020-01-26 11:47:00 -05:00
2020-02-24 15:07:18 -05:00
devour_whitespace ( toks ) ;
let kind = if let Some ( t ) = toks . next ( ) {
match t . kind {
2020-03-29 13:28:17 -04:00
v @ 'a' ..= 'z' | v @ 'A' ..= 'Z' = > {
2020-02-24 15:07:18 -05:00
match toks . next ( ) . unwrap ( ) . kind {
2020-03-29 13:28:17 -04:00
']' = > { }
2020-02-24 15:07:18 -05:00
_ = > return Err ( " expected \" ] \" . " . into ( ) ) ,
}
return Ok ( SelectorKind ::Attribute ( Attribute {
kind : AttributeKind ::Any ,
attr ,
value : String ::new ( ) ,
2020-03-29 13:28:17 -04:00
modifier : v . to_string ( ) ,
2020-02-24 15:07:18 -05:00
} ) ) ;
}
2020-03-29 13:28:17 -04:00
']' = > {
2020-02-24 15:07:18 -05:00
return Ok ( SelectorKind ::Attribute ( Attribute {
kind : AttributeKind ::Any ,
attr ,
value : String ::new ( ) ,
modifier : String ::new ( ) ,
} ) ) ;
}
2020-03-29 13:28:17 -04:00
'=' = > AttributeKind ::Equals ,
'~' = > AttributeKind ::InList ,
'|' = > AttributeKind ::BeginsWithHyphenOrExact ,
'^' = > AttributeKind ::StartsWith ,
'$' = > AttributeKind ::EndsWith ,
'*' = > AttributeKind ::Contains ,
2020-02-24 15:07:18 -05:00
_ = > return Err ( " Expected \" ] \" . " . into ( ) ) ,
}
} else {
todo! ( )
} ;
if kind ! = AttributeKind ::Equals {
match toks . next ( ) . unwrap ( ) . kind {
2020-03-29 13:28:17 -04:00
'=' = > { }
2020-02-24 15:07:18 -05:00
_ = > return Err ( " expected \" = \" . " . into ( ) ) ,
}
2020-01-26 11:47:00 -05:00
}
2020-02-24 15:07:18 -05:00
devour_whitespace ( toks ) ;
let value = if let Some ( t ) = toks . next ( ) {
match t . kind {
2020-03-29 13:28:17 -04:00
v @ 'a' ..= 'z' | v @ 'A' ..= 'Z' | v @ '-' | v @ '_' = > {
format! ( " {} {} " , v , eat_ident ( toks , scope , super_selector ) ? )
2020-02-24 15:07:18 -05:00
}
2020-03-29 13:28:17 -04:00
q @ '"' | q @ '\'' = > {
parse_quoted_string ( toks , scope , q , super_selector ) ? . to_string ( )
2020-02-24 15:07:18 -05:00
}
_ = > return Err ( " Expected identifier. " . into ( ) ) ,
}
} else {
todo! ( )
} ;
devour_whitespace ( toks ) ;
let modifier = if let Some ( t ) = toks . next ( ) {
match t . kind {
2020-03-29 13:28:17 -04:00
']' = > {
2020-02-24 15:07:18 -05:00
return Ok ( SelectorKind ::Attribute ( Attribute {
kind ,
attr ,
value ,
modifier : String ::new ( ) ,
} ) )
}
2020-03-29 13:28:17 -04:00
v @ 'a' ..= 'z' | v @ 'A' ..= 'Z' = > {
2020-02-24 15:07:18 -05:00
match toks . next ( ) . unwrap ( ) . kind {
2020-03-29 13:28:17 -04:00
']' = > { }
2020-02-24 15:07:18 -05:00
_ = > return Err ( " expected \" ] \" . " . into ( ) ) ,
}
2020-03-29 13:28:17 -04:00
format! ( " {} " , v )
2020-02-24 15:07:18 -05:00
}
_ = > return Err ( " Expected \" ] \" . " . into ( ) ) ,
}
} else {
todo! ( )
} ;
Ok ( SelectorKind ::Attribute ( Attribute {
kind ,
attr ,
value ,
modifier ,
} ) )
2020-01-26 11:47:00 -05:00
}
}
2020-01-04 22:55:04 -05:00
impl Display for Attribute {
fn fmt ( & self , f : & mut fmt ::Formatter < '_ > ) -> fmt ::Result {
2020-01-26 11:47:00 -05:00
match self . kind {
2020-02-24 15:07:18 -05:00
AttributeKind ::Any = > write! ( f , " [{}{}] " , self . attr , self . modifier ) ,
AttributeKind ::Equals = > write! ( f , " [{}={}{}] " , self . attr , self . value , self . modifier ) ,
AttributeKind ::InList = > write! ( f , " [{}~={}{}] " , self . attr , self . value , self . modifier ) ,
2020-01-26 11:47:00 -05:00
AttributeKind ::BeginsWithHyphenOrExact = > {
2020-02-24 15:07:18 -05:00
write! ( f , " [{}|={}{}] " , self . attr , self . value , self . modifier )
2020-01-26 11:47:00 -05:00
}
AttributeKind ::StartsWith = > {
2020-02-24 15:07:18 -05:00
write! ( f , " [{}^={}{}] " , self . attr , self . value , self . modifier )
2020-01-26 11:47:00 -05:00
}
AttributeKind ::EndsWith = > {
2020-02-24 15:07:18 -05:00
write! ( f , " [{}$={}{}] " , self . attr , self . value , self . modifier )
2020-01-26 11:47:00 -05:00
}
AttributeKind ::Contains = > {
2020-02-24 15:07:18 -05:00
write! ( f , " [{}*={}{}] " , self . attr , self . value , self . modifier )
2020-01-04 22:55:04 -05:00
}
}
}
}
#[ 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 ,
}