implement builtin fn map.deep-remove
This commit is contained in:
parent
f811b243c7
commit
eecff6d58a
1
.gitignore
vendored
1
.gitignore
vendored
@ -36,3 +36,4 @@ dart-sass
|
||||
sass-fairy
|
||||
duomo
|
||||
ibm-cloud-cognitive
|
||||
pico
|
||||
|
@ -9,7 +9,7 @@
|
||||
|
||||
# TBD
|
||||
|
||||
- implement builtin map-module function `map.deep-merge(..)`
|
||||
- implement builtin map-module functions `map.deep-merge(..)` and `map.deep-remove(..)`
|
||||
|
||||
# 0.12.3
|
||||
|
||||
|
@ -84,10 +84,10 @@ The spec runner does not work on Windows.
|
||||
Using a modified version of the spec runner that ignores warnings and error spans (but does include error messages), `grass` achieves the following results:
|
||||
|
||||
```
|
||||
2022-01-31
|
||||
PASSING: 6248
|
||||
FAILING: 624
|
||||
TOTAL: 6892
|
||||
2022-05-11
|
||||
PASSING: 6277
|
||||
FAILING: 596
|
||||
TOTAL: 6905
|
||||
```
|
||||
|
||||
The majority of the failing tests are purely aesthetic, relating to whitespace
|
||||
|
@ -1,3 +1,5 @@
|
||||
use std::iter::Peekable;
|
||||
|
||||
use crate::builtin::builtin_imports::*;
|
||||
|
||||
use crate::builtin::{
|
||||
@ -16,9 +18,7 @@ fn deep_merge_impl(map1: SassMap, map2: SassMap) -> SassMap {
|
||||
let mut result = map1;
|
||||
|
||||
for (key, value) in map2 {
|
||||
let result_map = result.get_ref(&key.node).and_then(Value::try_map);
|
||||
|
||||
match result_map {
|
||||
match result.get_ref(&key.node).and_then(Value::try_map) {
|
||||
Some(result_map) => match value.try_map() {
|
||||
Some(value_map) => {
|
||||
let merged = deep_merge_impl(result_map, value_map);
|
||||
@ -53,6 +53,92 @@ fn deep_merge(mut args: ArgumentResult, _: &mut Visitor) -> SassResult<Value> {
|
||||
Ok(Value::Map(deep_merge_impl(map1, map2)))
|
||||
}
|
||||
|
||||
fn deep_remove(mut args: ArgumentResult, _: &mut Visitor) -> SassResult<Value> {
|
||||
let span = args.span();
|
||||
|
||||
let map = args.get_err(0, "map")?.assert_map_with_name("map", span)?;
|
||||
|
||||
let key = args.get_err(1, "key")?;
|
||||
let keys = args.get_variadic()?.into_iter().map(|arg| arg.node);
|
||||
let mut keys = std::iter::once(key).chain(keys).collect::<Vec<_>>();
|
||||
|
||||
let last = keys.pop();
|
||||
|
||||
let map = modify_map(
|
||||
map,
|
||||
keys.into_iter(),
|
||||
|value| {
|
||||
let last = match last.as_ref() {
|
||||
Some(v) => v,
|
||||
None => return value,
|
||||
};
|
||||
|
||||
match value.try_map() {
|
||||
Some(mut nested_map) if nested_map.contains(last) => {
|
||||
nested_map.remove(last);
|
||||
Value::Map(nested_map)
|
||||
}
|
||||
Some(..) | None => value,
|
||||
}
|
||||
},
|
||||
false,
|
||||
span,
|
||||
);
|
||||
|
||||
Ok(map)
|
||||
}
|
||||
|
||||
fn modify_map(
|
||||
map: SassMap,
|
||||
keys: impl Iterator<Item = Value>,
|
||||
modify: impl Fn(Value) -> Value,
|
||||
// default=true
|
||||
add_nesting: bool,
|
||||
span: Span,
|
||||
) -> Value {
|
||||
let mut keys = keys.peekable();
|
||||
fn modify_nested_map(
|
||||
mut mutable_map: SassMap,
|
||||
mut keys: Peekable<impl Iterator<Item = Value>>,
|
||||
add_nesting: bool,
|
||||
span: Span,
|
||||
modify: impl Fn(Value) -> Value,
|
||||
) -> SassMap {
|
||||
let key = keys.next().unwrap();
|
||||
|
||||
if keys.peek().is_none() {
|
||||
let value = modify(mutable_map.get_ref(&key).cloned().unwrap_or(Value::Null));
|
||||
mutable_map.insert(key.span(span), value);
|
||||
return mutable_map;
|
||||
}
|
||||
|
||||
let nested_map = mutable_map.get_ref(&key).and_then(|v| v.try_map());
|
||||
|
||||
if nested_map.is_none() && !add_nesting {
|
||||
return mutable_map;
|
||||
}
|
||||
|
||||
mutable_map.insert(
|
||||
key.span(span),
|
||||
Value::Map(modify_nested_map(
|
||||
nested_map.unwrap_or_default(),
|
||||
keys,
|
||||
add_nesting,
|
||||
span,
|
||||
modify,
|
||||
)),
|
||||
);
|
||||
|
||||
mutable_map
|
||||
}
|
||||
|
||||
if keys.peek().is_some() {
|
||||
Value::Map(modify_nested_map(map, keys, add_nesting, span, modify))
|
||||
} else {
|
||||
modify(Value::Map(map))
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn declare(f: &mut Module) {
|
||||
f.insert_builtin("get", map_get);
|
||||
f.insert_builtin("has-key", map_has_key);
|
||||
@ -62,4 +148,5 @@ pub(crate) fn declare(f: &mut Module) {
|
||||
f.insert_builtin("values", map_values);
|
||||
f.insert_builtin("set", map_set);
|
||||
f.insert_builtin("deep-merge", deep_merge);
|
||||
f.insert_builtin("deep-remove", deep_remove);
|
||||
}
|
||||
|
@ -81,6 +81,10 @@ impl SassMap {
|
||||
self.0.into_iter().map(|(.., v)| v).collect()
|
||||
}
|
||||
|
||||
pub fn contains(&self, key: &Value) -> bool {
|
||||
self.0.iter().any(|(k, ..)| &k.node == key)
|
||||
}
|
||||
|
||||
pub fn as_list(self) -> Vec<Value> {
|
||||
self.0
|
||||
.into_iter()
|
||||
|
@ -133,3 +133,30 @@ test!(
|
||||
}"#,
|
||||
"a {\n color: (c: (d: e));\n}\n"
|
||||
);
|
||||
test!(
|
||||
deep_remove_key_dne,
|
||||
r#"@use "sass:map";
|
||||
|
||||
a {
|
||||
color: inspect(map.deep-remove((a: b), 1));
|
||||
}"#,
|
||||
"a {\n color: (a: b);\n}\n"
|
||||
);
|
||||
test!(
|
||||
deep_remove_nested_remove,
|
||||
r#"@use "sass:map";
|
||||
|
||||
a {
|
||||
color: inspect(map.deep-remove((c: (d: e)), c, d));
|
||||
}"#,
|
||||
"a {\n color: (c: ());\n}\n"
|
||||
);
|
||||
test!(
|
||||
deep_remove_nested_keys_dne,
|
||||
r#"@use "sass:map";
|
||||
|
||||
a {
|
||||
color: inspect(map.deep-remove((c: (d: e)), c, d, e, f, g));
|
||||
}"#,
|
||||
"a {\n color: (c: (d: e));\n}\n"
|
||||
);
|
||||
|
Loading…
x
Reference in New Issue
Block a user