2020-03-03 19:51:02 -05:00
|
|
|
use std::collections::HashMap;
|
2020-02-02 21:09:29 -05:00
|
|
|
|
2020-02-14 18:39:50 -05:00
|
|
|
use super::{Builtin, GLOBAL_FUNCTIONS};
|
2020-04-06 00:27:09 -04:00
|
|
|
use crate::common::QuoteKind;
|
2020-03-23 14:53:22 -04:00
|
|
|
use crate::scope::global_var_exists;
|
2020-03-19 16:24:31 -04:00
|
|
|
use crate::unit::Unit;
|
2020-04-03 23:47:56 -04:00
|
|
|
use crate::value::{SassFunction, Value};
|
2020-02-02 22:33:04 -05:00
|
|
|
|
2020-03-03 19:51:02 -05:00
|
|
|
pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
2020-03-16 10:35:38 -04:00
|
|
|
f.insert(
|
|
|
|
"if".to_owned(),
|
2020-04-04 18:17:04 -04:00
|
|
|
Builtin::new(|mut args, scope, super_selector| {
|
2020-03-16 10:35:38 -04:00
|
|
|
max_args!(args, 3);
|
2020-04-04 18:17:04 -04:00
|
|
|
if arg!(args, scope, super_selector, 0, "condition").is_true()? {
|
|
|
|
Ok(arg!(args, scope, super_selector, 1, "if-true"))
|
2020-03-16 10:35:38 -04:00
|
|
|
} else {
|
2020-04-04 18:17:04 -04:00
|
|
|
Ok(arg!(args, scope, super_selector, 2, "if-false"))
|
2020-03-16 10:35:38 -04:00
|
|
|
}
|
|
|
|
}),
|
|
|
|
);
|
|
|
|
f.insert(
|
|
|
|
"feature-exists".to_owned(),
|
2020-04-04 18:17:04 -04:00
|
|
|
Builtin::new(|mut args, scope, super_selector| {
|
2020-03-16 10:35:38 -04:00
|
|
|
max_args!(args, 1);
|
2020-04-04 18:17:04 -04:00
|
|
|
match arg!(args, scope, super_selector, 0, "feature") {
|
2020-03-23 15:02:50 -04:00
|
|
|
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),
|
2020-03-23 15:02:50 -04:00
|
|
|
// 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),
|
|
|
|
},
|
|
|
|
v => Err(format!("$feature: {} is not a string.", v).into()),
|
2020-03-16 10:35:38 -04:00
|
|
|
}
|
|
|
|
}),
|
|
|
|
);
|
|
|
|
f.insert(
|
|
|
|
"unit".to_owned(),
|
2020-04-04 18:17:04 -04:00
|
|
|
Builtin::new(|mut args, scope, super_selector| {
|
2020-03-16 10:35:38 -04:00
|
|
|
max_args!(args, 1);
|
2020-04-04 18:17:04 -04:00
|
|
|
let unit = match arg!(args, scope, super_selector, 0, "number") {
|
2020-03-16 10:35:38 -04:00
|
|
|
Value::Dimension(_, u) => u.to_string(),
|
2020-03-17 10:10:42 -04:00
|
|
|
v => return Err(format!("$number: {} is not a number.", v).into()),
|
2020-03-16 10:35:38 -04:00
|
|
|
};
|
|
|
|
Ok(Value::Ident(unit, QuoteKind::Double))
|
|
|
|
}),
|
|
|
|
);
|
|
|
|
f.insert(
|
|
|
|
"type-of".to_owned(),
|
2020-04-04 18:17:04 -04:00
|
|
|
Builtin::new(|mut args, scope, super_selector| {
|
2020-03-16 10:35:38 -04:00
|
|
|
max_args!(args, 1);
|
2020-04-04 18:17:04 -04:00
|
|
|
let value = arg!(args, scope, super_selector, 0, "value");
|
2020-03-16 10:35:38 -04:00
|
|
|
Ok(Value::Ident(value.kind()?.to_owned(), QuoteKind::None))
|
|
|
|
}),
|
|
|
|
);
|
|
|
|
f.insert(
|
|
|
|
"unitless".to_owned(),
|
2020-04-04 18:17:04 -04:00
|
|
|
Builtin::new(|mut args, scope, super_selector| {
|
2020-03-16 10:35:38 -04:00
|
|
|
max_args!(args, 1);
|
2020-04-04 18:17:04 -04:00
|
|
|
match arg!(args, scope, super_selector, 0, "number") {
|
2020-03-16 10:35:38 -04:00
|
|
|
Value::Dimension(_, Unit::None) => Ok(Value::True),
|
|
|
|
Value::Dimension(_, _) => Ok(Value::False),
|
|
|
|
_ => Ok(Value::True),
|
|
|
|
}
|
|
|
|
}),
|
|
|
|
);
|
|
|
|
f.insert(
|
|
|
|
"inspect".to_owned(),
|
2020-04-04 18:17:04 -04:00
|
|
|
Builtin::new(|mut args, scope, super_selector| {
|
2020-03-16 10:35:38 -04:00
|
|
|
max_args!(args, 1);
|
2020-03-23 22:13:11 -04:00
|
|
|
Ok(Value::Ident(
|
2020-04-06 00:27:09 -04:00
|
|
|
arg!(args, scope, super_selector, 0, "value").inspect(),
|
2020-03-23 22:13:11 -04:00
|
|
|
QuoteKind::None,
|
|
|
|
))
|
2020-03-16 10:35:38 -04:00
|
|
|
}),
|
|
|
|
);
|
|
|
|
f.insert(
|
|
|
|
"variable-exists".to_owned(),
|
2020-04-04 18:17:04 -04:00
|
|
|
Builtin::new(|mut args, scope, super_selector| {
|
2020-03-16 10:35:38 -04:00
|
|
|
max_args!(args, 1);
|
2020-04-04 18:17:04 -04:00
|
|
|
match arg!(args, scope, super_selector, 0, "name") {
|
2020-03-23 14:34:03 -04:00
|
|
|
Value::Ident(s, _) => Ok(Value::bool(scope.var_exists(&s))),
|
|
|
|
v => Err(format!("$name: {} is not a string.", v).into()),
|
|
|
|
}
|
2020-03-16 10:35:38 -04:00
|
|
|
}),
|
|
|
|
);
|
2020-03-23 14:53:22 -04:00
|
|
|
f.insert(
|
|
|
|
"global-variable-exists".to_owned(),
|
2020-04-04 18:17:04 -04:00
|
|
|
Builtin::new(|mut args, scope, super_selector| {
|
2020-03-23 14:53:22 -04:00
|
|
|
max_args!(args, 1);
|
2020-04-04 18:17:04 -04:00
|
|
|
match arg!(args, scope, super_selector, 0, "name") {
|
2020-03-23 14:53:22 -04:00
|
|
|
Value::Ident(s, _) => Ok(Value::bool(global_var_exists(&s))),
|
|
|
|
v => Err(format!("$name: {} is not a string.", v).into()),
|
|
|
|
}
|
|
|
|
}),
|
|
|
|
);
|
2020-03-16 10:35:38 -04:00
|
|
|
f.insert(
|
|
|
|
"mixin-exists".to_owned(),
|
2020-04-04 18:17:04 -04:00
|
|
|
Builtin::new(|mut args, scope, super_selector| {
|
2020-03-23 15:26:07 -04:00
|
|
|
max_args!(args, 2);
|
2020-04-04 18:17:04 -04:00
|
|
|
match arg!(args, scope, super_selector, 0, "name") {
|
2020-03-23 15:21:59 -04:00
|
|
|
Value::Ident(s, _) => Ok(Value::bool(scope.mixin_exists(&s))),
|
|
|
|
v => Err(format!("$name: {} is not a string.", v).into()),
|
|
|
|
}
|
2020-03-16 10:35:38 -04:00
|
|
|
}),
|
|
|
|
);
|
|
|
|
f.insert(
|
|
|
|
"function-exists".to_owned(),
|
2020-04-04 18:17:04 -04:00
|
|
|
Builtin::new(|mut args, scope, super_selector| {
|
2020-03-23 15:25:26 -04:00
|
|
|
max_args!(args, 2);
|
2020-04-04 18:17:04 -04:00
|
|
|
match arg!(args, scope, super_selector, 0, "name") {
|
2020-03-23 15:21:59 -04:00
|
|
|
Value::Ident(s, _) => Ok(Value::bool(
|
|
|
|
scope.fn_exists(&s) || GLOBAL_FUNCTIONS.contains_key(&s),
|
|
|
|
)),
|
|
|
|
v => Err(format!("$name: {} is not a string.", v).into()),
|
|
|
|
}
|
2020-03-16 10:35:38 -04:00
|
|
|
}),
|
|
|
|
);
|
2020-04-03 21:38:34 -04:00
|
|
|
f.insert(
|
|
|
|
"get-function".to_owned(),
|
2020-04-04 18:17:04 -04:00
|
|
|
Builtin::new(|mut args, scope, super_selector| {
|
2020-04-04 03:00:38 -04:00
|
|
|
max_args!(args, 3);
|
2020-04-04 18:17:04 -04:00
|
|
|
let name = match arg!(args, scope, super_selector, 0, "name") {
|
2020-04-03 21:38:34 -04:00
|
|
|
Value::Ident(s, _) => s,
|
|
|
|
v => return Err(format!("$name: {} is not a string.", v).into()),
|
|
|
|
};
|
2020-04-04 18:17:04 -04:00
|
|
|
let css = arg!(args, scope, super_selector, 1, "css" = Value::False).is_true()?;
|
|
|
|
let module = match arg!(args, scope, super_selector, 2, "module" = Value::Null) {
|
2020-04-04 12:45:30 -04:00
|
|
|
Value::Ident(s, ..) => Some(s),
|
|
|
|
Value::Null => None,
|
2020-04-04 14:13:13 -04:00
|
|
|
v => return Err(format!("$module: {} is not a string.", v).into()),
|
2020-04-04 12:45:30 -04:00
|
|
|
};
|
2020-04-04 12:38:07 -04:00
|
|
|
|
2020-04-04 12:45:30 -04:00
|
|
|
if module.is_some() && css {
|
2020-04-04 12:38:07 -04:00
|
|
|
return Err("$css and $module may not both be passed at once.".into());
|
|
|
|
}
|
2020-04-03 21:38:34 -04:00
|
|
|
|
2020-04-03 23:47:56 -04:00
|
|
|
let func = match scope.get_fn(&name) {
|
|
|
|
Ok(f) => SassFunction::UserDefined(Box::new(f), name),
|
2020-04-04 12:31:43 -04:00
|
|
|
Err(..) => match GLOBAL_FUNCTIONS.get(&name) {
|
2020-04-03 23:47:56 -04:00
|
|
|
Some(f) => SassFunction::Builtin(f.clone(), name),
|
2020-04-04 12:31:43 -04:00
|
|
|
None => return Err(format!("Function not found: {}", name).into()),
|
2020-04-03 23:47:56 -04:00
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
Ok(Value::Function(func))
|
2020-04-03 21:38:34 -04:00
|
|
|
}),
|
|
|
|
);
|
2020-04-04 12:31:43 -04:00
|
|
|
f.insert(
|
|
|
|
"call".to_owned(),
|
2020-04-04 18:17:04 -04:00
|
|
|
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,
|
|
|
|
v => return Err(format!("$function: {} is not a function reference.", v).into()),
|
|
|
|
};
|
2020-04-04 18:17:04 -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
|
|
|
}
|