1use super::{is_not, CallError, CheckedArg, FunctionMap, ResolvedArgs};
2use crate::css::{Value, ValueMap};
3use crate::value::ListSeparator;
4use crate::Scope;
5
6pub fn create_module() -> Scope {
11 let mut f = Scope::builtin_module("sass:map");
12 let mut g = Scope::new_global(Default::default()); def!(f, deep_merge(map1, map2), |s| {
15 let mut map1 = s.get(name!(map1))?;
16 let map2 = s.get_map(name!(map2), as_va_map)?;
17 do_deep_merge(&mut map1, map2);
18 Ok(Value::Map(map1))
19 });
20
21 def_va!(f, deep_remove(map, key, keys), |s| {
22 let mut map = s.get(name!(map))?;
23 let key = s.get(name!(key))?;
24 let keychain = match s.get(name!(keys))? {
25 Value::ArgList(mut args) => {
26 args.positional.insert(0, key);
27 args.positional
28 }
29 Value::List(mut keys, ..) => {
30 keys.insert(0, key);
31 keys
32 }
33 Value::Null => vec![key],
34 single => vec![key, single],
35 };
36 do_deep_remove(&mut map, &keychain);
37 Ok(Value::Map(map))
38 });
39
40 fn find_value<'a>(
42 map: &'a ValueMap,
43 key: &Value,
44 keys: &Value,
45 ) -> Result<Option<&'a Value>, CallError> {
46 let mut val = map.get(key);
47 match keys {
48 Value::ArgList(args) => {
49 args.check_no_named().map_err(CallError::msg)?;
50 for k in &args.positional {
51 match val {
52 Some(Value::Map(m)) => {
53 val = m.get(k);
54 }
55 _ => return Ok(None),
56 }
57 }
58 }
59 Value::List(keys, ..) => {
60 for k in keys {
61 match val {
62 Some(Value::Map(m)) => {
63 val = m.get(k);
64 }
65 _ => return Ok(None),
66 }
67 }
68 }
69 Value::Null => (),
70 single_key => match val {
71 Some(Value::Map(m)) => {
72 val = m.get(single_key);
73 }
74 _ => return Ok(None),
75 },
76 };
77 Ok(val)
78 }
79 def_va!(f, get(map, key, keys), |s| {
80 let map = s.get(name!(map))?;
81 Ok(find_value(&map, &s.get(name!(key))?, &s.get(name!(keys))?)?
82 .cloned()
83 .unwrap_or(Value::Null))
84 });
85 def_va!(f, has_key(map, key, keys), |s| {
86 let map = s.get(name!(map))?;
87 Ok(find_value(&map, &s.get(name!(key))?, &s.get(name!(keys))?)?
88 .is_some()
89 .into())
90 });
91 def!(f, keys(map), |s| {
92 let map: ValueMap = s.get(name!(map))?;
93 Ok(Value::List(
94 map.keys().cloned().collect(),
95 Some(ListSeparator::Comma),
96 false,
97 ))
98 });
99 def_va!(g, merge(map1, args), |s| {
100 let mut map1 = s.get(name!(map1))?;
101 let (keys, map2) = match s.get(name!(args))? {
102 Value::ArgList(mut args) => {
103 if let Some(map2) = args.only_named(&name!(map2)) {
104 (vec![], as_va_map(map2).named(name!(map2))?)
105 } else {
106 let mut values = args.positional;
107 let map2 = values
108 .pop()
109 .ok_or_else(|| {
110 CallError::msg("Expected $args to contain a key.")
111 })?
112 .try_into()
113 .named(name!(map2))?;
114 (values, map2)
115 }
116 }
117 direct => (vec![], direct.try_into().named(name!(map2))?),
118 };
119 fn do_merge(
120 mut keys: impl Iterator<Item = Value>,
121 map1: &mut ValueMap,
122 map2: ValueMap,
123 ) {
124 if let Some(key) = keys.next() {
125 if let Some(Value::Map(m1)) = map1.get_mut(&key) {
126 do_merge(keys, m1, map2);
127 } else {
128 let mut m2 = ValueMap::new();
129 do_merge(keys, &mut m2, map2);
130 map1.insert(key, Value::Map(m2));
131 }
132 } else {
133 for (key, value) in map2 {
134 map1.insert(key, value);
135 }
136 }
137 }
138 do_merge(keys.into_iter(), &mut map1, map2);
139 Ok(Value::Map(map1))
140 });
141 def_va!(g, remove(map, keys), |s| {
142 let mut map: ValueMap = s.get(name!(map))?;
143 match s.get(name!(keys))? {
144 Value::ArgList(mut args) => {
145 if let Some(key) = args.named.remove(&name!(key)) {
146 if args.positional.is_empty() {
147 map.remove(&key);
148 } else {
149 return Err(CallError::msg(
150 "Argument $key was passed both by position and by name."
151 ));
152 }
153 }
154 args.check_no_named().map_err(CallError::msg)?;
155 for key in args.positional {
156 map.remove(&key);
157 }
158 }
159 Value::List(keys, ..) => {
160 for key in keys {
161 map.remove(&key);
162 }
163 }
164 key => {
165 map.remove(&key);
166 }
167 }
168 Ok(Value::Map(map))
169 });
170 def_va!(g, set(map, args), set);
171 def!(f, values(map), |s| {
172 let map: ValueMap = s.get(name!(map))?;
173 Ok(Value::List(
174 map.values().cloned().collect(),
175 Some(ListSeparator::Comma),
176 false,
177 ))
178 });
179 f.expose_star(&g);
180 f
181}
182
183pub fn expose(m: &Scope, global: &mut FunctionMap) {
184 for (gname, lname) in &[
185 (name!(map_get), name!(get)),
186 (name!(map_set), name!(set)),
187 (name!(map_has_key), name!(has_key)),
188 (name!(map_keys), name!(keys)),
189 (name!(map_merge), name!(merge)),
190 (name!(map_remove), name!(remove)),
191 (name!(map_values), name!(values)),
192 ] {
193 global.insert(gname.clone(), m.get_lfunction(lname));
194 }
195}
196
197impl TryFrom<Value> for ValueMap {
198 type Error = String;
199 fn try_from(v: Value) -> Result<Self, String> {
200 match v {
201 Value::Map(m) => Ok(m),
202 Value::List(ref l, ..) if l.is_empty() => Ok(Self::new()),
204 v => Err(is_not(&v, "a map")),
205 }
206 }
207}
208
209fn as_va_map(v: Value) -> Result<ValueMap, String> {
210 match v {
211 Value::ArgList(args) => {
212 args.check_no_named().map_err(|e| e.to_string())?;
213 let mut values = args.positional;
214 let mut result = if let Some(last) = values.pop() {
215 last.try_into()?
216 } else {
217 return Err("arglist unexpectedly empty".into());
218 };
219 while let Some(prev) = values.pop() {
220 result = ValueMap::singleton(prev, Value::Map(result));
221 }
222 Ok(result)
223 }
224 Value::List(mut values, ..) => {
225 let mut result = if let Some(last) = values.pop() {
226 last.try_into()?
227 } else {
228 ValueMap::new()
229 };
230 while let Some(prev) = values.pop() {
231 result = ValueMap::singleton(prev, Value::Map(result));
232 }
233 Ok(result)
234 }
235 v => v.try_into(),
236 }
237}
238
239fn do_deep_merge(map1: &mut ValueMap, map2: ValueMap) {
240 for (key, value) in map2 {
241 match (map1.get_mut(&key), value) {
242 (Some(Value::Map(ref mut m1)), Value::Map(m2)) => {
243 do_deep_merge(m1, m2);
244 }
245 (Some(Value::Map(_)), Value::List(ref l, ..)) if l.is_empty() => {
246 }
248 (_, v2) => {
249 map1.insert(key, v2);
250 }
251 }
252 }
253}
254
255fn do_deep_remove(map: &mut ValueMap, keys: &[Value]) {
256 match keys.len() {
257 0 => (), 1 => {
259 map.remove(&keys[0]);
260 }
261 _ => {
262 if let Some(Value::Map(inner)) = map.get_mut(&keys[0]) {
263 do_deep_remove(inner, &keys[1..]);
264 }
265 }
266 }
267}
268
269fn set(s: &ResolvedArgs) -> Result<Value, CallError> {
270 let map = s.get(name!(map))?;
271 match s.get(name!(args))? {
272 Value::ArgList(mut args) => {
273 let keys = match args.named.remove(&"keys".into()) {
274 Some(Value::List(v, ..)) => Some(v),
275 Some(v) => Some(vec![v]),
276 None => None,
277 };
278 let key = args.named.remove(&"key".into());
279 if key.is_none() && keys.is_none() && args.positional.is_empty() {
280 return Err(CallError::msg(
281 "Expected $args to contain a key.",
282 ));
283 }
284 let value = args
285 .named
286 .remove(&"value".into())
287 .or_else(|| {
288 if key.is_some()
289 || keys.is_some()
290 || args.positional.len() > 1
291 {
292 args.positional.pop()
293 } else {
294 None
295 }
296 })
297 .ok_or_else(|| {
298 CallError::msg("Expected $args to contain a value.")
299 })?;
300
301 let mut keys = match (keys, args.positional.is_empty()) {
302 (Some(keys), true) => keys,
303 (None, _) => args.positional,
304 (Some(_), false) => {
305 return Err(CallError::msg(
306 "Got $keys both by name and by position.",
307 ))
308 }
309 };
310 if let Some(key) = key {
311 keys.push(key);
312 }
313 Ok(Value::Map(set_inner(map, &keys, value)?))
314 }
315 Value::List(mut v, ..) => {
316 if let Some(value) = v.pop() {
317 Ok(Value::Map(set_inner(map, &v, value)?))
318 } else {
319 Err(CallError::msg("Expected $args to contain a key."))
320 }
321 }
322 Value::Map(mut args) => {
323 let mut keys = match args.remove(&"keys".into()) {
324 Some(Value::List(v, ..)) => v,
325 Some(v) => vec![v],
326 None => vec![],
327 };
328 if let Some(key) = args.remove(&"key".into()) {
329 keys.push(key);
330 }
331 let value = args.remove(&"value".into()).ok_or_else(|| {
332 CallError::msg("Expected $args to contain a value.")
333 })?;
334 Ok(Value::Map(set_inner(map, &keys, value)?))
335 }
336 _ => Err(CallError::msg("Expected $args to contain a value.")),
337 }
338}
339fn set_inner(
340 mut map: ValueMap,
341 keys: &[Value],
342 value: Value,
343) -> Result<ValueMap, CallError> {
344 if let Some((key, rest)) = keys.split_first() {
345 let value = if rest.is_empty() {
346 value
347 } else {
348 let inner = match map.remove(key) {
349 Some(Value::Map(inner)) => inner,
350 _ => ValueMap::new(),
351 };
352 Value::Map(set_inner(inner, rest, value)?)
353 };
354 map.insert(key.clone(), value);
355 Ok(map)
356 } else {
357 Err(CallError::msg("Expected $args to contain a value."))
358 }
359}
360
361#[cfg(test)]
362mod test {
363 mod map_get {
366 use super::check_val;
367
368 #[test]
369 fn a() {
370 check_val("map-get((\"foo\": 1, \"bar\": 2), \"foo\");", "1")
371 }
372 #[test]
373 fn b() {
374 check_val("map-get((\"foo\": 1, \"bar\": 2), \"bar\");", "2")
375 }
376 #[test]
377 fn c() {
378 check_val("map-get((\"foo\": 1, \"bar\": 2), \"baz\");", "")
379 }
380 }
381
382 mod map_has_key {
383 use super::check_val;
384
385 #[test]
386 fn a() {
387 check_val(
388 "map-has-key((\"foo\": 1, \"bar\": 2), \"foo\");",
389 "true",
390 )
391 }
392 #[test]
393 fn b() {
394 check_val(
395 "map-has-key((\"foo\": 1, \"bar\": 2), \"baz\");",
396 "false",
397 )
398 }
399 }
400
401 fn check_val(src: &str, correct: &str) {
402 use crate::variablescope::test::do_evaluate;
403 assert_eq!(do_evaluate(&[], src.as_bytes()), correct)
404 }
405}