use core::marker::PhantomData;
use std::ffi::{c_int, c_uint};
use crate::types::sealed::Sealed;
use crate::types::{AnyTerm, Env, Invariant, RawTerm, Term};
#[derive(Clone, Copy)]
pub struct Tuple<'id> {
raw_term: RawTerm,
_id: Invariant<'id>,
}
impl<'id> Tuple<'id> {
#[crate::raw]
pub(crate) fn from_raw(raw_term: RawTerm) -> Self {
Self { raw_term, _id: PhantomData }
}
pub fn with_elements(self, env: impl Env<'id>) -> TupleView<'id> {
let mut arity: c_int = 0;
let mut array: *const RawTerm = std::ptr::null();
let ok = unsafe { enif_ffi::get_tuple(env.raw_env(), self.raw_term, &mut arity, &mut array) };
assert!(ok != 0, "enif_get_tuple failed on a validated Tuple");
let raw_elements = if arity == 0 {
&[][..]
} else {
unsafe { std::slice::from_raw_parts(array, arity as usize) }
};
TupleView { raw_term: self.raw_term, raw_elements, _id: PhantomData }
}
pub fn from_terms<I, T>(env: impl Env<'id>, terms: I) -> Tuple<'id>
where
I: IntoIterator<Item = T>,
T: Term<'id>,
{
let raw: Vec<RawTerm> = terms.into_iter().map(|t| t.raw_term()).collect();
let raw_term = unsafe {
enif_ffi::make_tuple_from_array(env.raw_env(), raw.as_ptr(), raw.len() as c_uint)
};
Tuple { raw_term, _id: PhantomData }
}
pub fn is_tuple(env: impl Env<'id>, term: impl Term<'id>) -> bool {
unsafe { enif_ffi::is_tuple(env.raw_env(), term.raw_term()) != 0 }
}
}
impl PartialEq for Tuple<'_> {
fn eq(&self, other: &Self) -> bool {
unsafe { enif_ffi::is_identical(self.raw_term, other.raw_term) != 0 }
}
}
impl Eq for Tuple<'_> {}
impl PartialOrd for Tuple<'_> {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Tuple<'_> {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
let c = unsafe { enif_ffi::compare(self.raw_term, other.raw_term) };
c.cmp(&0)
}
}
impl std::fmt::Debug for Tuple<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Tuple")
}
}
impl<'id> Sealed for Tuple<'id> {}
impl<'id> Term<'id> for Tuple<'id> {
fn raw_term(self) -> RawTerm {
self.raw_term
}
}
#[derive(Clone, Copy)]
pub struct TupleView<'id> {
raw_term: RawTerm,
raw_elements: &'id [RawTerm],
_id: Invariant<'id>,
}
impl<'id> TupleView<'id> {
fn elements(self) -> &'id [AnyTerm<'id>] {
unsafe { std::mem::transmute::<&'id [RawTerm], &'id [AnyTerm<'id>]>(self.raw_elements) }
}
pub fn len(self) -> usize {
self.raw_elements.len()
}
pub fn is_empty(self) -> bool {
self.raw_elements.is_empty()
}
}
impl<'id> std::ops::Index<usize> for TupleView<'id> {
type Output = AnyTerm<'id>;
fn index(&self, i: usize) -> &AnyTerm<'id> {
&self.elements()[i]
}
}
impl<'id> IntoIterator for TupleView<'id> {
type Item = AnyTerm<'id>;
type IntoIter = std::iter::Copied<std::slice::Iter<'id, AnyTerm<'id>>>;
fn into_iter(self) -> Self::IntoIter {
self.elements().iter().copied()
}
}
impl std::fmt::Debug for TupleView<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "TupleView")
}
}
impl<'id> Sealed for TupleView<'id> {}
impl<'id> Term<'id> for TupleView<'id> {
fn raw_term(self) -> RawTerm {
self.raw_term
}
}