simplify declaration of meta fns

This commit is contained in:
ConnorSkees 2020-04-30 18:31:55 -04:00
parent 7270890e45
commit 26aabb42ad

View File

@ -3,239 +3,255 @@ use super::GlobalFunctionMap;
use codemap::Spanned; use codemap::Spanned;
use super::{Builtin, GLOBAL_FUNCTIONS}; use super::{Builtin, GLOBAL_FUNCTIONS};
use crate::args::CallArgs;
use crate::common::QuoteKind; use crate::common::QuoteKind;
use crate::error::SassResult;
use crate::scope::global_var_exists; use crate::scope::global_var_exists;
use crate::scope::Scope;
use crate::selector::Selector;
use crate::unit::Unit; use crate::unit::Unit;
use crate::value::{SassFunction, Value}; use crate::value::{SassFunction, Value};
pub(crate) fn register(f: &mut GlobalFunctionMap) { pub(crate) fn register(f: &mut GlobalFunctionMap) {
f.insert( fn if_(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassResult<Value> {
"if", args.max_args(3)?;
Builtin::new(|mut args, scope, super_selector| { if arg!(args, scope, super_selector, 0, "condition").is_true(args.span())? {
args.max_args(3)?; Ok(arg!(args, scope, super_selector, 1, "if-true"))
if arg!(args, scope, super_selector, 0, "condition").is_true(args.span())? { } else {
Ok(arg!(args, scope, super_selector, 1, "if-true")) Ok(arg!(args, scope, super_selector, 2, "if-false"))
} else { }
Ok(arg!(args, scope, super_selector, 2, "if-false")) }
}
}), fn feature_exists(
); mut args: CallArgs,
f.insert( scope: &Scope,
"feature-exists", super_selector: &Selector,
Builtin::new(|mut args, scope, super_selector| { ) -> SassResult<Value> {
args.max_args(1)?; args.max_args(1)?;
match arg!(args, scope, super_selector, 0, "feature") { match arg!(args, scope, super_selector, 0, "feature") {
Value::Ident(s, _) => Ok(match s.as_str() { Value::Ident(s, _) => Ok(match s.as_str() {
// A local variable will shadow a global variable unless // A local variable will shadow a global variable unless
// `!global` is used. // `!global` is used.
"global-variable-shadowing" => Value::True, "global-variable-shadowing" => Value::True,
// the @extend rule will affect selectors nested in pseudo-classes // the @extend rule will affect selectors nested in pseudo-classes
// like :not() // like :not()
"extend-selector-pseudoclass" => Value::False, "extend-selector-pseudoclass" => Value::False,
// Full support for unit arithmetic using units defined in the // Full support for unit arithmetic using units defined in the
// [Values and Units Level 3][] spec. // [Values and Units Level 3][] spec.
"units-level-3" => Value::True, "units-level-3" => Value::True,
// The Sass `@error` directive is supported. // The Sass `@error` directive is supported.
"at-error" => Value::True, "at-error" => Value::True,
// The "Custom Properties Level 1" spec is supported. This means // The "Custom Properties Level 1" spec is supported. This means
// that custom properties are parsed statically, with only // that custom properties are parsed statically, with only
// interpolation treated as SassScript. // interpolation treated as SassScript.
"custom-property" => Value::False, "custom-property" => Value::False,
_ => Value::False, _ => Value::False,
}), }),
v => Err(( v => Err((
format!(
"$feature: {} is not a string.",
v.to_css_string(args.span())?
),
args.span(),
)
.into()),
}
}
fn unit(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassResult<Value> {
args.max_args(1)?;
let unit = match arg!(args, scope, super_selector, 0, "number") {
Value::Dimension(_, u) => u.to_string(),
v => {
return Err((
format!( format!(
"$feature: {} is not a string.", "$number: {} is not a number.",
v.to_css_string(args.span())? v.to_css_string(args.span())?
), ),
args.span(), args.span(),
) )
.into()), .into())
} }
}), };
); Ok(Value::Ident(unit, QuoteKind::Quoted))
f.insert( }
"unit",
Builtin::new(|mut args, scope, super_selector| { fn type_of(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassResult<Value> {
args.max_args(1)?; args.max_args(1)?;
let unit = match arg!(args, scope, super_selector, 0, "number") { let value = arg!(args, scope, super_selector, 0, "value");
Value::Dimension(_, u) => u.to_string(), Ok(Value::Ident(
v => { value.kind(args.span())?.to_owned(),
return Err(( QuoteKind::None,
format!( ))
"$number: {} is not a number.", }
v.to_css_string(args.span())? fn unitless(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassResult<Value> {
), args.max_args(1)?;
args.span(), match arg!(args, scope, super_selector, 0, "number") {
) Value::Dimension(_, Unit::None) => Ok(Value::True),
.into()) Value::Dimension(_, _) => Ok(Value::False),
} _ => Ok(Value::True),
}; }
Ok(Value::Ident(unit, QuoteKind::Quoted)) }
}),
); fn inspect(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassResult<Value> {
f.insert( args.max_args(1)?;
"type-of", Ok(Value::Ident(
Builtin::new(|mut args, scope, super_selector| { arg!(args, scope, super_selector, 0, "value").inspect(args.span())?,
args.max_args(1)?; QuoteKind::None,
let value = arg!(args, scope, super_selector, 0, "value"); ))
Ok(Value::Ident( }
value.kind(args.span())?.to_owned(),
QuoteKind::None, fn variable_exists(
)) mut args: CallArgs,
}), scope: &Scope,
); super_selector: &Selector,
f.insert( ) -> SassResult<Value> {
"unitless", args.max_args(1)?;
Builtin::new(|mut args, scope, super_selector| { match arg!(args, scope, super_selector, 0, "name") {
args.max_args(1)?; Value::Ident(s, _) => Ok(Value::bool(scope.var_exists(&s))),
match arg!(args, scope, super_selector, 0, "number") { v => Err((
Value::Dimension(_, Unit::None) => Ok(Value::True), format!("$name: {} is not a string.", v.to_css_string(args.span())?),
Value::Dimension(_, _) => Ok(Value::False), args.span(),
_ => Ok(Value::True), )
} .into()),
}), }
); }
f.insert(
"inspect", fn global_variable_exists(
Builtin::new(|mut args, scope, super_selector| { mut args: CallArgs,
args.max_args(1)?; scope: &Scope,
Ok(Value::Ident( super_selector: &Selector,
arg!(args, scope, super_selector, 0, "value").inspect(args.span())?, ) -> SassResult<Value> {
QuoteKind::None, args.max_args(1)?;
)) match arg!(args, scope, super_selector, 0, "name") {
}), Value::Ident(s, _) => Ok(Value::bool(global_var_exists(&s))),
); v => Err((
f.insert( format!("$name: {} is not a string.", v.to_css_string(args.span())?),
"variable-exists", args.span(),
Builtin::new(|mut args, scope, super_selector| { )
args.max_args(1)?; .into()),
match arg!(args, scope, super_selector, 0, "name") { }
Value::Ident(s, _) => Ok(Value::bool(scope.var_exists(&s))), }
v => Err((
fn mixin_exists(
mut args: CallArgs,
scope: &Scope,
super_selector: &Selector,
) -> SassResult<Value> {
args.max_args(2)?;
match arg!(args, scope, super_selector, 0, "name") {
Value::Ident(s, _) => Ok(Value::bool(scope.mixin_exists(&s))),
v => Err((
format!("$name: {} is not a string.", v.to_css_string(args.span())?),
args.span(),
)
.into()),
}
}
fn function_exists(
mut args: CallArgs,
scope: &Scope,
super_selector: &Selector,
) -> SassResult<Value> {
args.max_args(2)?;
match arg!(args, scope, super_selector, 0, "name") {
Value::Ident(s, _) => Ok(Value::bool(
scope.fn_exists(&s) || GLOBAL_FUNCTIONS.contains_key(s.as_str()),
)),
v => Err((
format!("$name: {} is not a string.", v.to_css_string(args.span())?),
args.span(),
)
.into()),
}
}
fn get_function(
mut args: CallArgs,
scope: &Scope,
super_selector: &Selector,
) -> SassResult<Value> {
args.max_args(3)?;
let name = match arg!(args, scope, super_selector, 0, "name") {
Value::Ident(s, _) => s,
v => {
return Err((
format!("$name: {} is not a string.", v.to_css_string(args.span())?), format!("$name: {} is not a string.", v.to_css_string(args.span())?),
args.span(), args.span(),
) )
.into()), .into())
} }
}), };
); let css =
arg!(args, scope, super_selector, 1, "css" = Value::False).is_true(args.span())?;
let module = match arg!(args, scope, super_selector, 2, "module" = Value::Null) {
Value::Ident(s, ..) => Some(s),
Value::Null => None,
v => {
return Err((
format!(
"$module: {} is not a string.",
v.to_css_string(args.span())?
),
args.span(),
)
.into())
}
};
if module.is_some() && css {
return Err((
"$css and $module may not both be passed at once.",
args.span(),
)
.into());
}
let func = match scope.get_fn(Spanned {
node: name.clone(),
span: args.span(),
}) {
Ok(f) => SassFunction::UserDefined(Box::new(f), name),
Err(..) => match GLOBAL_FUNCTIONS.get(name.as_str()) {
Some(f) => SassFunction::Builtin(f.clone(), name),
None => return Err((format!("Function not found: {}", name), args.span()).into()),
},
};
Ok(Value::Function(func))
}
fn call(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassResult<Value> {
let func = match arg!(args, scope, super_selector, 0, "function") {
Value::Function(f) => f,
v => {
return Err((
format!(
"$function: {} is not a function reference.",
v.to_css_string(args.span())?
),
args.span(),
)
.into())
}
};
func.call(args.decrement(), scope, super_selector)
}
f.insert("if", Builtin::new(if_));
f.insert("feature-exists", Builtin::new(feature_exists));
f.insert("unit", Builtin::new(unit));
f.insert("type-of", Builtin::new(type_of));
f.insert("unitless", Builtin::new(unitless));
f.insert("inspect", Builtin::new(inspect));
f.insert("variable-exists", Builtin::new(variable_exists));
f.insert( f.insert(
"global-variable-exists", "global-variable-exists",
Builtin::new(|mut args, scope, super_selector| { Builtin::new(global_variable_exists),
args.max_args(1)?;
match arg!(args, scope, super_selector, 0, "name") {
Value::Ident(s, _) => Ok(Value::bool(global_var_exists(&s))),
v => Err((
format!("$name: {} is not a string.", v.to_css_string(args.span())?),
args.span(),
)
.into()),
}
}),
);
f.insert(
"mixin-exists",
Builtin::new(|mut args, scope, super_selector| {
args.max_args(2)?;
match arg!(args, scope, super_selector, 0, "name") {
Value::Ident(s, _) => Ok(Value::bool(scope.mixin_exists(&s))),
v => Err((
format!("$name: {} is not a string.", v.to_css_string(args.span())?),
args.span(),
)
.into()),
}
}),
);
f.insert(
"function-exists",
Builtin::new(|mut args, scope, super_selector| {
args.max_args(2)?;
match arg!(args, scope, super_selector, 0, "name") {
Value::Ident(s, _) => Ok(Value::bool(
scope.fn_exists(&s) || GLOBAL_FUNCTIONS.contains_key(s.as_str()),
)),
v => Err((
format!("$name: {} is not a string.", v.to_css_string(args.span())?),
args.span(),
)
.into()),
}
}),
);
f.insert(
"get-function",
Builtin::new(|mut args, scope, super_selector| {
args.max_args(3)?;
let name = match arg!(args, scope, super_selector, 0, "name") {
Value::Ident(s, _) => s,
v => {
return Err((
format!("$name: {} is not a string.", v.to_css_string(args.span())?),
args.span(),
)
.into())
}
};
let css =
arg!(args, scope, super_selector, 1, "css" = Value::False).is_true(args.span())?;
let module = match arg!(args, scope, super_selector, 2, "module" = Value::Null) {
Value::Ident(s, ..) => Some(s),
Value::Null => None,
v => {
return Err((
format!(
"$module: {} is not a string.",
v.to_css_string(args.span())?
),
args.span(),
)
.into())
}
};
if module.is_some() && css {
return Err((
"$css and $module may not both be passed at once.",
args.span(),
)
.into());
}
let func = match scope.get_fn(Spanned {
node: name.clone(),
span: args.span(),
}) {
Ok(f) => SassFunction::UserDefined(Box::new(f), name),
Err(..) => match GLOBAL_FUNCTIONS.get(name.as_str()) {
Some(f) => SassFunction::Builtin(f.clone(), name),
None => {
return Err((format!("Function not found: {}", name), args.span()).into())
}
},
};
Ok(Value::Function(func))
}),
);
f.insert(
"call",
Builtin::new(|mut args, scope, super_selector| {
let func = match arg!(args, scope, super_selector, 0, "function") {
Value::Function(f) => f,
v => {
return Err((
format!(
"$function: {} is not a function reference.",
v.to_css_string(args.span())?
),
args.span(),
)
.into())
}
};
func.call(args.decrement(), scope, super_selector)
}),
); );
f.insert("mixin-exists", Builtin::new(mixin_exists));
f.insert("function-exists", Builtin::new(function_exists));
f.insert("get-function", Builtin::new(get_function));
f.insert("call", Builtin::new(call));
} }