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 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204
//! Utilities used for working with erlang linked lists.
//!
//! Right now the only supported way to read lists are through the ListIterator.
use crate::wrapper::{list, NIF_TERM};
use crate::{Decoder, Encoder, Env, Error, NifResult, Term};
/// Enables iteration over the items in the list.
///
/// Although this behaves like a standard Rust iterator
/// ([book](https://doc.rust-lang.org/book/iterators.html) /
/// [docs](https://doc.rust-lang.org/std/iter/trait.Iterator.html)), there are a couple of tricky
/// parts to using it.
///
/// Because the iterator is an iterator over `Term`s, you need to decode the terms before you
/// can do anything with them.
///
/// ## Example
/// An easy way to decode all terms in a list, is to use the `.map()` function of the iterator, and
/// decode every entry in the list. This will produce an iterator of `Result`s, and will therefore
/// not be directly usable in the way you might immediately expect.
///
/// For this case, the the `.collect()` function of rust iterators is useful, as it can lift
/// the `Result`s out of the list. (Contains extra type annotations for clarity)
///
/// ```
/// # use rustler::{Term, NifResult};
/// # use rustler::types::list::ListIterator;
/// # fn list_iterator_example(list_term: Term) -> NifResult<Vec<i64>> {
/// let list_iterator: ListIterator = list_term.decode()?;
///
/// let result: NifResult<Vec<i64>> = list_iterator
/// // Produces an iterator of NifResult<i64>
/// .map(|x| x.decode::<i64>())
/// // Lifts each value out of the result. Returns Ok(Vec<i64>) if successful, the first error
/// // Error(Error) on failure.
/// .collect::<NifResult<Vec<i64>>>();
/// # result
/// # }
/// ```
pub struct ListIterator<'a> {
term: Term<'a>,
}
impl<'a> ListIterator<'a> {
fn new(term: Term<'a>) -> Option<Self> {
if term.is_list() {
let iter = ListIterator { term };
Some(iter)
} else {
None
}
}
}
impl<'a> Iterator for ListIterator<'a> {
type Item = Term<'a>;
fn next(&mut self) -> Option<Term<'a>> {
let env = self.term.get_env();
let cell = unsafe { list::get_list_cell(env.as_c_arg(), self.term.as_c_arg()) };
match cell {
Some((head, tail)) => unsafe {
self.term = Term::new(self.term.get_env(), tail);
Some(Term::new(self.term.get_env(), head))
},
None => {
if self.term.is_empty_list() {
// We reached the end of the list, finish the iterator.
None
} else {
panic!("list iterator found improper list")
}
}
}
}
}
impl<'a> Decoder<'a> for ListIterator<'a> {
fn decode(term: Term<'a>) -> NifResult<Self> {
match ListIterator::new(term) {
Some(iter) => Ok(iter),
None => Err(Error::BadArg),
}
}
}
//impl<'a, T> Encoder for Iterator<Item = T> where T: Encoder {
// fn encode<'b>(&self, env: Env<'b>) -> Term<'b> {
// let term_arr: Vec<NIF_TERM> =
// self.map(|x| x.encode(env).as_c_arg()).collect();
// }
//}
impl<T> Encoder for Vec<T>
where
T: Encoder,
{
fn encode<'b>(&self, env: Env<'b>) -> Term<'b> {
self.as_slice().encode(env)
}
}
impl<'a, T> Decoder<'a> for Vec<T>
where
T: Decoder<'a>,
{
fn decode(term: Term<'a>) -> NifResult<Self> {
let iter: ListIterator = term.decode()?;
let res: NifResult<Self> = iter.map(|x| x.decode::<T>()).collect();
res
}
}
impl<T> Encoder for [T]
where
T: Encoder,
{
fn encode<'b>(&self, env: Env<'b>) -> Term<'b> {
let term_array: Vec<NIF_TERM> = self.iter().map(|x| x.encode(env).as_c_arg()).collect();
unsafe { Term::new(env, list::make_list(env.as_c_arg(), &term_array)) }
}
}
impl<'a, T> Encoder for &'a [T]
where
T: Encoder,
{
fn encode<'b>(&self, env: Env<'b>) -> Term<'b> {
let term_array: Vec<NIF_TERM> = self.iter().map(|x| x.encode(env).as_c_arg()).collect();
unsafe { Term::new(env, list::make_list(env.as_c_arg(), &term_array)) }
}
}
/// ## List terms
impl<'a> Term<'a> {
/// Returns a new empty list.
pub fn list_new_empty(env: Env<'a>) -> Term<'a> {
let list: &[u8] = &[];
list.encode(env)
}
/// Returns an iterator over a list term.
/// See documentation for ListIterator for more information.
///
/// Returns None if the term is not a list.
pub fn into_list_iterator(self) -> NifResult<ListIterator<'a>> {
ListIterator::new(self).ok_or(Error::BadArg)
}
/// Returns the length of a list term.
///
/// Returns None if the term is not a list.
///
/// ### Elixir equivalent
/// ```elixir
/// length(self_term)
/// ```
pub fn list_length(self) -> NifResult<usize> {
unsafe { list::get_list_length(self.get_env().as_c_arg(), self.as_c_arg()) }
.ok_or(Error::BadArg)
}
/// Unpacks a single cell at the head of a list term,
/// and returns the result as a tuple of (head, tail).
///
/// Returns None if the term is not a list.
///
/// ### Elixir equivalent
/// ```elixir
/// [head, tail] = self_term
/// {head, tail}
/// ```
pub fn list_get_cell(self) -> NifResult<(Term<'a>, Term<'a>)> {
let env = self.get_env();
unsafe {
list::get_list_cell(env.as_c_arg(), self.as_c_arg())
.map(|(t1, t2)| (Term::new(env, t1), Term::new(env, t2)))
.ok_or(Error::BadArg)
}
}
/// Makes a copy of the self list term and reverses it.
///
/// Returns Err(Error::BadArg) if the term is not a list.
pub fn list_reverse(self) -> NifResult<Term<'a>> {
let env = self.get_env();
unsafe {
list::make_reverse_list(env.as_c_arg(), self.as_c_arg())
.map(|t| Term::new(env, t))
.ok_or(Error::BadArg)
}
}
/// Adds `head` in a list cell with `self` as tail.
pub fn list_prepend(self, head: impl Encoder) -> Term<'a> {
let env = self.get_env();
let head = head.encode(env);
unsafe {
let term = list::make_list_cell(env.as_c_arg(), head.as_c_arg(), self.as_c_arg());
Term::new(env, term)
}
}
}