extendr_api/wrapper/pairlist.rs
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 205 206 207 208 209 210 211 212 213 214 215
//! A pairlist is a linked list of values with optional symbol tags.
use super::*;
#[derive(PartialEq, Clone)]
pub struct Pairlist {
pub(crate) robj: Robj,
}
impl Pairlist {
pub fn new() -> Self {
let robj = Robj::from(());
Self { robj }
}
/// Convert an iterator of names and values to a pairlist object.
/// ```
/// use extendr_api::prelude::*;
/// test! {
/// let pairs = (0..100).map(|i| (format!("n{}", i), i));
/// let pairlist = Pairlist::from_pairs(pairs);
/// assert_eq!(pairlist.len(), 100);
///
/// // Use "" to indicate the absense of the name
/// let unnamed_pairlist = Pairlist::from_pairs([("", "a"), ("", "b")]);
/// assert_eq!(call!("names", unnamed_pairlist)?, r!(NULL));
/// let unnamed_pairlist_r = R!(r#"pairlist("a", "b")"#)?.as_pairlist().unwrap();
/// assert_eq!(unnamed_pairlist_r.names().collect::<Vec<_>>(), vec!["", ""]);
/// }
/// ```
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: Robj::from_sexp(res),
};
Rf_unprotect(num_protects);
res
})
}
/// Generate paits of names and values.
/// ```
/// use extendr_api::prelude::*;
/// test! {
/// let pairs = (0..100).map(|i| (format!("n{}", i), i));
/// let pairlist = Pairlist::from_pairs(pairs);
/// assert_eq!(pairlist.iter().count(), 100);
/// assert_eq!(pairlist.iter().nth(50), Some(("n50", r!(50))));
/// }
/// ```
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)
}
}
impl Default for wrapper::pairlist::Pairlist {
fn default() -> Self {
Self::new()
}
}
/// Generate paits of names and values.
/// ```
/// use extendr_api::prelude::*;
/// test! {
/// let pairs = (0..100).map(|i| (format!("n{}", i), i));
/// let pairlist = Pairlist::from_pairs(pairs);
/// assert_eq!(pairlist.iter().count(), 100);
/// assert_eq!(pairlist.iter().nth(50), Some(("n50", r!(50))));
/// }
/// ```
#[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 {
/// Make an empty pairlist iterator.
pub fn new() -> Self {
unsafe {
Self {
robj: ().into(),
list_elem: R_NilValue,
}
}
}
}
impl Iterator for PairlistIter {
// Note: The static is bad here, but we await RFC 1598
// to do this properly. Howevere, symbols live forever.
// https://github.com/rust-lang/rfcs/blob/master/text/1598-generic_associated_types.md
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 = Robj::from_sexp(CAR(sexp));
self.list_elem = CDR(sexp);
if TYPEOF(tag) == SEXPTYPE::SYMSXP {
// printname is always a CHARSXP
let printname = PRINTNAME(tag);
rstr::charsxp_to_str(printname).map(|x| (x, value))
} else {
// empty string represents the absense of the name
Some(("", value))
}
}
}
}
}
impl IntoIterator for Pairlist {
type IntoIter = PairlistIter;
type Item = (&'static str, Robj);
/// Convert a PairList into an interator, consuming the pairlist.
/// ```
/// use extendr_api::prelude::*;
/// test! {
/// let pairlist = pairlist!(a=1, 2).as_pairlist().unwrap();
/// let vec : Vec<_> = pairlist.into_iter().collect();
/// assert_eq!(vec, vec![("a", r!(1)), ("", r!(2))]);
/// }
/// ```
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;
/// You can pass a PairlistIter to a function.
fn try_from(robj: &Robj) -> Result<Self> {
let pairlist: Pairlist = robj.try_into()?;
Ok(pairlist.into_iter())
}
}
impl From<PairlistIter> for Robj {
/// You can return a PairlistIter from a function.
fn from(iter: PairlistIter) -> Self {
iter.robj
}
}
impl From<()> for Pairlist {
/// Construct a NULL pairlist (which is a NULL).
fn from(_: ()) -> Self {
Pairlist {
robj: Robj::from(()),
}
}
}
impl std::fmt::Debug for Pairlist {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"pairlist!({})",
self.iter()
.map(|(k, v)| format!("{}={:?}", k, v))
.collect::<Vec<_>>()
.join(", ")
)?;
Ok(())
}
}