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