use std::ffi::c_uint;
use crate::codec::{CodecError, Decoder, Encoder};
use crate::types::{AnyTerm, Env, Term, Tuple};
macro_rules! tuple_codec {
($($T:ident),+) => {
impl<'id, $($T: Encoder<'id>),+> Encoder<'id> for ($($T,)+) {
fn encode(&self, env: impl Env<'id>) -> Result<AnyTerm<'id>, CodecError> {
#[allow(non_snake_case)]
let ($($T,)+) = self;
let raw = [$( $T.encode(env)?.raw_term() ),+];
let term = unsafe {
enif_ffi::make_tuple_from_array(env.raw_env(), raw.as_ptr(), raw.len() as c_uint)
};
Ok(AnyTerm::wrap(term, env))
}
}
impl<'id, $($T: Decoder<'id>),+> Decoder<'id> for ($($T,)+) {
fn decode(term: AnyTerm<'id>, env: impl Env<'id>) -> Result<Self, CodecError> {
const ARITY: usize = [$(stringify!($T)),+].len();
let view = Tuple::decode(term, env)?.with_elements(env);
if view.len() != ARITY {
return Err(CodecError::WrongArity);
}
let mut index = 0;
#[allow(unused_assignments)]
let result = ($(
{
let element = $T::decode(view[index], env)?;
index += 1;
element
},
)+);
Ok(result)
}
}
};
}
tuple_codec!(A);
tuple_codec!(A, B);
tuple_codec!(A, B, C);
tuple_codec!(A, B, C, D);
tuple_codec!(A, B, C, D, E);
tuple_codec!(A, B, C, D, E, F);
tuple_codec!(A, B, C, D, E, F, G);
tuple_codec!(A, B, C, D, E, F, G, H);
tuple_codec!(A, B, C, D, E, F, G, H, I);
tuple_codec!(A, B, C, D, E, F, G, H, I, J);
tuple_codec!(A, B, C, D, E, F, G, H, I, J, K);
tuple_codec!(A, B, C, D, E, F, G, H, I, J, K, L);