aorist_extendr_api/wrapper/environment.rs
1use super::*;
2
3#[derive(Debug, PartialEq, Clone)]
4pub struct Environment {
5 pub(crate) robj: Robj,
6}
7
8impl Environment {
9 /// Create a new, empty environment.
10 /// ```
11 /// use extendr_api::prelude::*;
12 /// test! {
13 /// let env = Environment::new_with_parent(global_env());
14 /// assert_eq!(env.len(), 0);
15 /// }
16 /// ```
17 pub fn new_with_parent(parent: Environment) -> Self {
18 // 14 is a reasonable default.
19 Environment::new_with_capacity(parent, 14)
20 }
21
22 /// Create a new, empty environment with a reserved size.
23 ///
24 /// This function will guess the hash table size if required.
25 /// Use the Env{} wrapper for more detail.
26 /// ```
27 /// use extendr_api::prelude::*;
28 /// test! {
29 /// let env = Environment::new_with_capacity(global_env(), 5);
30 /// env.set_local(sym!(a), 1);
31 /// env.set_local(sym!(b), 2);
32 /// assert_eq!(env.len(), 2);
33 /// }
34 /// ```
35 pub fn new_with_capacity(parent: Environment, capacity: usize) -> Self {
36 let robj = if capacity <= 5 {
37 // Unhashed envirnment
38 call!("new.env", FALSE, parent, 0).unwrap()
39 } else {
40 // Hashed environment for larger hashmaps.
41 call!("new.env", TRUE, parent, capacity as i32 * 2 + 1).unwrap()
42 };
43 assert!(robj.is_environment());
44 Self { robj }
45 }
46
47 /// Make an R environment object.
48 /// ```
49 /// use extendr_api::prelude::*;
50 /// use std::convert::TryInto;
51 /// test! {
52 /// let names_and_values = (0..100).map(|i| (format!("n{}", i), i));
53 /// let mut env = Environment::from_pairs(global_env(), names_and_values);
54 /// assert_eq!(env.len(), 100);
55 /// }
56 /// ```
57 #[allow(clippy::wrong_self_convention)]
58 pub fn from_pairs<NV>(parent: Environment, names_and_values: NV) -> Self
59 where
60 NV: IntoIterator,
61 NV::Item: SymPair,
62 {
63 single_threaded(|| {
64 let dict_len = 29;
65 let robj = call!("new.env", TRUE, parent, dict_len).unwrap();
66 for nv in names_and_values {
67 let (n, v) = nv.sym_pair();
68 if let Some(n) = n {
69 unsafe { Rf_defineVar(n.get(), v.get(), robj.get()) }
70 }
71 }
72 Environment { robj }
73 })
74 }
75
76 /// Get the enclosing (parent) environment.
77 pub fn parent(&self) -> Option<Environment> {
78 unsafe {
79 let sexp = self.robj.get();
80 let robj = new_owned(ENCLOS(sexp));
81 robj.try_into().ok()
82 }
83 }
84
85 /// Set the enclosing (parent) environment.
86 pub fn set_parent(&mut self, parent: Environment) -> &mut Self {
87 single_threaded(|| unsafe {
88 let sexp = self.robj.get();
89 SET_ENCLOS(sexp, parent.robj.get());
90 });
91 self
92 }
93
94 /// Get the environment flags.
95 pub fn envflags(&self) -> i32 {
96 unsafe {
97 let sexp = self.robj.get();
98 ENVFLAGS(sexp) as i32
99 }
100 }
101
102 /// Set the environment flags.
103 pub fn set_envflags(&mut self, flags: i32) -> &mut Self {
104 unsafe {
105 let sexp = self.robj.get();
106 SET_ENVFLAGS(sexp, flags)
107 }
108 self
109 }
110
111 /// Iterate over an environment.
112 pub fn iter(&self) -> EnvIter {
113 unsafe {
114 let hashtab = new_owned(HASHTAB(self.get()));
115 let frame = new_owned(FRAME(self.get()));
116 if hashtab.is_null() && frame.is_pairlist() {
117 EnvIter {
118 hash_table: ListIter::new(),
119 pairlist: frame.as_pairlist().unwrap().iter(),
120 }
121 } else {
122 EnvIter {
123 hash_table: hashtab.as_list().unwrap().values(),
124 pairlist: PairlistIter::new(),
125 }
126 }
127 }
128 }
129
130 /// Get the names in an environment.
131 /// ```
132 /// use extendr_api::prelude::*;
133 /// test! {
134 /// let names_and_values : std::collections::HashMap<_, _> = (0..4).map(|i| (format!("n{}", i), r!(i))).collect();
135 /// let env = Environment::from_pairs(global_env(), names_and_values);
136 /// assert_eq!(env.names().collect::<Vec<_>>(), vec!["n0", "n1", "n2", "n3"]);
137 /// }
138 /// ```
139 pub fn names(&self) -> impl Iterator<Item = &str> {
140 self.iter().map(|(k, _)| k)
141 }
142
143 /// Set or define a variable in an environment.
144 /// ```
145 /// use extendr_api::prelude::*;
146 /// test! {
147 /// let env = Environment::new_with_parent(global_env());
148 /// env.set_local(sym!(x), "harry");
149 /// env.set_local(sym!(x), "fred");
150 /// assert_eq!(env.local(sym!(x)), Ok(r!("fred")));
151 /// }
152 /// ```
153 pub fn set_local<K: Into<Robj>, V: Into<Robj>>(&self, key: K, value: V) {
154 let key = key.into();
155 let value = value.into();
156 if key.is_symbol() {
157 single_threaded(|| unsafe {
158 Rf_defineVar(key.get(), value.get(), self.get());
159 })
160 }
161 }
162
163 /// Get a variable from an environment, but not its ancestors.
164 /// ```
165 /// use extendr_api::prelude::*;
166 /// test! {
167 /// let env = Environment::new_with_parent(global_env());
168 /// env.set_local(sym!(x), "fred");
169 /// assert_eq!(env.local(sym!(x)), Ok(r!("fred")));
170 /// }
171 /// ```
172 pub fn local<K: Into<Robj>>(&self, key: K) -> Result<Robj> {
173 let key = key.into();
174 if key.is_symbol() {
175 unsafe { Ok(new_owned(Rf_findVarInFrame3(self.get(), key.get(), 1))) }
176 } else {
177 Err(Error::NotFound(key))
178 }
179 }
180}
181
182/// Iterator over the names and values of an environment
183///
184/// ```
185/// use extendr_api::prelude::*;
186/// test! {
187/// let names_and_values = (0..100).map(|i| (format!("n{}", i), i));
188/// let env = Environment::from_pairs(global_env(), names_and_values);
189/// let robj = r!(env);
190/// let names_and_values = robj.as_environment().unwrap().iter().collect::<Vec<_>>();
191/// assert_eq!(names_and_values.len(), 100);
192///
193/// let small_env = Environment::new_with_capacity(global_env(), 1);
194/// small_env.set_local(sym!(x), 1);
195/// let names_and_values = small_env.as_environment().unwrap().iter().collect::<Vec<_>>();
196/// assert_eq!(names_and_values, vec![("x", r!(1))]);
197///
198/// let large_env = Environment::new_with_capacity(global_env(), 1000);
199/// large_env.set_local(sym!(x), 1);
200/// let names_and_values = large_env.as_environment().unwrap().iter().collect::<Vec<_>>();
201/// assert_eq!(names_and_values, vec![("x", r!(1))]);
202/// }
203///
204/// ```
205#[derive(Clone)]
206pub struct EnvIter {
207 hash_table: ListIter,
208 pairlist: PairlistIter,
209}
210
211impl Iterator for EnvIter {
212 type Item = (&'static str, Robj);
213
214 fn next(&mut self) -> Option<Self::Item> {
215 loop {
216 // Environments are a hash table (list) or pair lists (pairlist)
217 // Get the first available value from the pair list.
218 for (key, value) in &mut self.pairlist {
219 // if the key and value are valid, return a pair.
220 if !key.is_na() && !value.is_unbound_value() {
221 return Some((key, value));
222 }
223 }
224
225 // Get the first pairlist from the hash table.
226 loop {
227 if let Some(obj) = self.hash_table.next() {
228 if !obj.is_null() && obj.is_pairlist() {
229 self.pairlist = obj.as_pairlist().unwrap().iter();
230 break;
231 }
232 // continue hash table loop.
233 } else {
234 // The hash table is empty, end of iteration.
235 return None;
236 }
237 }
238 }
239 }
240}