eclisp 0.1.1

Bindings for Embeddable Common Lisp
use crate::Value;

#[derive(thiserror::Error, Debug, PartialEq, Eq)]
pub enum ListError {
    #[error("Expected a cons cell")]
    NotCons,

    #[error("Improper list")]
    ImproperList,

    #[error("Failed to allocate Lisp root")]
    AllocationFailed,
}

pub struct ListIter<'a, 'rt> {
    start: Option<&'a Value<'rt>>,
    tail: Option<Value<'rt>>,
}

impl<'a, 'rt> ListIter<'a, 'rt> {
    pub(crate) fn new(list: &'a Value<'rt>) -> Self {
        Self {
            start: Some(list),
            tail: None,
        }
    }
}

impl<'a, 'rt> Iterator for ListIter<'a, 'rt> {
    type Item = Result<Value<'rt>, ListError>;

    /// DOES NOT DETECT INFINITE LISTS
    fn next(&mut self) -> Option<Self::Item> {
        let current = match self.tail.as_ref() {
            Some(tail) => tail,
            None => self.start.take()?,
        };

        if current.is_nil() {
            self.tail = None;
            return None;
        }

        if !current.is_cons() {
            self.tail = None;
            return Some(Err(ListError::ImproperList));
        }

        let result = (|| {
            let head = current.car()?;
            let tail = current.cdr()?;
            Ok((head, tail))
        })();

        match result {
            Ok((head, tail)) => {
                self.tail = Some(tail);
                Some(Ok(head))
            }
            Err(error) => {
                self.tail = None;
                Some(Err(error))
            }
        }
    }
}