Add (variable|function|mixin)-exists builtin functions

This commit is contained in:
ConnorSkees 2020-02-08 17:41:54 -05:00
parent cbec684052
commit 4585558266
5 changed files with 70 additions and 12 deletions

View File

@ -10,7 +10,7 @@ use crate::units::Unit;
use crate::value::Value;
pub(crate) fn register(f: &mut BTreeMap<String, Builtin>) {
decl!(f "rgb", |args| {
decl!(f "rgb", |args, _| {
let channels = args.get("channels").unwrap_or(&Value::Null);
if channels.is_null() {
let red: u16 = arg!(args, 0, "red").clone().try_into().unwrap();
@ -21,19 +21,19 @@ pub(crate) fn register(f: &mut BTreeMap<String, Builtin>) {
todo!("channels variable in `rgb`")
}
});
decl!(f "red", |args| {
decl!(f "red", |args, _| {
match arg!(args, 0, "red") {
Value::Color(c) => Some(Value::Dimension(BigRational::from_integer(BigInt::from(c.red())), Unit::None)),
_ => todo!("non-color given to builtin function `red()`")
}
});
decl!(f "green", |args| {
decl!(f "green", |args, _| {
match arg!(args, 0, "green") {
Value::Color(c) => Some(Value::Dimension(BigRational::from_integer(BigInt::from(c.green())), Unit::None)),
_ => todo!("non-color given to builtin function `green()`")
}
});
decl!(f "blue", |args| {
decl!(f "blue", |args, _| {
match arg!(args, 0, "blue") {
Value::Color(c) => Some(Value::Dimension(BigRational::from_integer(BigInt::from(c.blue())), Unit::None)),
_ => todo!("non-color given to builtin function `blue()`")

View File

@ -6,7 +6,7 @@ use crate::units::Unit;
use crate::value::Value;
pub(crate) fn register(f: &mut BTreeMap<String, Builtin>) {
decl!(f "if", |args| {
decl!(f "if", |args, _| {
let cond: &Value = arg!(args, 0, "condition");
let if_true = arg!(args, 1, "if-true").clone();
let if_false = arg!(args, 2, "if-false").clone();
@ -16,7 +16,7 @@ pub(crate) fn register(f: &mut BTreeMap<String, Builtin>) {
Some(if_false)
}
});
decl!(f "feature-exists", |args| {
decl!(f "feature-exists", |args, _| {
let feature: &Value = arg!(args, 0, "feature");
match feature.clone().unquote().to_string().as_str() {
// A local variable will shadow a global variable unless
@ -37,7 +37,7 @@ pub(crate) fn register(f: &mut BTreeMap<String, Builtin>) {
_ => Some(Value::False),
}
});
decl!(f "unit", |args| {
decl!(f "unit", |args, _| {
let number = arg!(args, 0, "number");
let unit = match number {
Value::Dimension(_, u) => u.to_string(),
@ -45,11 +45,11 @@ pub(crate) fn register(f: &mut BTreeMap<String, Builtin>) {
};
Some(Value::Ident(unit, QuoteKind::Double))
});
decl!(f "type-of", |args| {
decl!(f "type-of", |args, _| {
let value = arg!(args, 0, "value");
Some(Value::Ident(value.kind().to_owned(), QuoteKind::None))
});
decl!(f "unitless", |args| {
decl!(f "unitless", |args, _| {
let number = arg!(args, 0, "number");
match number {
Value::Dimension(_, Unit::None) => Some(Value::True),
@ -57,8 +57,20 @@ pub(crate) fn register(f: &mut BTreeMap<String, Builtin>) {
_ => Some(Value::True)
}
});
decl!(f "inspect", |args| {
decl!(f "inspect", |args, _| {
let value = arg!(args, 0, "value");
Some(Value::Ident(value.to_string(), QuoteKind::None))
});
decl!(f "variable-exists", |args, scope| {
let value = arg!(args, 0, "name");
Some(Value::bool(scope.var_exists(&value.to_string())))
});
decl!(f "mixin-exists", |args, scope| {
let value = arg!(args, 0, "name");
Some(Value::bool(scope.mixin_exists(&value.to_string())))
});
decl!(f "function-exists", |args, scope| {
let value = arg!(args, 0, "name");
Some(Value::bool(scope.fn_exists(&value.to_string())))
});
}

View File

@ -2,6 +2,7 @@ use lazy_static::lazy_static;
use std::collections::BTreeMap;
use crate::args::CallArgs;
use crate::common::Scope;
use crate::value::Value;
#[macro_use]
@ -15,7 +16,7 @@ mod meta;
mod selector;
mod string;
pub(crate) type Builtin = Box<dyn Fn(&CallArgs) -> Option<Value> + Send + Sync>;
pub(crate) type Builtin = Box<dyn Fn(&CallArgs, &Scope) -> Option<Value> + Send + Sync>;
lazy_static! {
pub(crate) static ref GLOBAL_FUNCTIONS: BTreeMap<String, Builtin> = {

View File

@ -200,7 +200,7 @@ impl Value {
let func = match scope.get_fn(&s) {
Ok(f) => f,
Err(_) => match GLOBAL_FUNCTIONS.get(&s) {
Some(f) => return f(&args),
Some(f) => return f(&args, scope),
None => todo!("called undefined function"),
},
};

View File

@ -229,6 +229,51 @@ test!(
"a {\n color: inspect(null)\n}\n",
"a {\n color: null;\n}\n"
);
test!(
variable_does_exist,
"$a: red; a {\n color: variable-exists(a)\n}\n",
"a {\n color: true;\n}\n"
);
test!(
variable_does_not_exist,
"a {\n color: variable-exists(a)\n}\n",
"a {\n color: false;\n}\n"
);
test!(
variable_exists_named,
"$a: red; a {\n color: variable-exists($name: a)\n}\n",
"a {\n color: true;\n}\n"
);
test!(
mixin_does_exist,
"@mixin a{} a {\n color: mixin-exists(a)\n}\n",
"a {\n color: true;\n}\n"
);
test!(
mixin_does_not_exist,
"a {\n color: mixin-exists(a)\n}\n",
"a {\n color: false;\n}\n"
);
test!(
mixin_exists_named,
"@mixin a{} a {\n color: mixin-exists($name: a)\n}\n",
"a {\n color: true;\n}\n"
);
test!(
function_does_exist,
"@function a(){} a {\n color: function-exists(a)\n}\n",
"a {\n color: true;\n}\n"
);
test!(
function_does_not_exist,
"a {\n color: function-exists(a)\n}\n",
"a {\n color: false;\n}\n"
);
test!(
function_exists_named,
"@function a(){} a {\n color: function-exists($name: a)\n}\n",
"a {\n color: true;\n}\n"
);
// test!(
// inspect_empty_list,
// "a {\n color: inspect(())\n}\n",