1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
use crate::{
	sys::{
		enif_get_list_cell, enif_get_list_length, enif_make_list_from_array, enif_make_string_len,
		ErlNifCharEncoding,
	},
	Env, Error, Result, Term,
};
use std::{
	mem::MaybeUninit,
	os::raw::{c_char, c_uint},
};

#[derive(Clone, Copy)]
pub struct List<'a>(Term<'a>);

impl<'a> List<'a> {
	pub(crate) fn from_term(term: Term) -> List {
		List(term)
	}

	pub fn term(&self) -> Term<'a> {
		self.0
	}

	pub fn env(&self) -> Env<'a> {
		self.0.env()
	}

	pub fn new(
		env: Env<'a>,
		terms: impl IntoIterator<Item = impl Into<Term<'a>>>,
	) -> Result<List<'a>> {
		let terms = terms
			.into_iter()
			.map(|term| term.into().raw())
			.collect::<Vec<_>>();
		let term =
			unsafe { enif_make_list_from_array(env.raw(), terms.as_ptr(), terms.len() as c_uint) };
		let term = Term::from_raw(env, term);
		let list = List(term);
		Ok(list)
	}

	pub fn from_string(env: Env<'a>, string: &str) -> Result<List<'a>> {
		let term = unsafe {
			enif_make_string_len(
				env.raw(),
				string.as_ptr() as *const c_char,
				string.len(),
				ErlNifCharEncoding::ERL_NIF_LATIN1,
			)
		};
		Ok(List(Term::from_raw(env, term)))
	}

	pub fn size(&self) -> Result<usize> {
		let len = unsafe {
			let mut len = MaybeUninit::uninit();
			let success =
				enif_get_list_length(self.env().raw(), self.term().raw(), len.as_mut_ptr());
			if success == 0 {
				return Err(Error::message("failed to get list length"));
			}
			len.assume_init()
		};
		Ok(len as usize)
	}

	pub fn iter(&self) -> ListIterator<'a> {
		ListIterator::new(*self)
	}
}

pub struct ListIterator<'a> {
	term: Term<'a>,
}

impl<'a> ListIterator<'a> {
	fn new(list: List<'a>) -> ListIterator<'a> {
		ListIterator { term: list.term() }
	}
}

impl<'a> Iterator for ListIterator<'a> {
	type Item = Term<'a>;

	fn next(&mut self) -> Option<Self::Item> {
		let cell = unsafe {
			let mut head = MaybeUninit::uninit();
			let mut tail = MaybeUninit::uninit();
			let success = enif_get_list_cell(
				self.term.env.raw(),
				self.term.raw(),
				head.as_mut_ptr(),
				tail.as_mut_ptr(),
			);
			if success == 0 {
				None
			} else {
				Some((head.assume_init(), tail.assume_init()))
			}
		};
		match cell {
			Some((head, tail)) => {
				self.term = Term::from_raw(self.term.env, tail);
				Some(Term::from_raw(self.term.env, head))
			}
			None => None,
		}
	}
}