use super::*;
#[derive(Debug, PartialEq, Clone)]
pub struct Pairlist {
pub(crate) robj: Robj,
}
impl Pairlist {
pub fn from_pairs<NV>(pairs: NV) -> Self
where
NV: IntoIterator,
NV::IntoIter: DoubleEndedIterator,
NV::Item: SymPair,
{
crate::single_threaded(|| unsafe {
let mut num_protects = 0;
let mut res = R_NilValue;
for nv in pairs.into_iter().rev() {
let (name, val) = nv.sym_pair();
let val = Rf_protect(val.get());
res = Rf_protect(Rf_cons(val, res));
num_protects += 2;
if let Some(name) = name {
SET_TAG(res, name.get());
}
}
let res = Pairlist {
robj: new_owned(res),
};
Rf_unprotect(num_protects as i32);
res
})
}
pub fn iter(&self) -> PairlistIter {
unsafe {
PairlistIter {
robj: self.robj.clone(),
list_elem: self.robj.get(),
}
}
}
pub fn names(&self) -> impl Iterator<Item = &'static str> {
self.iter().map(|(tag, _)| tag)
}
pub fn values(&self) -> impl Iterator<Item = Robj> {
self.iter().map(|(_, robj)| robj)
}
}
#[derive(Clone)]
pub struct PairlistIter {
pub(crate) robj: Robj,
pub(crate) list_elem: SEXP,
}
impl Default for PairlistIter {
fn default() -> Self {
PairlistIter::new()
}
}
impl PairlistIter {
pub fn new() -> Self {
unsafe {
Self {
robj: ().into(),
list_elem: R_NilValue,
}
}
}
}
impl Iterator for PairlistIter {
type Item = (&'static str, Robj);
fn next(&mut self) -> Option<Self::Item> {
unsafe {
let sexp = self.list_elem;
if sexp == R_NilValue {
None
} else {
let tag = TAG(sexp);
let value = new_owned(CAR(sexp));
self.list_elem = CDR(sexp);
if TYPEOF(tag) == SYMSXP as i32 {
let printname = PRINTNAME(tag);
assert!(TYPEOF(printname) as u32 == CHARSXP);
let name = to_str(R_CHAR(printname) as *const u8);
Some((std::mem::transmute(name), value))
} else {
Some(("", value))
}
}
}
}
}
impl IntoIterator for Pairlist {
type IntoIter = PairlistIter;
type Item = (&'static str, Robj);
fn into_iter(self) -> Self::IntoIter {
unsafe {
let sexp = self.robj.get();
PairlistIter {
robj: self.robj,
list_elem: sexp,
}
}
}
}
impl TryFrom<Robj> for PairlistIter {
type Error = Error;
fn try_from(robj: Robj) -> Result<Self> {
let pairlist: Pairlist = robj.try_into()?;
Ok(pairlist.into_iter())
}
}
impl From<PairlistIter> for Robj {
fn from(iter: PairlistIter) -> Self {
iter.robj
}
}
impl From<()> for Pairlist {
fn from(_: ()) -> Self {
Pairlist {
robj: Robj::from(()),
}
}
}