mica 0.7.1

A simple, user-friendly, embeddable scripting language
Documentation
use crate::{builtin_traits::iterator, ll::value::RawValue, Engine, Error, TypeBuilder, UserData};

pub(crate) struct ListIter {
    list: RawValue,
    i: usize,
    len: usize,
}

impl ListIter {
    pub(crate) unsafe fn new(list: RawValue) -> Self {
        let slice = unsafe { list.get_raw_list_unchecked().get().as_slice() };
        ListIter { list, i: 0, len: slice.len() }
    }

    fn has_next(&self) -> bool {
        self.i < self.len
    }

    fn next(&mut self) -> Result<Option<RawValue>, LenChangedDuringIteration> {
        unsafe {
            let list = self.list.get_raw_list_unchecked().get();
            let slice = list.as_slice();
            if slice.len() != self.len {
                return Err(LenChangedDuringIteration { was: self.len, became: slice.len() });
            }
            if let Some(v) = slice.get(self.i) {
                self.i += 1;
                Ok(Some(*v))
            } else {
                Ok(None)
            }
        }
    }
}

impl UserData for ListIter {
    fn visit_references(&self, visit: &mut dyn FnMut(RawValue)) {
        visit(self.list);
    }
}

#[derive(Debug)]
struct LenChangedDuringIteration {
    was: usize,
    became: usize,
}

impl std::fmt::Display for LenChangedDuringIteration {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "list length changed during iteration (was {}, became {})", self.was, self.became)
    }
}

impl std::error::Error for LenChangedDuringIteration {}

pub(crate) fn load_list_iter(engine: &mut Engine) -> Result<(), Error> {
    engine.add_type(
        TypeBuilder::<ListIter>::new("ListIter")
            .add_builtin_trait_function(iterator::HasNext, ListIter::has_next)
            .add_builtin_trait_function(iterator::Next, ListIter::next),
    )?;
    Ok(())
}