extendr_api/wrapper/pairlist.rs
1//! A pairlist is a linked list of values with optional symbol tags.
2
3use super::*;
4use extendr_ffi::{
5 R_NilValue, Rf_cons, Rf_protect, Rf_unprotect, CAR, CDR, PRINTNAME, SET_TAG, TAG, TYPEOF,
6};
7
8#[derive(PartialEq, Clone)]
9pub struct Pairlist {
10 pub(crate) robj: Robj,
11}
12
13impl Pairlist {
14 pub fn new() -> Self {
15 let robj = Robj::from(());
16 Self { robj }
17 }
18
19 /// Convert an iterator of names and values to a pairlist object.
20 /// ```
21 /// use extendr_api::prelude::*;
22 /// test! {
23 /// let pairs = (0..100).map(|i| (format!("n{}", i), i));
24 /// let pairlist = Pairlist::from_pairs(pairs);
25 /// assert_eq!(pairlist.len(), 100);
26 ///
27 /// // Use "" to indicate the absense of the name
28 /// let unnamed_pairlist = Pairlist::from_pairs([("", "a"), ("", "b")]);
29 /// assert_eq!(call!("names", unnamed_pairlist)?, r!(NULL));
30 /// let unnamed_pairlist_r = R!(r#"pairlist("a", "b")"#)?.as_pairlist().unwrap();
31 /// assert_eq!(unnamed_pairlist_r.names().collect::<Vec<_>>(), vec!["", ""]);
32 /// }
33 /// ```
34 pub fn from_pairs<NV>(pairs: NV) -> Self
35 where
36 NV: IntoIterator,
37 NV::IntoIter: DoubleEndedIterator,
38 NV::Item: SymPair,
39 {
40 crate::single_threaded(|| unsafe {
41 let mut num_protects = 0;
42 let mut res = R_NilValue;
43 for nv in pairs.into_iter().rev() {
44 let (name, val) = nv.sym_pair();
45 let val = Rf_protect(val.get());
46 res = Rf_protect(Rf_cons(val, res));
47 num_protects += 2;
48 if let Some(name) = name {
49 SET_TAG(res, name.get());
50 }
51 }
52 let res = Pairlist {
53 robj: Robj::from_sexp(res),
54 };
55 Rf_unprotect(num_protects);
56 res
57 })
58 }
59
60 /// Generate paits of names and values.
61 /// ```
62 /// use extendr_api::prelude::*;
63 /// test! {
64 /// let pairs = (0..100).map(|i| (format!("n{}", i), i));
65 /// let pairlist = Pairlist::from_pairs(pairs);
66 /// assert_eq!(pairlist.iter().count(), 100);
67 /// assert_eq!(pairlist.iter().nth(50), Some(("n50", r!(50))));
68 /// }
69 /// ```
70 pub fn iter(&self) -> PairlistIter {
71 unsafe {
72 PairlistIter {
73 robj: self.robj.clone(),
74 list_elem: self.robj.get(),
75 }
76 }
77 }
78
79 pub fn names(&self) -> impl Iterator<Item = &'static str> {
80 self.iter().map(|(tag, _)| tag)
81 }
82
83 pub fn values(&self) -> impl Iterator<Item = Robj> {
84 self.iter().map(|(_, robj)| robj)
85 }
86}
87
88impl Default for wrapper::pairlist::Pairlist {
89 fn default() -> Self {
90 Self::new()
91 }
92}
93
94/// Generate paits of names and values.
95/// ```
96/// use extendr_api::prelude::*;
97/// test! {
98/// let pairs = (0..100).map(|i| (format!("n{}", i), i));
99/// let pairlist = Pairlist::from_pairs(pairs);
100/// assert_eq!(pairlist.iter().count(), 100);
101/// assert_eq!(pairlist.iter().nth(50), Some(("n50", r!(50))));
102/// }
103/// ```
104#[derive(Clone)]
105pub struct PairlistIter {
106 pub(crate) robj: Robj,
107 pub(crate) list_elem: SEXP,
108}
109
110impl Default for PairlistIter {
111 fn default() -> Self {
112 PairlistIter::new()
113 }
114}
115
116impl PairlistIter {
117 /// Make an empty pairlist iterator.
118 pub fn new() -> Self {
119 unsafe {
120 Self {
121 robj: ().into(),
122 list_elem: R_NilValue,
123 }
124 }
125 }
126}
127
128impl Iterator for PairlistIter {
129 // Note: The static is bad here, but we await RFC 1598
130 // to do this properly. Howevere, symbols live forever.
131 // https://github.com/rust-lang/rfcs/blob/master/text/1598-generic_associated_types.md
132 type Item = (&'static str, Robj);
133
134 fn next(&mut self) -> Option<Self::Item> {
135 unsafe {
136 let sexp = self.list_elem;
137 if sexp == R_NilValue {
138 None
139 } else {
140 let tag = TAG(sexp);
141 let value = Robj::from_sexp(CAR(sexp));
142 self.list_elem = CDR(sexp);
143 if TYPEOF(tag) == SEXPTYPE::SYMSXP {
144 // printname is always a CHARSXP
145 let printname = PRINTNAME(tag);
146 rstr::charsxp_to_str(printname).map(|x| (x, value))
147 } else {
148 // empty string represents the absense of the name
149 Some(("", value))
150 }
151 }
152 }
153 }
154}
155
156impl IntoIterator for Pairlist {
157 type IntoIter = PairlistIter;
158 type Item = (&'static str, Robj);
159
160 /// Convert a PairList into an interator, consuming the pairlist.
161 /// ```
162 /// use extendr_api::prelude::*;
163 /// test! {
164 /// let pairlist = pairlist!(a=1, 2).as_pairlist().unwrap();
165 /// let vec : Vec<_> = pairlist.into_iter().collect();
166 /// assert_eq!(vec, vec![("a", r!(1)), ("", r!(2))]);
167 /// }
168 /// ```
169 fn into_iter(self) -> Self::IntoIter {
170 unsafe {
171 let sexp = self.robj.get();
172 PairlistIter {
173 robj: self.robj,
174 list_elem: sexp,
175 }
176 }
177 }
178}
179
180impl TryFrom<&Robj> for PairlistIter {
181 type Error = Error;
182
183 /// You can pass a PairlistIter to a function.
184 fn try_from(robj: &Robj) -> Result<Self> {
185 let pairlist: Pairlist = robj.try_into()?;
186 Ok(pairlist.into_iter())
187 }
188}
189
190impl From<PairlistIter> for Robj {
191 /// You can return a PairlistIter from a function.
192 fn from(iter: PairlistIter) -> Self {
193 iter.robj
194 }
195}
196
197impl From<()> for Pairlist {
198 /// Construct a NULL pairlist (which is a NULL).
199 fn from(_: ()) -> Self {
200 Pairlist {
201 robj: Robj::from(()),
202 }
203 }
204}
205
206impl std::fmt::Debug for Pairlist {
207 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
208 write!(
209 f,
210 "pairlist!({})",
211 self.iter()
212 .map(|(k, v)| format!("{}={:?}", k, v))
213 .collect::<Vec<_>>()
214 .join(", ")
215 )?;
216 Ok(())
217 }
218}