2020-02-22 17:26:30 -05:00
use crate ::common ::{ Scope , Symbol , Whitespace } ;
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-01-26 18:43:07 -05:00
use crate ::utils ::{
2020-02-24 15:07:18 -05:00
devour_whitespace , devour_whitespace_or_comment , flatten_ident , parse_interpolation ,
parse_quoted_string , IsWhitespace ,
2020-01-26 18:43:07 -05:00
} ;
2020-01-12 19:56:58 -05:00
use crate ::{ Token , TokenKind } ;
2020-02-22 17:26:30 -05:00
use std ::fmt ::{ self , Display , Write } ;
2020-01-04 22:55:04 -05:00
use std ::iter ::Peekable ;
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-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 {
TokenKind ::Ident ( s ) = > {
if let Some ( Token {
kind : TokenKind ::Symbol ( Symbol ::OpenParen ) ,
..
} ) = tokens . peek ( )
{
tokens . next ( ) ;
2020-02-02 15:21:47 -05:00
devour_whitespace_or_comment ( tokens ) ;
let mut toks = String ::new ( ) ;
2020-02-02 15:06:46 -05:00
while let Some ( Token { kind , .. } ) = tokens . peek ( ) {
if kind = = & TokenKind ::Symbol ( Symbol ::CloseParen ) {
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-02-02 15:06:46 -05:00
TokenKind ::Symbol ( Symbol ::Colon ) = > {
if let Some ( Token {
kind : TokenKind ::Ident ( s ) ,
..
} ) = tokens . next ( )
{
self . selectors . push ( SelectorKind ::PseudoElement ( s ) )
}
}
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-01-26 16:55:06 -05:00
if devour_whitespace_or_comment ( tokens ) {
2020-01-18 20:24:28 -05:00
if let Some ( Token {
2020-01-11 16:12:23 -05:00
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 ) ;
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-01-14 17:39:19 -05:00
if let Some ( Token { kind , .. } ) = tokens . next ( ) {
2020-01-18 20:24:28 -05:00
match kind {
2020-02-14 11:52:31 -05:00
TokenKind ::Ident ( v ) | TokenKind ::Number ( v ) = > {
self . selectors . push ( SelectorKind ::Element ( v ) )
}
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 ) ,
2020-02-17 09:40:30 -05:00
TokenKind ::Symbol ( Symbol ::Colon ) = > self . consume_pseudo_selector ( tokens ) ? ,
2020-02-22 17:26:30 -05:00
TokenKind ::Symbol ( Symbol ::Comma ) = > {
self . selectors . push ( SelectorKind ::Multiple ) ;
if tokens . peek ( ) . unwrap ( ) . kind = = TokenKind ::Whitespace ( Whitespace ::Newline ) {
self . selectors . push ( SelectorKind ::Newline ) ;
devour_whitespace ( tokens ) ;
}
}
2020-01-14 17:39:19 -05:00
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 ) ,
2020-01-29 21:02:32 -05:00
TokenKind ::Symbol ( Symbol ::Percent ) = > {
self . selectors . push ( SelectorKind ::Placeholder )
}
2020-01-26 11:07:57 -05:00
TokenKind ::Symbol ( Symbol ::BitAnd ) = > self . selectors . push ( if self . is_interpolated {
SelectorKind ::InterpolatedSuper
} else {
SelectorKind ::Super
} ) ,
TokenKind ::Interpolation = > {
self . is_interpolated = true ;
2020-02-09 19:07:44 -05:00
self . tokens_to_selectors (
2020-03-01 12:03:14 -05:00
& mut Lexer ::new (
& parse_interpolation (
tokens ,
self . scope ,
& Selector ( vec! [ SelectorKind ::Element ( String ::from ( " & " ) ) ] ) ,
) ?
. to_string ( ) ,
)
. peekable ( ) ,
2020-02-17 07:18:54 -05:00
) ? ;
2020-01-26 11:07:57 -05:00
self . is_interpolated = false ;
}
2020-03-01 12:03:14 -05:00
TokenKind ::Symbol ( Symbol ::OpenSquareBrace ) = > self . selectors . push (
Attribute ::from_tokens ( tokens , self . scope , self . super_selector ) ? ,
) ,
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 {
TokenKind ::Ident ( mut s ) = > {
2020-03-01 12:03:14 -05:00
s . push_str ( & flatten_ident ( toks , scope , super_selector ) ? ) ;
2020-02-24 15:07:18 -05:00
s
}
2020-03-01 12:03:14 -05:00
TokenKind ::Interpolation = > {
parse_interpolation ( toks , scope , super_selector ) ? . to_string ( )
}
2020-02-24 15:07:18 -05:00
q @ TokenKind ::Symbol ( Symbol ::DoubleQuote )
| q @ TokenKind ::Symbol ( Symbol ::SingleQuote ) = > {
2020-03-01 12:03:14 -05:00
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 {
TokenKind ::Ident ( s ) if s . len ( ) = = 1 = > {
devour_whitespace ( toks ) ;
match toks . next ( ) . unwrap ( ) . kind {
TokenKind ::Symbol ( Symbol ::CloseSquareBrace ) = > { }
_ = > return Err ( " expected \" ] \" . " . into ( ) ) ,
}
return Ok ( SelectorKind ::Attribute ( Attribute {
kind : AttributeKind ::Any ,
attr ,
value : String ::new ( ) ,
modifier : s ,
} ) ) ;
}
TokenKind ::Symbol ( Symbol ::CloseSquareBrace ) = > {
return Ok ( SelectorKind ::Attribute ( Attribute {
kind : AttributeKind ::Any ,
attr ,
value : String ::new ( ) ,
modifier : String ::new ( ) ,
} ) ) ;
}
TokenKind ::Symbol ( Symbol ::Equal ) = > AttributeKind ::Equals ,
TokenKind ::Symbol ( Symbol ::Tilde ) = > AttributeKind ::InList ,
TokenKind ::Symbol ( Symbol ::BitOr ) = > AttributeKind ::BeginsWithHyphenOrExact ,
TokenKind ::Symbol ( Symbol ::Xor ) = > AttributeKind ::StartsWith ,
TokenKind ::Symbol ( Symbol ::Dollar ) = > AttributeKind ::EndsWith ,
TokenKind ::Symbol ( Symbol ::Mul ) = > AttributeKind ::Contains ,
_ = > return Err ( " Expected \" ] \" . " . into ( ) ) ,
}
} else {
todo! ( )
} ;
if kind ! = AttributeKind ::Equals {
match toks . next ( ) . unwrap ( ) . kind {
TokenKind ::Symbol ( Symbol ::Equal ) = > { }
_ = > 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 {
TokenKind ::Ident ( mut s ) = > {
2020-03-01 12:03:14 -05:00
s . push_str ( & flatten_ident ( toks , scope , super_selector ) ? ) ;
2020-02-24 15:07:18 -05:00
s
}
q @ TokenKind ::Symbol ( Symbol ::DoubleQuote )
| q @ TokenKind ::Symbol ( Symbol ::SingleQuote ) = > {
2020-03-01 12:03:14 -05:00
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 {
TokenKind ::Symbol ( Symbol ::CloseSquareBrace ) = > {
return Ok ( SelectorKind ::Attribute ( Attribute {
kind ,
attr ,
value ,
modifier : String ::new ( ) ,
} ) )
}
TokenKind ::Ident ( s ) if s . len ( ) = = 1 = > {
match toks . next ( ) . unwrap ( ) . kind {
TokenKind ::Symbol ( Symbol ::CloseSquareBrace ) = > { }
_ = > return Err ( " expected \" ] \" . " . into ( ) ) ,
}
format! ( " {} " , s )
}
_ = > 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 ,
}