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}