From f16fc40bdefd10d6ebe5c1c77e4c12616e8f1486 Mon Sep 17 00:00:00 2001 From: Connor Skees Date: Sun, 4 Aug 2024 17:42:51 +0000 Subject: [PATCH] support $keys... argument to map-has-key --- crates/compiler/src/builtin/functions/map.rs | 26 ++++++++- crates/lib/tests/map.rs | 61 ++++++++++++++++++++ 2 files changed, 85 insertions(+), 2 deletions(-) diff --git a/crates/compiler/src/builtin/functions/map.rs b/crates/compiler/src/builtin/functions/map.rs index c4db25a2..66dda99a 100644 --- a/crates/compiler/src/builtin/functions/map.rs +++ b/crates/compiler/src/builtin/functions/map.rs @@ -41,12 +41,34 @@ pub(crate) fn map_get(mut args: ArgumentResult, visitor: &mut Visitor) -> SassRe } pub(crate) fn map_has_key(mut args: ArgumentResult, visitor: &mut Visitor) -> SassResult { - args.max_args(2)?; let key = args.get_err(1, "key")?; let map = args .get_err(0, "map")? .assert_map_with_name("map", args.span())?; - Ok(Value::bool(map.get(&key).is_some())) + + // since we already extracted the map and first key, + // neither will be returned in the variadic args list + let keys = args.get_variadic()?; + + let mut val = match map.get(&key) { + Some(v) => v, + None => return Ok(Value::False), + }; + for key in keys { + // if at any point we find a value that's not a map, + // we return null + let val_map = match val.try_map() { + Some(val_map) => val_map, + None => return Ok(Value::False), + }; + + val = match val_map.get(&key) { + Some(v) => v, + None => return Ok(Value::False), + }; + } + + Ok(Value::True) } pub(crate) fn map_keys(mut args: ArgumentResult, visitor: &mut Visitor) -> SassResult { diff --git a/crates/lib/tests/map.rs b/crates/lib/tests/map.rs index d6dbfb38..f3eaddee 100644 --- a/crates/lib/tests/map.rs +++ b/crates/lib/tests/map.rs @@ -255,6 +255,67 @@ test!( "a {\n color: inspect((a: b, !important: c));\n}\n", "a {\n color: (a: b, !important: c);\n}\n" ); +test!( + map_has_key_multiple_keys_true, + r#" + @use "sass:map"; + $fonts: ( + "Helvetica": ( + "weights": ( + "regular": 400, + "medium": 500, + "bold": 700 + ) + ) + ); + a { + color: map.has-key($fonts, "Helvetica", "weights", "regular"); + }"#, + "a {\n color: true;\n}\n" +); +test!( + map_has_key_multiple_keys_false, + r#" + @use "sass:map"; + $fonts: ( + "Helvetica": ( + "weights": ( + "regular": 400, + "medium": 500, + "bold": 700 + ) + ) + ); + a { + color: map.has-key($fonts, "Helvetica", "colors"); + }"#, + "a {\n color: false;\n}\n" +); +test!( + map_has_key_multiple_keys_value_is_null, + "a {\n color: map-has-key((a: (b: null)), \"a\", \"b\");\n}\n", + "a {\n color: true;\n}\n" +); +test!( + map_has_key_multiple_keys_value_is_false, + "a {\n color: map-has-key((a: (b: false)), \"a\", \"b\");\n}\n", + "a {\n color: true;\n}\n" +); +test!( + map_has_key_multiple_keys_deeply_nested, + "a {\n color: map-has-key((a: (b: (c: (d: (e: (f: (g: (h: (i: (j: (k: (l: m)))))))))))), a, b, c, d, e, f, g, h, i, j, k, l);\n}\n", + "a {\n color: true;\n}\n" +); +test!( + map_has_key_multiple_keys_empty_map, + "a {\n color: map-has-key((), a, b, c);\n}\n", + "a {\n color: false;\n}\n" +); +test!( + map_has_key_multiple_keys_value_isnt_map, + "a {\n color: map-has-key((a: (b: 5)), a, b, c);\n}\n", + "a {\n color: false;\n}\n" +); error!( bang_identifier_not_important_as_key, "a {\n color: inspect((a: b, !a: c));\n}\n", r#"Error: expected ")"."#