simplify declaration of meta fns
This commit is contained in:
parent
7270890e45
commit
26aabb42ad
@ -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));
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user