From 81c85a6f8647f2cc30f8634bf4163674f5a698b1 Mon Sep 17 00:00:00 2001 From: ConnorSkees <39542938+ConnorSkees@users.noreply.github.com> Date: Fri, 3 Apr 2020 23:47:56 -0400 Subject: [PATCH] handle builtin functions in get-function --- src/builtin/color/hsl.rs | 26 +++++++++++----------- src/builtin/color/opacity.rs | 12 +++++------ src/builtin/color/other.rs | 8 +++---- src/builtin/color/rgb.rs | 12 +++++------ src/builtin/list.rs | 18 ++++++++-------- src/builtin/map.rs | 12 +++++------ src/builtin/math.rs | 12 +++++------ src/builtin/meta.rs | 37 +++++++++++++++++++------------ src/builtin/mod.rs | 19 +++++++++++++++- src/builtin/string.rs | 16 +++++++------- src/value/function.rs | 42 ++++++++++++++++++++++++++++++++++++ src/value/mod.rs | 5 +++-- src/value/parse.rs | 2 +- tests/get-function.rs | 20 +++++++++++++++++ 14 files changed, 165 insertions(+), 76 deletions(-) create mode 100644 src/value/function.rs diff --git a/src/builtin/color/hsl.rs b/src/builtin/color/hsl.rs index b5382c2..d7a4df7 100644 --- a/src/builtin/color/hsl.rs +++ b/src/builtin/color/hsl.rs @@ -11,7 +11,7 @@ use crate::value::{Number, Value}; pub(crate) fn register(f: &mut HashMap) { f.insert( "hsl".to_owned(), - Box::new(|mut args, _| { + Builtin::new(|mut args, _| { if args.is_empty() { return Err("Missing argument $channels.".into()); } @@ -89,7 +89,7 @@ pub(crate) fn register(f: &mut HashMap) { ); f.insert( "hsla".to_owned(), - Box::new(|mut args, _| { + Builtin::new(|mut args, _| { if args.is_empty() { return Err("Missing argument $channels.".into()); } @@ -167,7 +167,7 @@ pub(crate) fn register(f: &mut HashMap) { ); f.insert( "hue".to_owned(), - Box::new(|mut args, _| { + Builtin::new(|mut args, _| { max_args!(args, 1); match arg!(args, 0, "color") { Value::Color(c) => Ok(Value::Dimension(c.hue(), Unit::Deg)), @@ -177,7 +177,7 @@ pub(crate) fn register(f: &mut HashMap) { ); f.insert( "saturation".to_owned(), - Box::new(|mut args, _| { + Builtin::new(|mut args, _| { max_args!(args, 1); match arg!(args, 0, "color") { Value::Color(c) => Ok(Value::Dimension(c.saturation(), Unit::Percent)), @@ -187,7 +187,7 @@ pub(crate) fn register(f: &mut HashMap) { ); f.insert( "lightness".to_owned(), - Box::new(|mut args, _| { + Builtin::new(|mut args, _| { max_args!(args, 1); match arg!(args, 0, "color") { Value::Color(c) => Ok(Value::Dimension(c.lightness(), Unit::Percent)), @@ -197,7 +197,7 @@ pub(crate) fn register(f: &mut HashMap) { ); f.insert( "adjust-hue".to_owned(), - Box::new(|mut args, _| { + Builtin::new(|mut args, _| { max_args!(args, 2); let color = match arg!(args, 0, "color") { Value::Color(c) => c, @@ -212,7 +212,7 @@ pub(crate) fn register(f: &mut HashMap) { ); f.insert( "lighten".to_owned(), - Box::new(|mut args, _| { + Builtin::new(|mut args, _| { max_args!(args, 2); let color = match arg!(args, 0, "color") { Value::Color(c) => c, @@ -227,7 +227,7 @@ pub(crate) fn register(f: &mut HashMap) { ); f.insert( "darken".to_owned(), - Box::new(|mut args, _| { + Builtin::new(|mut args, _| { max_args!(args, 2); let color = match arg!(args, 0, "color") { Value::Color(c) => c, @@ -242,7 +242,7 @@ pub(crate) fn register(f: &mut HashMap) { ); f.insert( "saturate".to_owned(), - Box::new(|mut args, _| { + Builtin::new(|mut args, _| { max_args!(args, 2); if args.len() == 1 { return Ok(Value::Ident( @@ -270,7 +270,7 @@ pub(crate) fn register(f: &mut HashMap) { ); f.insert( "desaturate".to_owned(), - Box::new(|mut args, _| { + Builtin::new(|mut args, _| { max_args!(args, 2); let color = match arg!(args, 0, "color") { Value::Color(c) => c, @@ -285,7 +285,7 @@ pub(crate) fn register(f: &mut HashMap) { ); f.insert( "grayscale".to_owned(), - Box::new(|mut args, _| { + Builtin::new(|mut args, _| { max_args!(args, 1); let color = match arg!(args, 0, "color") { Value::Color(c) => c, @@ -302,7 +302,7 @@ pub(crate) fn register(f: &mut HashMap) { ); f.insert( "complement".to_owned(), - Box::new(|mut args, _| { + Builtin::new(|mut args, _| { max_args!(args, 1); let color = match arg!(args, 0, "color") { Value::Color(c) => c, @@ -313,7 +313,7 @@ pub(crate) fn register(f: &mut HashMap) { ); f.insert( "invert".to_owned(), - Box::new(|mut args, _| { + Builtin::new(|mut args, _| { max_args!(args, 2); let weight = match arg!( args, diff --git a/src/builtin/color/opacity.rs b/src/builtin/color/opacity.rs index 87fc49b..a74df3b 100644 --- a/src/builtin/color/opacity.rs +++ b/src/builtin/color/opacity.rs @@ -9,7 +9,7 @@ use crate::value::Value; pub(crate) fn register(f: &mut HashMap) { f.insert( "alpha".to_owned(), - Box::new(|mut args, _| { + Builtin::new(|mut args, _| { max_args!(args, 1); match arg!(args, 0, "color") { Value::Color(c) => Ok(Value::Dimension(c.alpha(), Unit::None)), @@ -19,7 +19,7 @@ pub(crate) fn register(f: &mut HashMap) { ); f.insert( "opacity".to_owned(), - Box::new(|mut args, _| { + Builtin::new(|mut args, _| { max_args!(args, 1); match arg!(args, 0, "color") { Value::Color(c) => Ok(Value::Dimension(c.alpha(), Unit::None)), @@ -33,7 +33,7 @@ pub(crate) fn register(f: &mut HashMap) { ); f.insert( "opacify".to_owned(), - Box::new(|mut args, _| { + Builtin::new(|mut args, _| { max_args!(args, 2); let color = match arg!(args, 0, "color") { Value::Color(c) => c, @@ -48,7 +48,7 @@ pub(crate) fn register(f: &mut HashMap) { ); f.insert( "fade-in".to_owned(), - Box::new(|mut args, _| { + Builtin::new(|mut args, _| { max_args!(args, 2); let color = match arg!(args, 0, "color") { Value::Color(c) => c, @@ -63,7 +63,7 @@ pub(crate) fn register(f: &mut HashMap) { ); f.insert( "transparentize".to_owned(), - Box::new(|mut args, _| { + Builtin::new(|mut args, _| { max_args!(args, 2); let color = match arg!(args, 0, "color") { Value::Color(c) => c, @@ -78,7 +78,7 @@ pub(crate) fn register(f: &mut HashMap) { ); f.insert( "fade-out".to_owned(), - Box::new(|mut args, _| { + Builtin::new(|mut args, _| { max_args!(args, 2); let color = match arg!(args, 0, "color") { Value::Color(c) => c, diff --git a/src/builtin/color/other.rs b/src/builtin/color/other.rs index 45f376d..91e8a90 100644 --- a/src/builtin/color/other.rs +++ b/src/builtin/color/other.rs @@ -31,7 +31,7 @@ macro_rules! opt_hsl { } pub(crate) fn register(f: &mut HashMap) { - f.insert("change-color".to_owned(), Box::new(|mut args, _| { + f.insert("change-color".to_owned(), Builtin::new(|mut args, _| { if args.get_positional(1).is_some() { return Err("Only one positional argument is allowed. All other arguments must be passed by name.".into()); } @@ -73,7 +73,7 @@ pub(crate) fn register(f: &mut HashMap) { })); f.insert( "adjust-color".to_owned(), - Box::new(|mut args, _| { + Builtin::new(|mut args, _| { let color = match arg!(args, 0, "color") { Value::Color(c) => c, v => return Err(format!("$color: {} is not a color.", v).into()), @@ -123,7 +123,7 @@ pub(crate) fn register(f: &mut HashMap) { ); f.insert( "scale-color".to_owned(), - Box::new(|mut args, _| { + Builtin::new(|mut args, _| { let color = match arg!(args, 0, "color") { Value::Color(c) => c, v => return Err(format!("$color: {} is not a color.", v).into()), @@ -209,7 +209,7 @@ pub(crate) fn register(f: &mut HashMap) { ); f.insert( "ie-hex-str".to_owned(), - Box::new(|mut args, _| { + Builtin::new(|mut args, _| { max_args!(args, 1); let color = match arg!(args, 0, "color") { Value::Color(c) => c, diff --git a/src/builtin/color/rgb.rs b/src/builtin/color/rgb.rs index d89dbdd..fd55b94 100644 --- a/src/builtin/color/rgb.rs +++ b/src/builtin/color/rgb.rs @@ -10,7 +10,7 @@ use crate::value::{Number, Value}; pub(crate) fn register(f: &mut HashMap) { f.insert( "rgb".to_owned(), - Box::new(|mut args, _| { + Builtin::new(|mut args, _| { if args.is_empty() { return Err("Missing argument $channels.".into()); } @@ -132,7 +132,7 @@ pub(crate) fn register(f: &mut HashMap) { ); f.insert( "rgba".to_owned(), - Box::new(|mut args, _| { + Builtin::new(|mut args, _| { if args.is_empty() { return Err("Missing argument $channels.".into()); } @@ -254,7 +254,7 @@ pub(crate) fn register(f: &mut HashMap) { ); f.insert( "red".to_owned(), - Box::new(|mut args, _| { + Builtin::new(|mut args, _| { max_args!(args, 1); match arg!(args, 0, "color") { Value::Color(c) => Ok(Value::Dimension(c.red(), Unit::None)), @@ -264,7 +264,7 @@ pub(crate) fn register(f: &mut HashMap) { ); f.insert( "green".to_owned(), - Box::new(|mut args, _| { + Builtin::new(|mut args, _| { max_args!(args, 1); match arg!(args, 0, "color") { Value::Color(c) => Ok(Value::Dimension(c.green(), Unit::None)), @@ -274,7 +274,7 @@ pub(crate) fn register(f: &mut HashMap) { ); f.insert( "blue".to_owned(), - Box::new(|mut args, _| { + Builtin::new(|mut args, _| { max_args!(args, 1); match arg!(args, 0, "color") { Value::Color(c) => Ok(Value::Dimension(c.blue(), Unit::None)), @@ -284,7 +284,7 @@ pub(crate) fn register(f: &mut HashMap) { ); f.insert( "mix".to_owned(), - Box::new(|mut args, _| { + Builtin::new(|mut args, _| { max_args!(args, 3); let color1 = match arg!(args, 0, "color1") { Value::Color(c) => c, diff --git a/src/builtin/list.rs b/src/builtin/list.rs index 68eb380..c273ad7 100644 --- a/src/builtin/list.rs +++ b/src/builtin/list.rs @@ -10,7 +10,7 @@ use crate::value::{Number, Value}; pub(crate) fn register(f: &mut HashMap) { f.insert( "length".to_owned(), - Box::new(|mut args, _| { + Builtin::new(|mut args, _| { max_args!(args, 1); let len = match arg!(args, 0, "list") { Value::List(v, ..) => Number::from(v.len()), @@ -22,7 +22,7 @@ pub(crate) fn register(f: &mut HashMap) { ); f.insert( "nth".to_owned(), - Box::new(|mut args, _| { + Builtin::new(|mut args, _| { max_args!(args, 2); let list = match arg!(args, 0, "list") { Value::List(v, ..) => v, @@ -60,7 +60,7 @@ pub(crate) fn register(f: &mut HashMap) { ); f.insert( "list-separator".to_owned(), - Box::new(|mut args, _| { + Builtin::new(|mut args, _| { max_args!(args, 1); Ok(Value::Ident( match arg!(args, 0, "list") { @@ -74,7 +74,7 @@ pub(crate) fn register(f: &mut HashMap) { ); f.insert( "set-nth".to_owned(), - Box::new(|mut args, _| { + Builtin::new(|mut args, _| { max_args!(args, 3); let (mut list, sep, brackets) = match arg!(args, 0, "list") { Value::List(v, sep, b) => (v, sep, b), @@ -115,7 +115,7 @@ pub(crate) fn register(f: &mut HashMap) { ); f.insert( "append".to_owned(), - Box::new(|mut args, _| { + Builtin::new(|mut args, _| { max_args!(args, 3); let (mut list, sep, brackets) = match arg!(args, 0, "list") { Value::List(v, sep, b) => (v, sep, b), @@ -145,7 +145,7 @@ pub(crate) fn register(f: &mut HashMap) { ); f.insert( "join".to_owned(), - Box::new(|mut args, _| { + Builtin::new(|mut args, _| { max_args!(args, 4); let (mut list1, sep1, brackets) = match arg!(args, 0, "list1") { Value::List(v, sep, brackets) => (v, sep, brackets), @@ -204,7 +204,7 @@ pub(crate) fn register(f: &mut HashMap) { ); f.insert( "is-bracketed".to_owned(), - Box::new(|mut args, _| { + Builtin::new(|mut args, _| { max_args!(args, 1); Ok(Value::bool(match arg!(args, 0, "list") { Value::List(.., brackets) => match brackets { @@ -217,7 +217,7 @@ pub(crate) fn register(f: &mut HashMap) { ); f.insert( "index".to_owned(), - Box::new(|mut args, _| { + Builtin::new(|mut args, _| { max_args!(args, 2); let list = match arg!(args, 0, "list") { Value::List(v, ..) => v, @@ -242,7 +242,7 @@ pub(crate) fn register(f: &mut HashMap) { ); f.insert( "zip".to_owned(), - Box::new(|args, _| { + Builtin::new(|args, _| { let lists = args .get_variadic()? .into_iter() diff --git a/src/builtin/map.rs b/src/builtin/map.rs index 4699fa8..004db8c 100644 --- a/src/builtin/map.rs +++ b/src/builtin/map.rs @@ -7,7 +7,7 @@ use crate::value::{SassMap, Value}; pub(crate) fn register(f: &mut HashMap) { f.insert( "map-get".to_owned(), - Box::new(|mut args, _| { + Builtin::new(|mut args, _| { max_args!(args, 2); let key = arg!(args, 1, "key"); let map = match arg!(args, 0, "map") { @@ -20,7 +20,7 @@ pub(crate) fn register(f: &mut HashMap) { ); f.insert( "map-has-key".to_owned(), - Box::new(|mut args, _| { + Builtin::new(|mut args, _| { max_args!(args, 2); let key = arg!(args, 1, "key"); let map = match arg!(args, 0, "map") { @@ -33,7 +33,7 @@ pub(crate) fn register(f: &mut HashMap) { ); f.insert( "map-keys".to_owned(), - Box::new(|mut args, _| { + Builtin::new(|mut args, _| { max_args!(args, 1); let map = match arg!(args, 0, "map") { Value::Map(m) => m, @@ -49,7 +49,7 @@ pub(crate) fn register(f: &mut HashMap) { ); f.insert( "map-values".to_owned(), - Box::new(|mut args, _| { + Builtin::new(|mut args, _| { max_args!(args, 1); let map = match arg!(args, 0, "map") { Value::Map(m) => m, @@ -65,7 +65,7 @@ pub(crate) fn register(f: &mut HashMap) { ); f.insert( "map-merge".to_owned(), - Box::new(|mut args, _| { + Builtin::new(|mut args, _| { max_args!(args, 2); let mut map1 = match arg!(args, 0, "map1") { Value::Map(m) => m, @@ -83,7 +83,7 @@ pub(crate) fn register(f: &mut HashMap) { ); f.insert( "map-remove".to_owned(), - Box::new(|mut args, _| { + Builtin::new(|mut args, _| { let mut map = match arg!(args, 0, "map") { Value::Map(m) => m, Value::List(v, ..) if v.is_empty() => SassMap::new(), diff --git a/src/builtin/math.rs b/src/builtin/math.rs index 24612a0..13a164a 100644 --- a/src/builtin/math.rs +++ b/src/builtin/math.rs @@ -7,7 +7,7 @@ use crate::value::{Number, Value}; pub(crate) fn register(f: &mut HashMap) { f.insert( "percentage".to_owned(), - Box::new(|mut args, _| { + Builtin::new(|mut args, _| { max_args!(args, 1); let num = match arg!(args, 0, "number") { Value::Dimension(n, Unit::None) => n * Number::from(100), @@ -21,7 +21,7 @@ pub(crate) fn register(f: &mut HashMap) { ); f.insert( "round".to_owned(), - Box::new(|mut args, _| { + Builtin::new(|mut args, _| { max_args!(args, 1); match arg!(args, 0, "number") { Value::Dimension(n, u) => Ok(Value::Dimension(n.round(), u)), @@ -31,7 +31,7 @@ pub(crate) fn register(f: &mut HashMap) { ); f.insert( "ceil".to_owned(), - Box::new(|mut args, _| { + Builtin::new(|mut args, _| { max_args!(args, 1); match arg!(args, 0, "number") { Value::Dimension(n, u) => Ok(Value::Dimension(n.ceil(), u)), @@ -41,7 +41,7 @@ pub(crate) fn register(f: &mut HashMap) { ); f.insert( "floor".to_owned(), - Box::new(|mut args, _| { + Builtin::new(|mut args, _| { max_args!(args, 1); match arg!(args, 0, "number") { Value::Dimension(n, u) => Ok(Value::Dimension(n.floor(), u)), @@ -51,7 +51,7 @@ pub(crate) fn register(f: &mut HashMap) { ); f.insert( "abs".to_owned(), - Box::new(|mut args, _| { + Builtin::new(|mut args, _| { max_args!(args, 1); match arg!(args, 0, "number") { Value::Dimension(n, u) => Ok(Value::Dimension(n.abs(), u)), @@ -61,7 +61,7 @@ pub(crate) fn register(f: &mut HashMap) { ); f.insert( "comparable".to_owned(), - Box::new(|mut args, _| { + Builtin::new(|mut args, _| { max_args!(args, 2); let unit1 = match arg!(args, 0, "number1") { Value::Dimension(_, u) => u, diff --git a/src/builtin/meta.rs b/src/builtin/meta.rs index dd9cbb9..d0ba248 100644 --- a/src/builtin/meta.rs +++ b/src/builtin/meta.rs @@ -4,12 +4,12 @@ use super::{Builtin, GLOBAL_FUNCTIONS}; use crate::common::{Brackets, QuoteKind}; use crate::scope::global_var_exists; use crate::unit::Unit; -use crate::value::Value; +use crate::value::{SassFunction, Value}; pub(crate) fn register(f: &mut HashMap) { f.insert( "if".to_owned(), - Box::new(|mut args, _| { + Builtin::new(|mut args, _| { max_args!(args, 3); if arg!(args, 0, "condition").is_true()? { Ok(arg!(args, 1, "if-true")) @@ -20,7 +20,7 @@ pub(crate) fn register(f: &mut HashMap) { ); f.insert( "feature-exists".to_owned(), - Box::new(|mut args, _| { + Builtin::new(|mut args, _| { max_args!(args, 1); match arg!(args, 0, "feature") { Value::Ident(s, _) => match s.as_str() { @@ -47,7 +47,7 @@ pub(crate) fn register(f: &mut HashMap) { ); f.insert( "unit".to_owned(), - Box::new(|mut args, _| { + Builtin::new(|mut args, _| { max_args!(args, 1); let unit = match arg!(args, 0, "number") { Value::Dimension(_, u) => u.to_string(), @@ -58,7 +58,7 @@ pub(crate) fn register(f: &mut HashMap) { ); f.insert( "type-of".to_owned(), - Box::new(|mut args, _| { + Builtin::new(|mut args, _| { max_args!(args, 1); let value = arg!(args, 0, "value"); Ok(Value::Ident(value.kind()?.to_owned(), QuoteKind::None)) @@ -66,7 +66,7 @@ pub(crate) fn register(f: &mut HashMap) { ); f.insert( "unitless".to_owned(), - Box::new(|mut args, _| { + Builtin::new(|mut args, _| { max_args!(args, 1); match arg!(args, 0, "number") { Value::Dimension(_, Unit::None) => Ok(Value::True), @@ -77,7 +77,7 @@ pub(crate) fn register(f: &mut HashMap) { ); f.insert( "inspect".to_owned(), - Box::new(|mut args, _| { + Builtin::new(|mut args, _| { max_args!(args, 1); Ok(Value::Ident( match arg!(args, 0, "value") { @@ -85,6 +85,7 @@ pub(crate) fn register(f: &mut HashMap) { Brackets::None => "()".to_string(), Brackets::Bracketed => "[]".to_string(), }, + Value::Function(f) => format!("get-function(\"{}\")", f.name()), v => v.to_string(), }, QuoteKind::None, @@ -93,7 +94,7 @@ pub(crate) fn register(f: &mut HashMap) { ); f.insert( "variable-exists".to_owned(), - Box::new(|mut args, scope| { + Builtin::new(|mut args, scope| { max_args!(args, 1); match arg!(args, 0, "name") { Value::Ident(s, _) => Ok(Value::bool(scope.var_exists(&s))), @@ -103,7 +104,7 @@ pub(crate) fn register(f: &mut HashMap) { ); f.insert( "global-variable-exists".to_owned(), - Box::new(|mut args, _| { + Builtin::new(|mut args, _| { max_args!(args, 1); match arg!(args, 0, "name") { Value::Ident(s, _) => Ok(Value::bool(global_var_exists(&s))), @@ -113,7 +114,7 @@ pub(crate) fn register(f: &mut HashMap) { ); f.insert( "mixin-exists".to_owned(), - Box::new(|mut args, scope| { + Builtin::new(|mut args, scope| { max_args!(args, 2); match arg!(args, 0, "name") { Value::Ident(s, _) => Ok(Value::bool(scope.mixin_exists(&s))), @@ -123,7 +124,7 @@ pub(crate) fn register(f: &mut HashMap) { ); f.insert( "function-exists".to_owned(), - Box::new(|mut args, scope| { + Builtin::new(|mut args, scope| { max_args!(args, 2); match arg!(args, 0, "name") { Value::Ident(s, _) => Ok(Value::bool( @@ -135,7 +136,7 @@ pub(crate) fn register(f: &mut HashMap) { ); f.insert( "get-function".to_owned(), - Box::new(|mut args, scope| { + Builtin::new(|mut args, scope| { max_args!(args, 2); let name = match arg!(args, 0, "name") { Value::Ident(s, _) => s, @@ -143,10 +144,18 @@ pub(crate) fn register(f: &mut HashMap) { }; let css = arg!(args, 1, "css" = Value::False).is_true()?; - Ok(Value::Function(Box::new(scope.get_fn(&name)?), css)) + let func = match scope.get_fn(&name) { + Ok(f) => SassFunction::UserDefined(Box::new(f), name), + Err(e) => match GLOBAL_FUNCTIONS.get(&name) { + Some(f) => SassFunction::Builtin(f.clone(), name), + None => return Err(e), + }, + }; + + Ok(Value::Function(func)) }), ); - f.insert("call".to_owned(), Box::new(|_args, _scope| { + f.insert("call".to_owned(), Builtin::new(|_args, _scope| { todo!("builtin function `call()` is blocked on refactoring how call args are stored and parsed") // let func = arg!(args, 0, "function").to_string(); // let func = match scope.get_fn(&func) { diff --git a/src/builtin/mod.rs b/src/builtin/mod.rs index 3c0548d..73ab12f 100644 --- a/src/builtin/mod.rs +++ b/src/builtin/mod.rs @@ -1,5 +1,6 @@ use once_cell::sync::Lazy; use std::collections::HashMap; +use std::sync::atomic::{AtomicUsize, Ordering}; use crate::args::CallArgs; use crate::error::SassResult; @@ -17,7 +18,23 @@ mod meta; mod selector; mod string; -pub(crate) type Builtin = Box SassResult + Send + Sync>; +static FUNCTION_COUNT: AtomicUsize = AtomicUsize::new(0); + +// TODO: impl Fn +#[derive(Clone)] +pub(crate) struct Builtin(pub fn(CallArgs, &Scope) -> SassResult, usize); +impl Builtin { + pub fn new(body: fn(CallArgs, &Scope) -> SassResult) -> Builtin { + let count = FUNCTION_COUNT.fetch_add(1, Ordering::Relaxed); + Self(body, count) + } +} + +impl PartialEq for Builtin { + fn eq(&self, other: &Self) -> bool { + self.1 == other.1 + } +} pub(crate) static GLOBAL_FUNCTIONS: Lazy> = Lazy::new(|| { let mut m = HashMap::new(); diff --git a/src/builtin/string.rs b/src/builtin/string.rs index b488b31..4978fd4 100644 --- a/src/builtin/string.rs +++ b/src/builtin/string.rs @@ -11,7 +11,7 @@ use crate::value::{Number, Value}; pub(crate) fn register(f: &mut HashMap) { f.insert( "to-upper-case".to_owned(), - Box::new(|mut args, _| { + Builtin::new(|mut args, _| { max_args!(args, 1); match arg!(args, 0, "string") { Value::Ident(i, q) => Ok(Value::Ident(i.to_ascii_uppercase(), q)), @@ -21,7 +21,7 @@ pub(crate) fn register(f: &mut HashMap) { ); f.insert( "to-lower-case".to_owned(), - Box::new(|mut args, _| { + Builtin::new(|mut args, _| { max_args!(args, 1); match arg!(args, 0, "string") { Value::Ident(i, q) => Ok(Value::Ident(i.to_ascii_lowercase(), q)), @@ -31,7 +31,7 @@ pub(crate) fn register(f: &mut HashMap) { ); f.insert( "str-length".to_owned(), - Box::new(|mut args, _| { + Builtin::new(|mut args, _| { max_args!(args, 1); match arg!(args, 0, "string") { Value::Ident(i, _) => Ok(Value::Dimension( @@ -44,7 +44,7 @@ pub(crate) fn register(f: &mut HashMap) { ); f.insert( "quote".to_owned(), - Box::new(|mut args, _| { + Builtin::new(|mut args, _| { max_args!(args, 1); match arg!(args, 0, "string") { Value::Ident(i, _) => Ok(Value::Ident(i, QuoteKind::Double)), @@ -54,7 +54,7 @@ pub(crate) fn register(f: &mut HashMap) { ); f.insert( "unquote".to_owned(), - Box::new(|mut args, _| { + Builtin::new(|mut args, _| { max_args!(args, 1); match arg!(args, 0, "string") { i @ Value::Ident(..) => Ok(i.unquote()), @@ -64,7 +64,7 @@ pub(crate) fn register(f: &mut HashMap) { ); f.insert( "str-slice".to_owned(), - Box::new(|mut args, _| { + Builtin::new(|mut args, _| { max_args!(args, 3); let (string, quotes) = match arg!(args, 0, "string") { Value::Ident(s, q) => (s, q), @@ -127,7 +127,7 @@ pub(crate) fn register(f: &mut HashMap) { ); f.insert( "str-index".to_owned(), - Box::new(|mut args, _| { + Builtin::new(|mut args, _| { max_args!(args, 2); let s1 = match arg!(args, 0, "string") { Value::Ident(i, _) => i, @@ -147,7 +147,7 @@ pub(crate) fn register(f: &mut HashMap) { ); f.insert( "str-insert".to_owned(), - Box::new(|mut args, _| { + Builtin::new(|mut args, _| { max_args!(args, 3); let (s1, quotes) = match arg!(args, 0, "string") { Value::Ident(i, q) => (i, q.normalize()), diff --git a/src/value/function.rs b/src/value/function.rs new file mode 100644 index 0000000..3c797d6 --- /dev/null +++ b/src/value/function.rs @@ -0,0 +1,42 @@ +use std::fmt; + +use crate::atrule::Function; +use crate::builtin::Builtin; + +#[derive(Clone)] +pub(crate) enum SassFunction { + Builtin(Builtin, String), + UserDefined(Box, String), +} + +impl SassFunction { + pub fn name(&self) -> &str { + match self { + Self::Builtin(_, name) => name, + Self::UserDefined(_, name) => name, + } + } +} + +impl fmt::Debug for SassFunction { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + todo!() + } +} + +impl PartialEq for SassFunction { + fn eq(&self, other: &Self) -> bool { + match self { + Self::UserDefined(f, ..) => match other { + Self::UserDefined(f2, ..) => f == f2, + Self::Builtin(..) => false, + }, + Self::Builtin(f, ..) => match other { + Self::UserDefined(..) => false, + Self::Builtin(f2, ..) => f == f2, + }, + } + } +} + +impl Eq for SassFunction {} diff --git a/src/value/mod.rs b/src/value/mod.rs index ff0609e..1cfb189 100644 --- a/src/value/mod.rs +++ b/src/value/mod.rs @@ -2,15 +2,16 @@ use std::cmp::Ordering; use std::fmt::{self, Display, Write}; use std::iter::Iterator; -use crate::atrule::Function; use crate::color::Color; use crate::common::{Brackets, ListSeparator, Op, QuoteKind}; use crate::error::SassResult; use crate::unit::{Unit, UNIT_CONVERSION_TABLE}; +pub(crate) use function::SassFunction; pub(crate) use map::SassMap; pub(crate) use number::Number; +mod function; mod map; mod number; mod ops; @@ -32,7 +33,7 @@ pub(crate) enum Value { Map(SassMap), ArgList(Vec), /// Returned by `get-function()` - Function(Box, bool), + Function(SassFunction), } impl Display for Value { diff --git a/src/value/parse.rs b/src/value/parse.rs index 4d95018..56cd52a 100644 --- a/src/value/parse.rs +++ b/src/value/parse.rs @@ -368,7 +368,7 @@ impl Value { Ok(f) => f, Err(_) => match GLOBAL_FUNCTIONS.get(&s) { Some(f) => { - return Ok(IntermediateValue::Value(f( + return Ok(IntermediateValue::Value(f.0( eat_call_args(toks, scope, super_selector)?, scope, )?)) diff --git a/tests/get-function.rs b/tests/get-function.rs index d76a9f9..006ca39 100644 --- a/tests/get-function.rs +++ b/tests/get-function.rs @@ -32,3 +32,23 @@ test!( a {b: type-of(get-function(user-defined));}", "a {\n b: function;\n}\n" ); +test!( + type_of_builtin_function, + "a {b: type-of(get-function(lighten));}", + "a {\n b: function;\n}\n" +); +test!( + same_builtin_function_is_equal, + "a {b: get-function(lighten) == get-function(lighten);}", + "a {\n b: true;\n}\n" +); +test!( + different_builtin_function_not_equal, + "a {b: get-function(lighten) == get-function(darken);}", + "a {\n b: false;\n}\n" +); +test!( + inspect_builtin_function, + "a {b: inspect(get-function(lighten));}", + "a {\n b: get-function(\"lighten\");\n}\n" +);