tokay/value/
list.rs

1//! List object
2use super::{BoxedObject, Iter, Object, RefValue};
3use crate::value;
4use tokay_macros::tokay_method;
5extern crate self as tokay;
6
7/// Alias for the inner list definition
8type InnerList = Vec<RefValue>;
9
10/// List object type
11#[derive(Debug, Clone, PartialEq, PartialOrd)]
12#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
13#[cfg_attr(feature = "serde", serde(transparent))]
14pub struct List {
15    list: InnerList,
16}
17
18impl Object for List {
19    fn severity(&self) -> u8 {
20        30
21    }
22
23    fn name(&self) -> &'static str {
24        "list"
25    }
26
27    fn repr(&self) -> String {
28        let mut ret = "(".to_string();
29
30        for item in self.iter() {
31            if ret.len() > 1 {
32                ret.push_str(", ");
33            }
34
35            ret.push_str(&item.borrow().repr());
36        }
37
38        if self.len() <= 1 {
39            ret.push_str(", )");
40        } else {
41            ret.push_str(")");
42        }
43
44        ret
45    }
46
47    fn is_true(&self) -> bool {
48        self.len() > 0
49    }
50
51    fn is_mutable(&self) -> bool {
52        true
53    }
54}
55
56#[allow(unused_doc_comments)]
57impl List {
58    pub fn new() -> Self {
59        Self {
60            list: InnerList::new(),
61        }
62    }
63
64    pub fn with_capacity(len: usize) -> Self {
65        Self {
66            list: InnerList::with_capacity(len),
67        }
68    }
69
70    /// Constructs a new list from all specified `args`.
71    tokay_method!("list : @*args", {
72        let list = if args.len() == 1 {
73            // In case of an iter, use the collect-method
74            if args[0].is("iter") {
75                return Ok(args[0]
76                    .call_method("collect", context, Vec::new(), None)?
77                    .unwrap());
78            }
79            // Otherwise, create a list with one item
80            else {
81                List::from(args[0].clone())
82            }
83        }
84        // When multiple items are provided, turn these into list items
85        else {
86            List { list: args }
87        };
88
89        Ok(RefValue::from(list))
90    });
91
92    /// Clone `list` into a standalone copy.
93    tokay_method!("list_clone : @list", {
94        let borrowed = list.borrow();
95
96        if let Some(list) = borrowed.object::<List>() {
97            Ok(RefValue::from(list.clone()))
98        } else {
99            Ok(RefValue::from(List {
100                list: vec![list.clone()],
101            }))
102        }
103    });
104
105    /// Returns the length of the specified `list`.
106    tokay_method!("list_len : @list", {
107        let list = list.borrow();
108
109        Ok(RefValue::from(if let Some(list) = list.object::<List>() {
110            list.len()
111        } else if list.is_void() {
112            0
113        } else {
114            1
115        }))
116    });
117
118    /** Retrieves item with `index` from `list`.
119
120    When `upsert=true`, it fills the list to the specified `index` and inserts and returns the `default` value.
121    A `default`-value of `void` will become `null` in upsert-mode.
122
123    Otherwise, `default` is just returned when the specified `item` is not present.
124
125    This method is also invoked when using the `dict` item syntax.
126    */
127    tokay_method!(
128        "list_get_item : @list, index, default=void, upsert=false",
129        {
130            // In case list is not a list, make it a list.
131            if !list.is("list") {
132                list = Self::list(vec![list], None)?;
133            }
134
135            {
136                let list = list.borrow();
137                let index = index.to_usize()?;
138
139                if let Some(value) = list.object::<List>().unwrap().get(index) {
140                    return Ok(value.clone());
141                }
142            }
143
144            if upsert.is_true() {
145                // follow the void paradigm; void cannot be upserted, so default to null.
146                if default.is_void() {
147                    default = value![null];
148                }
149
150                return Self::list_set_item(vec![list, index, default], None);
151            }
152
153            Ok(default)
154        }
155    );
156
157    tokay_method!("list_set_item : @list, item, value=void", {
158        // In case list is not a list, make it a list.
159        if !list.is("list") {
160            list = Self::list(vec![list], None)?;
161        }
162
163        let mut list = list.borrow_mut();
164        let list = list.object_mut::<List>().unwrap();
165
166        let item = item.to_usize()?;
167        let len = list.len();
168
169        if item >= len {
170            list.resize_with(item + 1, Default::default)
171        }
172
173        if value.is_void() {
174            value = list.remove(item);
175        } else {
176            list[item] = value.clone();
177        }
178
179        Ok(value)
180    });
181
182    tokay_method!("list_flatten : @list", {
183        if let Some(list) = list.borrow().object::<List>() {
184            let mut ret = List::with_capacity(list.len());
185
186            for item in list.iter() {
187                if let Some(list) = item.borrow().object::<List>() {
188                    // TODO: flatten list recursively until a parametrizable depth.
189                    for item in list.iter() {
190                        ret.push(item.clone());
191                    }
192                } else {
193                    ret.push(item.clone());
194                }
195            }
196
197            return Ok(RefValue::from(ret));
198        }
199
200        Ok(RefValue::from(crate::value!([list])))
201    });
202
203    tokay_method!("list_iadd : @list, append", {
204        // Don't append void
205        if append.is_void() {
206            return Ok(list);
207        }
208
209        // In case list is not a list, make it a list.
210        if !list.is("list") {
211            let item = RefValue::from(list.borrow().clone());
212            if item.is_void() {
213                *(list.borrow_mut()) = Self::list(vec![], None)?.into();
214            } else {
215                *(list.borrow_mut()) = Self::list(vec![item], None)?.into();
216            }
217        }
218
219        // Append or extend in-place when possible.
220        if let (Ok(mut inner), Ok(to_append)) = (list.try_borrow_mut(), append.try_borrow()) {
221            let inner = inner.object_mut::<List>().unwrap();
222
223            // When append is a list, append all items to list
224            if let Some(append) = to_append.object::<List>() {
225                inner.reserve(append.len());
226
227                for item in append.iter() {
228                    inner.push(item.clone());
229                }
230            // Otherwise, just push append to the list.
231            } else {
232                inner.push(append.clone());
233            }
234
235            return Ok(list.clone());
236        }
237
238        // Otherwise, perform ordinary add first, then re-assign to list
239        let new = Self::list_add(vec![list.clone(), append.clone()], None)?;
240        *list.borrow_mut() = new.into();
241
242        Ok(list)
243    });
244
245    tokay_method!("list_add : @list, append", {
246        // Don't append void
247        if append.is_void() {
248            return Ok(list);
249        }
250
251        // In case list is not a list, make it a list.
252        if !list.is("list") {
253            list = RefValue::from(List::from(list));
254        }
255
256        let mut list = list.borrow().object::<List>().unwrap().clone();
257
258        // When append is a list, append all items to list
259        if let Some(append) = append.borrow().object::<List>() {
260            list.reserve(append.len());
261
262            for item in append.iter() {
263                list.push(item.clone());
264            }
265        // Otherwise, just push append to the list.
266        } else {
267            list.push(append.clone());
268        }
269
270        Ok(RefValue::from(list))
271    });
272
273    /** Explicitly pushes `item` to `list`.
274
275    When `index` is provided, the value is inserted at the given offset,
276    otherwise it is appended (pushed) to the list's end. */
277    tokay_method!("list_push : @list, item, index=void", {
278        // Don't push void
279        if item.is_void() {
280            return Ok(value![void]);
281        }
282
283        let mut list = list.borrow_mut();
284
285        // If first parameter is not a list, just do nothing!
286        if let Some(list) = list.object_mut::<List>() {
287            if index.is_void() {
288                list.push(item);
289            } else {
290                let index = index.to_usize()?;
291                let len = list.len();
292
293                if index > len {
294                    return Err(format!(
295                        "{} provided index {} out of range in list sized {}",
296                        __function, index, len
297                    )
298                    .into());
299                }
300
301                list.insert(index, item);
302            }
303        }
304
305        Ok(value![void])
306    });
307
308    /** Explicitly extends `extend` to `list`.
309
310    When `index` is provided, the list behind extend is inserted at the given offset,
311    otherwise it is extended to the list's end. */
312    tokay_method!("list_extend : @list, extend, index=void", {
313        // Don't extend void
314        if extend.is_void() {
315            return Ok(value![void]);
316        }
317
318        // In case extend is not a list, make it a list.
319        if !extend.is("list") {
320            extend = RefValue::from(List::from(extend));
321        }
322
323        let mut list = list.borrow_mut();
324
325        // If first parameter is not a list, just do nothing!
326        if let Some(list) = list.object_mut::<List>() {
327            let extend = extend.borrow();
328            let extend = extend.object::<List>().unwrap();
329
330            list.reserve(extend.len());
331
332            if index.is_void() {
333                for item in extend.iter() {
334                    if !item.is_void() {
335                        list.push(item.clone());
336                    }
337                }
338            } else {
339                let mut index = index.to_usize()?;
340                let len = list.len();
341
342                if index > len {
343                    return Err(format!(
344                        "{} provided index {} out of range in list sized {}",
345                        __function, index, len
346                    )
347                    .into());
348                }
349
350                for item in extend.iter() {
351                    if !item.is_void() {
352                        list.insert(index, item.clone());
353                        index += 1;
354                    }
355                }
356            }
357        }
358
359        Ok(value![void])
360    });
361
362    /** Pops item off a list. */
363    tokay_method!("list_pop : @list, index=void", {
364        let index = if index.is_void() {
365            None
366        } else {
367            Some(index.to_usize()?)
368        };
369
370        if !list.is("list") {
371            if index.is_none() {
372                return Ok(list); // "pops" the list, which is not a list
373            }
374
375            return Ok(value![void]);
376        }
377
378        let mut list = list.borrow_mut();
379        let list = list.object_mut::<List>().unwrap();
380
381        // Either pop or remove, regarding index setting.
382        Ok(match index {
383            None => match list.pop() {
384                Some(item) => item,
385                None => value![void],
386            },
387            Some(index) => {
388                let len = list.len();
389
390                if index < len {
391                    list.remove(index)
392                } else {
393                    value![void]
394                }
395            }
396        })
397    });
398
399    tokay_method!("list_sort : @list", {
400        if !list.is("list") {
401            return Ok(Self::list(vec![list], None)?);
402        }
403
404        {
405            let mut list = list.borrow_mut();
406            let list = list.object_mut::<List>().unwrap();
407            list.sort();
408        }
409
410        Ok(list)
411    });
412
413    /** Find `item` in `list` and return its offset.
414
415    In case `item` is not in the list, -1 is returned.
416    */
417    tokay_method!("list_index : @list, item", {
418        let list = list.borrow();
419
420        if let Some(list) = list.object::<List>() {
421            if let Some(index) = list
422                .list
423                .iter()
424                .position(|val| *val.borrow() == *item.borrow())
425            {
426                return Ok(value![index]);
427            }
428        }
429
430        Ok(value![-1])
431    });
432}
433
434impl std::ops::Deref for List {
435    type Target = InnerList;
436
437    fn deref(&self) -> &Self::Target {
438        &self.list
439    }
440}
441
442impl std::ops::DerefMut for List {
443    fn deref_mut(&mut self) -> &mut Self::Target {
444        &mut self.list
445    }
446}
447
448impl std::iter::IntoIterator for List {
449    type Item = RefValue;
450    type IntoIter = std::vec::IntoIter<Self::Item>;
451
452    fn into_iter(self) -> Self::IntoIter {
453        self.list.into_iter()
454    }
455}
456
457impl From<RefValue> for List {
458    fn from(refvalue: RefValue) -> Self {
459        match refvalue.name() {
460            "iter" => {
461                let mut iter = refvalue.borrow_mut();
462                let iter = iter.object_mut::<Iter>().unwrap();
463                let mut list = InnerList::new();
464
465                for item in iter {
466                    list.push(item);
467                }
468
469                Self { list }
470            }
471            "list" => {
472                let list = refvalue.borrow();
473                let list = list.object::<List>().unwrap();
474                (*list).clone()
475            }
476            "void" => Self { list: Vec::new() },
477            _ => Self {
478                list: vec![refvalue.clone()],
479            },
480        }
481    }
482}
483
484impl From<&RefValue> for List {
485    fn from(refvalue: &RefValue) -> Self {
486        List::from(refvalue.clone())
487    }
488}
489
490impl From<List> for RefValue {
491    fn from(value: List) -> Self {
492        RefValue::from(Box::new(value) as BoxedObject)
493    }
494}
495
496impl From<InnerList> for RefValue {
497    fn from(list: InnerList) -> Self {
498        RefValue::from(List { list })
499    }
500}