koto_runtime/core_lib/
map.rs

1//! The `map` core library module
2
3use super::{iterator::adaptors, value_sort::compare_values};
4use crate::{Result, prelude::*};
5use std::cmp::Ordering;
6
7/// Initializes the `map` core library module
8pub fn make_module() -> KMap {
9    let result = KMap::with_type("core.map");
10
11    result.add_fn("clear", |ctx| {
12        let expected_error = "|Map|";
13
14        match map_instance_and_args(ctx, expected_error)? {
15            (KValue::Map(m), []) => {
16                m.data_mut().clear();
17                Ok(KValue::Map(m.clone()))
18            }
19            (instance, args) => unexpected_args_after_instance(expected_error, instance, args),
20        }
21    });
22
23    result.add_fn("contains_key", |ctx| {
24        let expected_error = "|Map, Any|";
25
26        match map_instance_and_args(ctx, expected_error)? {
27            (KValue::Map(m), [key]) => {
28                let result = m.data().contains_key(&ValueKey::try_from(key.clone())?);
29                Ok(result.into())
30            }
31            (instance, args) => unexpected_args_after_instance(expected_error, instance, args),
32        }
33    });
34
35    result.add_fn("extend", |ctx| {
36        let expected_error = "|Map, Iterable|";
37
38        match map_instance_and_args(ctx, expected_error)? {
39            (KValue::Map(m), [KValue::Map(other)]) => {
40                m.data_mut().extend(
41                    other
42                        .data()
43                        .iter()
44                        .map(|(key, value)| (key.clone(), value.clone())),
45                );
46                Ok(KValue::Map(m.clone()))
47            }
48            (KValue::Map(m), [iterable]) if iterable.is_iterable() => {
49                let m = m.clone();
50                let iterable = iterable.clone();
51                let iterator = ctx.vm.make_iterator(iterable)?;
52
53                {
54                    let mut map_data = m.data_mut();
55                    let (size_hint, _) = iterator.size_hint();
56                    map_data.reserve(size_hint);
57
58                    for output in iterator {
59                        use KIteratorOutput as Output;
60                        let (key, value) = match output {
61                            Output::ValuePair(key, value) => (key, value),
62                            Output::Value(KValue::Tuple(t)) if t.len() == 2 => {
63                                let key = t[0].clone();
64                                let value = t[1].clone();
65                                (key, value)
66                            }
67                            Output::Value(value) => (value, KValue::Null),
68                            Output::Error(error) => return Err(error),
69                        };
70
71                        map_data.insert(ValueKey::try_from(key.clone())?, value);
72                    }
73                }
74
75                Ok(KValue::Map(m))
76            }
77            (instance, args) => unexpected_args_after_instance(expected_error, instance, args),
78        }
79    });
80
81    result.add_fn("get", |ctx| {
82        let (map, key, default) = {
83            let expected_error = "|Map, Any|, or |Map, Any, Any|";
84
85            match map_instance_and_args(ctx, expected_error)? {
86                (KValue::Map(map), [key]) => (map, key, &KValue::Null),
87                (KValue::Map(map), [key, default]) => (map, key, default),
88                (instance, args) => {
89                    return unexpected_args_after_instance(expected_error, instance, args);
90                }
91            }
92        };
93
94        let result = map
95            .get(&ValueKey::try_from(key.clone())?)
96            .unwrap_or_else(|| default.clone());
97
98        Ok(result)
99    });
100
101    result.add_fn("get_index", |ctx| {
102        use KValue::{Map, Null, Number};
103        let expected_error = "|Map, Number|";
104
105        let (map, index, default) = match map_instance_and_args(ctx, expected_error)? {
106            (Map(map), [Number(n)]) => (map, n, Null),
107            (Map(map), [Number(n), default]) => (map, n, default.clone()),
108            (instance, args) => {
109                return unexpected_args_after_instance(expected_error, instance, args);
110            }
111        };
112
113        if *index >= 0 {
114            match map.data().get_index(usize::from(index)) {
115                Some((key, value)) => Ok(KValue::Tuple(
116                    vec![key.value().clone(), value.clone()].into(),
117                )),
118                None => Ok(default),
119            }
120        } else {
121            Ok(default)
122        }
123    });
124
125    result.add_fn("get_meta", |ctx| {
126        let expected_error = "|Map|";
127
128        match map_instance_and_args(ctx, expected_error)? {
129            (KValue::Map(map), []) => {
130                if map.meta_map().is_some() {
131                    Ok(KValue::Map(KMap::from_data_and_meta_maps(
132                        &KMap::default(),
133                        map,
134                    )))
135                } else {
136                    Ok(KValue::Null)
137                }
138            }
139            (instance, args) => unexpected_args_after_instance(expected_error, instance, args),
140        }
141    });
142
143    result.add_fn("insert", |ctx| {
144        let expected_error = "|Map, Any|, or |Map, Any, Any|";
145
146        match map_instance_and_args(ctx, expected_error)? {
147            (KValue::Map(m), [key]) => match m
148                .data_mut()
149                .insert(ValueKey::try_from(key.clone())?, KValue::Null)
150            {
151                Some(old_value) => Ok(old_value),
152                None => Ok(KValue::Null),
153            },
154            (KValue::Map(m), [key, value]) => {
155                match m
156                    .data_mut()
157                    .insert(ValueKey::try_from(key.clone())?, value.clone())
158                {
159                    Some(old_value) => Ok(old_value),
160                    None => Ok(KValue::Null),
161                }
162            }
163            (instance, args) => unexpected_args_after_instance(expected_error, instance, args),
164        }
165    });
166
167    result.add_fn("is_empty", |ctx| {
168        let expected_error = "|Map|";
169
170        match map_instance_and_args(ctx, expected_error)? {
171            (KValue::Map(m), []) => Ok(m.is_empty().into()),
172            (instance, args) => unexpected_args_after_instance(expected_error, instance, args),
173        }
174    });
175
176    result.add_fn("keys", |ctx| {
177        let expected_error = "|Map|";
178
179        match map_instance_and_args(ctx, expected_error)? {
180            (KValue::Map(m), []) => {
181                let result = adaptors::PairFirst::new(KIterator::with_map(m.clone()));
182                Ok(KIterator::new(result).into())
183            }
184            (instance, args) => unexpected_args_after_instance(expected_error, instance, args),
185        }
186    });
187
188    result.add_fn("remove", |ctx| {
189        let expected_error = "|Map, Any|";
190
191        match map_instance_and_args(ctx, expected_error)? {
192            (KValue::Map(m), [key]) => {
193                match m.data_mut().shift_remove(&ValueKey::try_from(key.clone())?) {
194                    Some(old_value) => Ok(old_value),
195                    None => Ok(KValue::Null),
196                }
197            }
198            (instance, args) => unexpected_args_after_instance(expected_error, instance, args),
199        }
200    });
201
202    result.add_fn("sort", |ctx| {
203        let expected_error = "|Map|, or |Map, |Any, Any| -> Any|";
204
205        match map_instance_and_args(ctx, expected_error)? {
206            (KValue::Map(m), []) => {
207                let mut error = None;
208                m.data_mut().sort_by(|key_a, _, key_b, _| {
209                    if error.is_some() {
210                        return Ordering::Equal;
211                    }
212
213                    match key_a.partial_cmp(key_b) {
214                        Some(ordering) => ordering,
215                        None => {
216                            // This should never happen, ValueKeys can only be made with sortable values
217                            error = Some(runtime_error!("invalid map key encountered"));
218                            Ordering::Equal
219                        }
220                    }
221                });
222
223                if let Some(error) = error {
224                    error
225                } else {
226                    Ok(KValue::Map(m.clone()))
227                }
228            }
229            (KValue::Map(m), [f]) if f.is_callable() => {
230                let m = m.clone();
231                let f = f.clone();
232                let mut error = None;
233
234                let get_sort_key = |vm: &mut KotoVm,
235                                    cache: &mut ValueMap,
236                                    key: &ValueKey,
237                                    value: &KValue|
238                 -> Result<KValue> {
239                    let value =
240                        vm.call_function(f.clone(), &[key.value().clone(), value.clone()])?;
241                    cache.insert(key.clone(), value.clone());
242                    Ok(value)
243                };
244
245                let mut cache = ValueMap::with_capacity(m.len());
246                m.data_mut().sort_by(|key_a, value_a, key_b, value_b| {
247                    if error.is_some() {
248                        return Ordering::Equal;
249                    }
250
251                    let value_a = match cache.get(key_a) {
252                        Some(value) => value.clone(),
253                        None => match get_sort_key(ctx.vm, &mut cache, key_a, value_a) {
254                            Ok(val) => val,
255                            Err(e) => {
256                                error.get_or_insert(Err(e));
257                                KValue::Null
258                            }
259                        },
260                    };
261                    let value_b = match cache.get(key_b) {
262                        Some(value) => value.clone(),
263                        None => match get_sort_key(ctx.vm, &mut cache, key_b, value_b) {
264                            Ok(val) => val,
265                            Err(e) => {
266                                error.get_or_insert(Err(e));
267                                KValue::Null
268                            }
269                        },
270                    };
271
272                    match compare_values(ctx.vm, &value_a, &value_b) {
273                        Ok(ordering) => ordering,
274                        Err(e) => {
275                            error.get_or_insert(Err(e));
276                            Ordering::Equal
277                        }
278                    }
279                });
280
281                if let Some(error) = error {
282                    error
283                } else {
284                    Ok(KValue::Map(m))
285                }
286            }
287            (instance, args) => unexpected_args_after_instance(expected_error, instance, args),
288        }
289    });
290
291    result.add_fn("update", |ctx| {
292        let expected_error = "|Map, Any, |Any| -> Any||, or |Map, Any, Any, |Any| -> Any|";
293
294        match map_instance_and_args(ctx, expected_error)? {
295            (KValue::Map(m), [key, f]) if f.is_callable() => do_map_update(
296                m.clone(),
297                ValueKey::try_from(key.clone())?,
298                KValue::Null,
299                f.clone(),
300                ctx.vm,
301            ),
302            (KValue::Map(m), [key, default, f]) if f.is_callable() => do_map_update(
303                m.clone(),
304                ValueKey::try_from(key.clone())?,
305                default.clone(),
306                f.clone(),
307                ctx.vm,
308            ),
309            (instance, args) => unexpected_args_after_instance(expected_error, instance, args),
310        }
311    });
312
313    result.add_fn("values", |ctx| {
314        let expected_error = "|Map|";
315
316        match map_instance_and_args(ctx, expected_error)? {
317            (KValue::Map(m), []) => {
318                let result = adaptors::PairSecond::new(KIterator::with_map(m.clone()));
319                Ok(KIterator::new(result).into())
320            }
321            (instance, args) => unexpected_args_after_instance(expected_error, instance, args),
322        }
323    });
324
325    result.add_fn("with_meta", |ctx| {
326        let expected_error = "|Map, Map|";
327
328        match map_instance_and_args(ctx, expected_error)? {
329            (KValue::Map(data), [KValue::Map(meta)]) => {
330                let mut data = data.clone();
331                data.set_meta_map(meta.meta_map().cloned());
332                Ok(data.into())
333            }
334            (instance, args) => unexpected_args_after_instance(expected_error, instance, args),
335        }
336    });
337
338    result
339}
340
341fn do_map_update(
342    map: KMap,
343    key: ValueKey,
344    default: KValue,
345    f: KValue,
346    vm: &mut KotoVm,
347) -> Result<KValue> {
348    if !map.data().contains_key(&key) {
349        map.data_mut().insert(key.clone(), default);
350    }
351    let value = map.get(&key).unwrap();
352    match vm.call_function(f, value) {
353        Ok(new_value) => {
354            map.data_mut().insert(key, new_value.clone());
355            Ok(new_value)
356        }
357        Err(error) => Err(error),
358    }
359}
360
361fn map_instance_and_args<'a>(
362    ctx: &'a CallContext<'_>,
363    expected_error: &str,
364) -> Result<(&'a KValue, &'a [KValue])> {
365    use KValue::Map;
366
367    // For core.map ops, allow using maps with metamaps when the ops are used as standalone
368    // functions.
369    match (ctx.instance(), ctx.args()) {
370        (instance @ Map(m), args) if m.meta_map().is_none() => Ok((instance, args)),
371        (_, [first @ Map(_), rest @ ..]) => Ok((first, rest)),
372        (instance, args) => unexpected_args_after_instance(expected_error, instance, args),
373    }
374}