grass/src/builtin/meta.rs

242 lines
8.7 KiB
Rust
Raw Normal View History

use std::collections::HashMap;
2020-02-02 21:09:29 -05:00
2020-04-12 19:37:12 -04:00
use codemap::Spanned;
use super::{Builtin, GLOBAL_FUNCTIONS};
2020-04-06 00:27:09 -04:00
use crate::common::QuoteKind;
use crate::scope::global_var_exists;
use crate::unit::Unit;
use crate::value::{SassFunction, Value};
2020-02-02 22:33:04 -05:00
pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
f.insert(
"if".to_owned(),
Builtin::new(|mut args, scope, super_selector| {
max_args!(args, 3);
2020-04-12 19:37:12 -04:00
if arg!(args, scope, super_selector, 0, "condition").is_true(args.span())? {
Ok(arg!(args, scope, super_selector, 1, "if-true"))
} else {
Ok(arg!(args, scope, super_selector, 2, "if-false"))
}
}),
);
f.insert(
"feature-exists".to_owned(),
Builtin::new(|mut args, scope, super_selector| {
max_args!(args, 1);
match arg!(args, scope, super_selector, 0, "feature") {
Value::Ident(s, _) => match s.as_str() {
// A local variable will shadow a global variable unless
// `!global` is used.
2020-03-23 15:06:04 -04:00
"global-variable-shadowing" => Ok(Value::True),
// the @extend rule will affect selectors nested in pseudo-classes
// like :not()
"extend-selector-pseudoclass" => Ok(Value::False),
// Full support for unit arithmetic using units defined in the
// [Values and Units Level 3][] spec.
"units-level-3" => Ok(Value::True),
// The Sass `@error` directive is supported.
"at-error" => Ok(Value::True),
// The "Custom Properties Level 1" spec is supported. This means
// that custom properties are parsed statically, with only
// interpolation treated as SassScript.
"custom-property" => Ok(Value::False),
_ => Ok(Value::False),
},
2020-04-12 19:37:12 -04:00
v => Err((
format!(
"$feature: {} is not a string.",
v.to_css_string(args.span())?
),
args.span(),
)
.into()),
}
}),
);
f.insert(
"unit".to_owned(),
Builtin::new(|mut args, scope, super_selector| {
max_args!(args, 1);
let unit = match arg!(args, scope, super_selector, 0, "number") {
Value::Dimension(_, u) => u.to_string(),
2020-04-12 19:37:12 -04:00
v => {
return Err((
format!(
"$number: {} is not a number.",
v.to_css_string(args.span())?
),
args.span(),
)
.into())
}
};
Ok(Value::Ident(unit, QuoteKind::Double))
}),
);
f.insert(
"type-of".to_owned(),
Builtin::new(|mut args, scope, super_selector| {
max_args!(args, 1);
let value = arg!(args, scope, super_selector, 0, "value");
2020-04-12 19:37:12 -04:00
Ok(Value::Ident(
value.kind(args.span())?.to_owned(),
QuoteKind::None,
))
}),
);
f.insert(
"unitless".to_owned(),
Builtin::new(|mut args, scope, super_selector| {
max_args!(args, 1);
match arg!(args, scope, super_selector, 0, "number") {
Value::Dimension(_, Unit::None) => Ok(Value::True),
Value::Dimension(_, _) => Ok(Value::False),
_ => Ok(Value::True),
}
}),
);
f.insert(
"inspect".to_owned(),
Builtin::new(|mut args, scope, super_selector| {
max_args!(args, 1);
2020-03-23 22:13:11 -04:00
Ok(Value::Ident(
2020-04-12 19:37:12 -04:00
arg!(args, scope, super_selector, 0, "value").inspect(args.span())?,
2020-03-23 22:13:11 -04:00
QuoteKind::None,
))
}),
);
f.insert(
"variable-exists".to_owned(),
Builtin::new(|mut args, scope, super_selector| {
max_args!(args, 1);
match arg!(args, scope, super_selector, 0, "name") {
Value::Ident(s, _) => Ok(Value::bool(scope.var_exists(&s))),
2020-04-12 19:37:12 -04:00
v => Err((
format!("$name: {} is not a string.", v.to_css_string(args.span())?),
args.span(),
)
.into()),
}
}),
);
f.insert(
"global-variable-exists".to_owned(),
Builtin::new(|mut args, scope, super_selector| {
max_args!(args, 1);
match arg!(args, scope, super_selector, 0, "name") {
Value::Ident(s, _) => Ok(Value::bool(global_var_exists(&s))),
2020-04-12 19:37:12 -04:00
v => Err((
format!("$name: {} is not a string.", v.to_css_string(args.span())?),
args.span(),
)
.into()),
}
}),
);
f.insert(
"mixin-exists".to_owned(),
Builtin::new(|mut args, scope, super_selector| {
2020-03-23 15:26:07 -04:00
max_args!(args, 2);
match arg!(args, scope, super_selector, 0, "name") {
Value::Ident(s, _) => Ok(Value::bool(scope.mixin_exists(&s))),
2020-04-12 19:37:12 -04:00
v => Err((
format!("$name: {} is not a string.", v.to_css_string(args.span())?),
args.span(),
)
.into()),
}
}),
);
f.insert(
"function-exists".to_owned(),
Builtin::new(|mut args, scope, super_selector| {
2020-03-23 15:25:26 -04:00
max_args!(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),
)),
2020-04-12 19:37:12 -04:00
v => Err((
format!("$name: {} is not a string.", v.to_css_string(args.span())?),
args.span(),
)
.into()),
}
}),
);
f.insert(
"get-function".to_owned(),
Builtin::new(|mut args, scope, super_selector| {
2020-04-04 03:00:38 -04:00
max_args!(args, 3);
let name = match arg!(args, scope, super_selector, 0, "name") {
Value::Ident(s, _) => s,
2020-04-12 19:37:12 -04:00
v => {
return Err((
format!("$name: {} is not a string.", v.to_css_string(args.span())?),
args.span(),
)
.into())
}
};
2020-04-12 19:37:12 -04:00
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,
2020-04-12 19:37:12 -04:00
v => {
return Err((
format!(
"$module: {} is not a string.",
v.to_css_string(args.span())?
),
args.span(),
)
.into())
}
};
if module.is_some() && css {
2020-04-12 19:37:12 -04:00
return Err((
"$css and $module may not both be passed at once.",
args.span(),
)
.into());
}
2020-04-12 19:37:12 -04:00
let func = match scope.get_fn(Spanned {
node: name.clone(),
span: args.span(),
}) {
Ok(f) => SassFunction::UserDefined(Box::new(f), name),
2020-04-04 12:31:43 -04:00
Err(..) => match GLOBAL_FUNCTIONS.get(&name) {
Some(f) => SassFunction::Builtin(f.clone(), name),
2020-04-12 19:37:12 -04:00
None => {
return Err((format!("Function not found: {}", name), args.span()).into())
}
},
};
Ok(Value::Function(func))
}),
);
2020-04-04 12:31:43 -04:00
f.insert(
"call".to_owned(),
Builtin::new(|mut args, scope, super_selector| {
let func = match arg!(args, scope, super_selector, 0, "function") {
2020-04-04 12:31:43 -04:00
Value::Function(f) => f,
2020-04-12 19:37:12 -04:00
v => {
return Err((
format!(
"$function: {} is not a function reference.",
v.to_css_string(args.span())?
),
args.span(),
)
.into())
}
2020-04-04 12:31:43 -04:00
};
func.call(args.decrement(), scope, super_selector)
2020-04-04 12:31:43 -04:00
}),
);
2020-02-07 00:10:43 -05:00
}