2020-05-31 04:51:41 -04:00
use peekmore ::PeekMore ;
2020-04-12 19:37:12 -04:00
use codemap ::{ Span , Spanned } ;
2020-06-16 20:00:11 -04:00
use crate ::{
color ::Color ,
common ::{ Brackets , ListSeparator , Op , QuoteKind } ,
error ::SassResult ,
parse ::Parser ,
selector ::Selector ,
unit ::Unit ,
utils ::hex_char_for ,
{ Cow , Token } ,
} ;
2020-03-30 15:43:15 -04:00
2020-04-06 21:22:03 -04:00
use css_function ::is_special_function ;
2020-03-30 15:43:15 -04:00
pub ( crate ) use map ::SassMap ;
2020-02-08 18:43:18 -05:00
pub ( crate ) use number ::Number ;
2020-04-04 21:07:53 -04:00
pub ( crate ) use sass_function ::SassFunction ;
2020-01-25 09:58:53 -05:00
2020-06-16 19:38:30 -04:00
pub ( crate ) mod css_function ;
2020-03-30 15:43:15 -04:00
mod map ;
2020-02-08 18:43:18 -05:00
mod number ;
2020-02-08 16:07:37 -05:00
mod ops ;
2020-04-04 21:07:53 -04:00
mod sass_function ;
2020-01-25 09:58:53 -05:00
2020-03-30 15:43:15 -04:00
#[ derive(Debug, Clone, PartialEq, Eq) ]
2020-01-25 09:58:53 -05:00
pub ( crate ) enum Value {
Important ,
True ,
False ,
Null ,
2020-02-08 18:43:18 -05:00
Dimension ( Number , Unit ) ,
2020-03-23 19:56:24 -04:00
List ( Vec < Value > , ListSeparator , Brackets ) ,
2020-04-21 18:54:19 -04:00
Color ( Box < Color > ) ,
2020-03-21 12:14:02 -04:00
UnaryOp ( Op , Box < Value > ) ,
2020-01-25 10:54:25 -05:00
BinaryOp ( Box < Value > , Op , Box < Value > ) ,
2020-01-25 09:58:53 -05:00
Paren ( Box < Value > ) ,
2020-05-22 14:35:41 -04:00
String ( String , QuoteKind ) ,
2020-03-30 15:43:15 -04:00
Map ( SassMap ) ,
2020-04-12 19:37:12 -04:00
ArgList ( Vec < Spanned < Value > > ) ,
2020-04-03 21:38:34 -04:00
/// Returned by `get-function()`
2020-04-03 23:47:56 -04:00
Function ( SassFunction ) ,
2020-01-25 09:58:53 -05:00
}
2020-04-19 13:51:34 -04:00
fn visit_quoted_string ( buf : & mut String , force_double_quote : bool , string : & str ) -> SassResult < ( ) > {
let mut has_single_quote = false ;
let mut has_double_quote = false ;
let mut buffer = String ::new ( ) ;
if force_double_quote {
buffer . push ( '"' ) ;
}
let mut iter = string . chars ( ) . peekable ( ) ;
while let Some ( c ) = iter . next ( ) {
match c {
'\'' = > {
if force_double_quote {
buffer . push ( '\'' ) ;
} else if has_double_quote {
return visit_quoted_string ( buf , true , string ) ;
} else {
has_single_quote = true ;
buffer . push ( '\'' ) ;
}
}
'"' = > {
if force_double_quote {
buffer . push ( '\\' ) ;
buffer . push ( '"' ) ;
} else if has_single_quote {
return visit_quoted_string ( buf , true , string ) ;
} else {
has_double_quote = true ;
buffer . push ( '"' ) ;
}
}
'\x00' ..= '\x08' | '\x0A' ..= '\x1F' = > {
buffer . push ( '\\' ) ;
if c as u32 > 0xF {
buffer . push ( hex_char_for ( c as u32 > > 4 ) )
}
buffer . push ( hex_char_for ( c as u32 & 0xF ) ) ;
if iter . peek ( ) . is_none ( ) {
break ;
}
let next = iter . peek ( ) . unwrap ( ) ;
if next . is_ascii_hexdigit ( ) | | next = = & ' ' | | next = = & '\t' {
buffer . push ( ' ' ) ;
}
}
'\\' = > {
buffer . push ( '\\' ) ;
buffer . push ( '\\' ) ;
}
_ = > buffer . push ( c ) ,
}
}
if force_double_quote {
buffer . push ( '"' ) ;
} else {
let quote = if has_double_quote { '\'' } else { '"' } ;
buffer = format! ( " {} {} {} " , quote , buffer , quote ) ;
}
buf . push_str ( & buffer ) ;
Ok ( ( ) )
}
2020-04-12 19:37:12 -04:00
impl Value {
2020-04-19 00:39:18 -04:00
pub fn is_null ( & self , span : Span ) -> SassResult < bool > {
2020-04-26 18:27:08 -04:00
Ok ( match self {
2020-05-22 20:47:53 -04:00
Value ::Null = > true ,
2020-05-22 14:35:41 -04:00
Value ::String ( i , QuoteKind ::None ) if i . is_empty ( ) = > true ,
2020-04-19 00:39:18 -04:00
Self ::BinaryOp ( .. ) | Self ::Paren ( .. ) | Self ::UnaryOp ( .. ) = > {
2020-04-26 18:27:08 -04:00
self . clone ( ) . eval ( span ) ? . is_null ( span ) ?
2020-04-19 00:39:18 -04:00
}
2020-04-26 18:27:08 -04:00
Self ::List ( v , _ , Brackets ::Bracketed ) if v . is_empty ( ) = > false ,
2020-05-24 16:41:09 -04:00
Self ::List ( v , .. ) = > v
. iter ( )
. map ( | f | Ok ( f . is_null ( span ) ? ) )
. collect ::< SassResult < Vec < bool > > > ( ) ?
. into_iter ( )
. all ( | f | f ) ,
2020-04-26 18:27:08 -04:00
_ = > false ,
} )
2020-04-12 19:37:12 -04:00
}
2020-04-18 20:11:49 -04:00
2020-05-05 11:08:34 -04:00
pub fn to_css_string ( & self , span : Span ) -> SassResult < Cow < 'static , str > > {
2020-04-12 19:37:12 -04:00
Ok ( match self {
2020-05-25 13:09:20 -04:00
Self ::Important = > Cow ::const_str ( " !important " ) ,
2020-03-16 21:29:00 -04:00
Self ::Dimension ( num , unit ) = > match unit {
2020-03-18 20:11:14 -04:00
Unit ::Mul ( .. ) = > {
2020-04-18 20:11:49 -04:00
return Err ( ( format! ( " {} {} isn't a valid CSS value. " , num , unit ) , span ) . into ( ) ) ;
2020-03-16 21:29:00 -04:00
}
2020-05-25 13:09:20 -04:00
_ = > Cow ::owned ( format! ( " {} {} " , num , unit ) ) ,
2020-03-16 21:29:00 -04:00
} ,
2020-05-24 17:41:24 -04:00
Self ::Map ( .. ) | Self ::Function ( .. ) = > {
2020-04-26 23:11:04 -04:00
return Err ( (
format! ( " {} isn't a valid CSS value. " , self . inspect ( span ) ? ) ,
span ,
)
. into ( ) )
}
2020-03-23 19:56:24 -04:00
Self ::List ( vals , sep , brackets ) = > match brackets {
2020-05-25 13:09:20 -04:00
Brackets ::None = > Cow ::owned (
2020-03-23 19:56:24 -04:00
vals . iter ( )
2020-04-19 00:39:18 -04:00
. filter ( | x | ! x . is_null ( span ) . unwrap ( ) )
2020-04-12 19:37:12 -04:00
. map ( | x | x . to_css_string ( span ) )
2020-05-05 11:08:34 -04:00
. collect ::< SassResult < Vec < Cow < 'static , str > > > > ( ) ?
2020-03-23 19:56:24 -04:00
. join ( sep . as_str ( ) ) ,
) ,
2020-05-25 13:09:20 -04:00
Brackets ::Bracketed = > Cow ::owned ( format! (
2020-05-05 11:08:34 -04:00
" [{}] " ,
vals . iter ( )
. filter ( | x | ! x . is_null ( span ) . unwrap ( ) )
. map ( | x | x . to_css_string ( span ) )
. collect ::< SassResult < Vec < Cow < 'static , str > > > > ( ) ?
. join ( sep . as_str ( ) ) ,
) ) ,
2020-03-23 19:56:24 -04:00
} ,
2020-05-25 13:09:20 -04:00
Self ::Color ( c ) = > Cow ::owned ( c . to_string ( ) ) ,
2020-04-12 19:37:12 -04:00
Self ::UnaryOp ( .. ) | Self ::BinaryOp ( .. ) = > {
2020-04-21 18:22:26 -04:00
self . clone ( ) . eval ( span ) ? . to_css_string ( span ) ?
2020-04-12 19:37:12 -04:00
}
2020-04-21 18:22:26 -04:00
Self ::Paren ( val ) = > val . to_css_string ( span ) ? ,
2020-05-22 14:35:41 -04:00
Self ::String ( string , QuoteKind ::None ) = > {
2020-04-19 13:51:34 -04:00
let mut after_newline = false ;
let mut buf = String ::with_capacity ( string . len ( ) ) ;
for c in string . chars ( ) {
match c {
2020-04-20 12:12:39 -04:00
'\n' = > {
buf . push ( ' ' ) ;
after_newline = true ;
}
2020-04-19 13:51:34 -04:00
' ' = > {
if ! after_newline {
buf . push ( ' ' ) ;
2020-02-24 19:49:24 -05:00
}
}
2020-04-19 13:51:34 -04:00
_ = > {
buf . push ( c ) ;
after_newline = false ;
}
2020-02-24 19:49:24 -05:00
}
}
2020-05-25 13:09:20 -04:00
Cow ::owned ( buf )
2020-04-19 13:51:34 -04:00
}
2020-05-22 14:35:41 -04:00
Self ::String ( string , QuoteKind ::Quoted ) = > {
2020-04-19 13:51:34 -04:00
let mut buf = String ::with_capacity ( string . len ( ) ) ;
2020-05-22 14:20:31 -04:00
visit_quoted_string ( & mut buf , false , string ) ? ;
2020-05-25 13:09:20 -04:00
Cow ::owned ( buf )
2020-02-24 19:49:24 -05:00
}
2020-05-25 13:09:20 -04:00
Self ::True = > Cow ::const_str ( " true " ) ,
Self ::False = > Cow ::const_str ( " false " ) ,
Self ::Null = > Cow ::const_str ( " " ) ,
Self ::ArgList ( args ) = > Cow ::owned (
2020-05-05 11:08:34 -04:00
args . iter ( )
. filter ( | x | ! x . is_null ( span ) . unwrap ( ) )
2020-05-25 13:09:20 -04:00
. map ( | a | Ok ( a . node . to_css_string ( span ) ? ) )
. collect ::< SassResult < Vec < Cow < 'static , str > > > > ( ) ?
2020-05-05 11:08:34 -04:00
. join ( " , " ) ,
) ,
2020-04-12 19:37:12 -04:00
} )
2020-01-26 15:04:16 -05:00
}
2020-04-12 19:37:12 -04:00
pub fn is_true ( & self , span : Span ) -> SassResult < bool > {
2020-02-08 16:01:21 -05:00
match self {
2020-02-17 10:27:04 -05:00
Value ::Null | Value ::False = > Ok ( false ) ,
2020-03-23 15:21:59 -04:00
Self ::BinaryOp ( .. ) | Self ::Paren ( .. ) | Self ::UnaryOp ( .. ) = > {
2020-04-12 19:37:12 -04:00
self . clone ( ) . eval ( span ) ? . is_true ( span )
2020-03-23 15:21:59 -04:00
}
2020-02-17 10:27:04 -05:00
_ = > Ok ( true ) ,
2020-02-08 16:01:21 -05:00
}
2020-01-25 09:58:53 -05:00
}
2020-02-02 21:11:22 -05:00
pub fn unquote ( self ) -> Self {
match self {
2020-05-22 14:35:41 -04:00
Self ::String ( s1 , _ ) = > Self ::String ( s1 , QuoteKind ::None ) ,
2020-04-19 22:54:56 -04:00
Self ::List ( v , sep , bracket ) = > {
2020-04-21 18:22:26 -04:00
Self ::List ( v . into_iter ( ) . map ( Value ::unquote ) . collect ( ) , sep , bracket )
2020-04-19 22:54:56 -04:00
}
2020-02-24 16:58:48 -05:00
v = > v ,
2020-02-02 21:11:22 -05:00
}
2020-01-25 09:58:53 -05:00
}
2020-04-21 18:22:26 -04:00
pub const fn span ( self , span : Span ) -> Spanned < Self > {
2020-04-12 19:37:12 -04:00
Spanned { node : self , span }
}
pub fn kind ( & self , span : Span ) -> SassResult < & 'static str > {
2020-02-03 07:56:21 -05:00
match self {
2020-03-23 15:13:19 -04:00
Self ::Color ( .. ) = > Ok ( " color " ) ,
2020-05-22 14:35:41 -04:00
Self ::String ( .. ) | Self ::Important = > Ok ( " string " ) ,
2020-03-23 15:13:19 -04:00
Self ::Dimension ( .. ) = > Ok ( " number " ) ,
Self ::List ( .. ) = > Ok ( " list " ) ,
2020-04-03 21:38:34 -04:00
Self ::Function ( .. ) = > Ok ( " function " ) ,
2020-04-02 13:33:26 -04:00
Self ::ArgList ( .. ) = > Ok ( " arglist " ) ,
2020-03-23 15:13:19 -04:00
Self ::True | Self ::False = > Ok ( " bool " ) ,
Self ::Null = > Ok ( " null " ) ,
2020-03-30 15:43:15 -04:00
Self ::Map ( .. ) = > Ok ( " map " ) ,
2020-04-12 19:37:12 -04:00
Self ::BinaryOp ( .. ) | Self ::Paren ( .. ) | Self ::UnaryOp ( .. ) = > {
self . clone ( ) . eval ( span ) ? . kind ( span )
}
2020-02-03 07:56:21 -05:00
}
}
2020-04-06 21:22:03 -04:00
pub fn is_special_function ( & self ) -> bool {
match self {
2020-05-22 14:35:41 -04:00
Self ::String ( s , QuoteKind ::None ) = > is_special_function ( s ) ,
2020-04-06 21:22:03 -04:00
_ = > false ,
}
}
2020-02-08 15:53:49 -05:00
pub fn bool ( b : bool ) -> Self {
if b {
Value ::True
} else {
Value ::False
}
}
2020-04-26 18:27:08 -04:00
// TODO:
// https://github.com/sass/dart-sass/blob/d4adea7569832f10e3a26d0e420ae51640740cfb/lib/src/ast/sass/expression/list.dart#L39
2020-05-06 11:50:35 -04:00
pub fn inspect ( & self , span : Span ) -> SassResult < Cow < 'static , str > > {
2020-04-12 19:37:12 -04:00
Ok ( match self {
2020-04-06 00:27:09 -04:00
Value ::List ( v , _ , brackets ) if v . is_empty ( ) = > match brackets {
2020-05-25 13:09:20 -04:00
Brackets ::None = > Cow ::const_str ( " () " ) ,
Brackets ::Bracketed = > Cow ::const_str ( " [] " ) ,
2020-04-06 00:27:09 -04:00
} ,
2020-04-20 02:55:55 -04:00
Value ::List ( v , sep , brackets ) if v . len ( ) = = 1 = > match brackets {
Brackets ::None = > match sep {
2020-04-21 18:22:26 -04:00
ListSeparator ::Space = > v [ 0 ] . inspect ( span ) ? ,
2020-05-25 13:09:20 -04:00
ListSeparator ::Comma = > Cow ::owned ( format! ( " ( {} ,) " , v [ 0 ] . inspect ( span ) ? ) ) ,
2020-04-20 02:55:55 -04:00
} ,
Brackets ::Bracketed = > match sep {
2020-05-25 13:09:20 -04:00
ListSeparator ::Space = > Cow ::owned ( format! ( " [ {} ] " , v [ 0 ] . inspect ( span ) ? ) ) ,
ListSeparator ::Comma = > Cow ::owned ( format! ( " [ {} ,] " , v [ 0 ] . inspect ( span ) ? ) ) ,
2020-04-20 02:55:55 -04:00
} ,
} ,
2020-05-25 13:09:20 -04:00
Self ::List ( vals , sep , brackets ) = > Cow ::owned ( match brackets {
2020-04-21 18:22:26 -04:00
Brackets ::None = > vals
. iter ( )
. map ( | x | x . inspect ( span ) )
2020-05-06 11:50:35 -04:00
. collect ::< SassResult < Vec < Cow < 'static , str > > > > ( ) ?
2020-04-21 18:22:26 -04:00
. join ( sep . as_str ( ) ) ,
2020-04-20 03:07:02 -04:00
Brackets ::Bracketed = > format! (
" [{}] " ,
vals . iter ( )
. map ( | x | x . inspect ( span ) )
2020-05-06 11:50:35 -04:00
. collect ::< SassResult < Vec < Cow < 'static , str > > > > ( ) ?
2020-04-20 03:07:02 -04:00
. join ( sep . as_str ( ) ) ,
) ,
2020-05-06 11:50:35 -04:00
} ) ,
2020-05-25 13:09:20 -04:00
Value ::Function ( f ) = > Cow ::owned ( format! ( " get-function( \" {} \" ) " , f . name ( ) ) ) ,
Value ::Null = > Cow ::const_str ( " null " ) ,
Value ::Map ( map ) = > Cow ::owned ( format! (
2020-04-18 20:11:49 -04:00
" ({}) " ,
map . iter ( )
. map ( | ( k , v ) | Ok ( format! (
" {}: {} " ,
k . to_css_string ( span ) ? ,
v . to_css_string ( span ) ?
) ) )
. collect ::< SassResult < Vec < String > > > ( ) ?
. join ( " , " )
2020-05-06 11:50:35 -04:00
) ) ,
2020-04-26 18:27:08 -04:00
Value ::Paren ( v ) = > v . inspect ( span ) ? ,
2020-05-06 11:50:35 -04:00
v = > v . to_css_string ( span ) ? ,
2020-04-12 19:37:12 -04:00
} )
2020-04-06 00:27:09 -04:00
}
2020-05-31 04:51:41 -04:00
pub fn as_list ( self ) -> Vec < Value > {
match self {
Value ::List ( v , .. ) = > v ,
Value ::Map ( m ) = > m . entries ( ) ,
v = > vec! [ v ] ,
}
}
/// Parses `self` as a selector list, in the same manner as the
/// `selector-parse()` function.
///
/// Returns a `SassError` if `self` isn't a type that can be parsed as a
/// selector, or if parsing fails. If `allow_parent` is `true`, this allows
/// parent selectors. Otherwise, they're considered parse errors.
///
/// `name` is the argument name. It's used for error reporting.
pub fn to_selector (
self ,
2020-06-16 19:38:30 -04:00
parser : & mut Parser < '_ > ,
2020-05-31 04:51:41 -04:00
name : & str ,
allows_parent : bool ,
) -> SassResult < Selector > {
2020-06-16 19:38:30 -04:00
let string = match self . clone ( ) . selector_string ( parser . span_before ) ? {
2020-05-31 04:51:41 -04:00
Some ( v ) = > v ,
2020-06-16 19:38:30 -04:00
None = > return Err ( ( format! ( " $ {} : {} is not a valid selector: it must be a string, a list of strings, or a list of lists of strings. " , name , self . inspect ( parser . span_before ) ? ) , parser . span_before ) . into ( ) ) ,
2020-05-31 04:51:41 -04:00
} ;
2020-06-16 19:38:30 -04:00
Parser {
toks : & mut string
. chars ( )
. map ( | c | Token ::new ( parser . span_before , c ) )
. collect ::< Vec < Token > > ( )
. into_iter ( )
. peekmore ( ) ,
map : parser . map ,
path : parser . path ,
scopes : parser . scopes ,
global_scope : parser . global_scope ,
super_selectors : parser . super_selectors ,
span_before : parser . span_before ,
content : parser . content . clone ( ) ,
in_mixin : parser . in_mixin ,
in_function : parser . in_function ,
in_control_flow : parser . in_control_flow ,
at_root : parser . at_root ,
at_root_has_selector : parser . at_root_has_selector ,
}
. parse_selector ( allows_parent , true , String ::new ( ) )
2020-05-31 04:51:41 -04:00
}
fn selector_string ( self , span : Span ) -> SassResult < Option < String > > {
Ok ( Some ( match self . eval ( span ) ? . node {
Self ::String ( text , .. ) = > text ,
Self ::List ( list , sep , .. ) if ! list . is_empty ( ) = > {
let mut result = Vec ::new ( ) ;
match sep {
ListSeparator ::Comma = > {
for complex in list {
if let Value ::String ( text , .. ) = complex {
result . push ( text ) ;
} else if let Value ::List ( _ , ListSeparator ::Space , .. ) = complex {
result . push ( match complex . selector_string ( span ) ? {
Some ( v ) = > v ,
None = > return Ok ( None ) ,
} ) ;
} else {
return Ok ( None ) ;
}
}
}
ListSeparator ::Space = > {
for compound in list {
if let Value ::String ( text , .. ) = compound {
result . push ( text ) ;
} else {
return Ok ( None ) ;
}
}
}
}
result . join ( sep . as_str ( ) )
}
_ = > return Ok ( None ) ,
} ) )
}
2020-01-25 09:58:53 -05:00
}