miniextendr_api/
named_vector.rs1use std::collections::{BTreeMap, HashMap, HashSet};
25
26use crate::ffi::{self, SEXP, SEXPTYPE, SexpExt};
27use crate::from_r::{SexpError, SexpTypeError, TryFromSexp};
28use crate::into_r::IntoR;
29
30pub trait AtomicElement: Sized {
37 fn vec_to_sexp(values: Vec<Self>) -> SEXP;
39
40 fn vec_from_sexp(sexp: SEXP) -> Result<Vec<Self>, SexpError>;
42}
43
44impl AtomicElement for i32 {
47 fn vec_to_sexp(values: Vec<Self>) -> SEXP {
48 values.into_sexp()
49 }
50
51 fn vec_from_sexp(sexp: SEXP) -> Result<Vec<Self>, SexpError> {
52 let actual = sexp.type_of();
53 if actual != SEXPTYPE::INTSXP {
54 return Err(SexpTypeError {
55 expected: SEXPTYPE::INTSXP,
56 actual,
57 }
58 .into());
59 }
60 let slice: &[i32] = TryFromSexp::try_from_sexp(sexp)?;
61 Ok(slice.to_vec())
62 }
63}
64
65impl AtomicElement for f64 {
66 fn vec_to_sexp(values: Vec<Self>) -> SEXP {
67 values.into_sexp()
68 }
69
70 fn vec_from_sexp(sexp: SEXP) -> Result<Vec<Self>, SexpError> {
71 let actual = sexp.type_of();
72 if actual != SEXPTYPE::REALSXP {
73 return Err(SexpTypeError {
74 expected: SEXPTYPE::REALSXP,
75 actual,
76 }
77 .into());
78 }
79 let slice: &[f64] = TryFromSexp::try_from_sexp(sexp)?;
80 Ok(slice.to_vec())
81 }
82}
83
84impl AtomicElement for u8 {
85 fn vec_to_sexp(values: Vec<Self>) -> SEXP {
86 values.into_sexp()
87 }
88
89 fn vec_from_sexp(sexp: SEXP) -> Result<Vec<Self>, SexpError> {
90 let actual = sexp.type_of();
91 if actual != SEXPTYPE::RAWSXP {
92 return Err(SexpTypeError {
93 expected: SEXPTYPE::RAWSXP,
94 actual,
95 }
96 .into());
97 }
98 let slice: &[u8] = TryFromSexp::try_from_sexp(sexp)?;
99 Ok(slice.to_vec())
100 }
101}
102
103impl AtomicElement for bool {
106 fn vec_to_sexp(values: Vec<Self>) -> SEXP {
107 values.into_sexp()
108 }
109
110 fn vec_from_sexp(sexp: SEXP) -> Result<Vec<Self>, SexpError> {
111 <Vec<bool>>::try_from_sexp(sexp)
112 }
113}
114
115impl AtomicElement for String {
118 fn vec_to_sexp(values: Vec<Self>) -> SEXP {
119 values.into_sexp()
120 }
121
122 fn vec_from_sexp(sexp: SEXP) -> Result<Vec<Self>, SexpError> {
123 <Vec<String>>::try_from_sexp(sexp)
124 }
125}
126
127impl AtomicElement for Option<i32> {
130 fn vec_to_sexp(values: Vec<Self>) -> SEXP {
131 values.into_sexp()
132 }
133
134 fn vec_from_sexp(sexp: SEXP) -> Result<Vec<Self>, SexpError> {
135 <Vec<Option<i32>>>::try_from_sexp(sexp)
136 }
137}
138
139impl AtomicElement for Option<f64> {
140 fn vec_to_sexp(values: Vec<Self>) -> SEXP {
141 values.into_sexp()
142 }
143
144 fn vec_from_sexp(sexp: SEXP) -> Result<Vec<Self>, SexpError> {
145 <Vec<Option<f64>>>::try_from_sexp(sexp)
146 }
147}
148
149impl AtomicElement for Option<bool> {
150 fn vec_to_sexp(values: Vec<Self>) -> SEXP {
151 values.into_sexp()
152 }
153
154 fn vec_from_sexp(sexp: SEXP) -> Result<Vec<Self>, SexpError> {
155 <Vec<Option<bool>>>::try_from_sexp(sexp)
156 }
157}
158
159impl AtomicElement for Option<String> {
160 fn vec_to_sexp(values: Vec<Self>) -> SEXP {
161 values.into_sexp()
162 }
163
164 fn vec_from_sexp(sexp: SEXP) -> Result<Vec<Self>, SexpError> {
165 <Vec<Option<String>>>::try_from_sexp(sexp)
166 }
167}
168#[derive(Debug, Clone, PartialEq, Eq)]
191pub struct NamedVector<M>(pub M);
192
193impl<M> NamedVector<M> {
194 pub fn into_inner(self) -> M {
196 self.0
197 }
198}
199
200impl<M> From<M> for NamedVector<M> {
201 fn from(m: M) -> Self {
202 NamedVector(m)
203 }
204}
205pub(crate) unsafe fn set_names_on_sexp<S: AsRef<str>>(sexp: SEXP, keys: &[S]) {
215 unsafe {
216 let n = keys.len();
217 let names = ffi::Rf_allocVector(SEXPTYPE::STRSXP, n as ffi::R_xlen_t);
218 ffi::Rf_protect(names);
219
220 for (i, key) in keys.iter().enumerate() {
221 let s = key.as_ref();
222 let charsxp = SEXP::charsxp(s);
223 names.set_string_elt(i as ffi::R_xlen_t, charsxp);
224 }
225
226 sexp.set_names(names);
227 ffi::Rf_unprotect(1);
228 }
229}
230
231fn extract_names_strict(sexp: SEXP) -> Result<Vec<String>, SexpError> {
235 use crate::from_r::charsxp_to_str;
236
237 let names = sexp.get_names();
238 let len = sexp.len();
239
240 if names.type_of() != SEXPTYPE::STRSXP || names.len() != len {
241 return Err(SexpError::InvalidValue(
242 "NamedVector requires a names attribute on the input vector".to_string(),
243 ));
244 }
245
246 let mut result = Vec::with_capacity(len);
247 let mut seen = HashSet::with_capacity(len);
248
249 for i in 0..len {
250 let charsxp = names.string_elt(i as ffi::R_xlen_t);
251
252 if charsxp == SEXP::na_string() {
254 return Err(SexpError::InvalidValue(
255 "NamedVector does not allow NA names".to_string(),
256 ));
257 }
258
259 let name = unsafe { charsxp_to_str(charsxp) };
260
261 if name.is_empty() {
263 return Err(SexpError::InvalidValue(
264 "NamedVector does not allow empty names".to_string(),
265 ));
266 }
267
268 if !seen.insert(name.to_string()) {
270 return Err(SexpError::DuplicateName(name.to_string()));
271 }
272
273 result.push(name.to_string());
274 }
275
276 Ok(result)
277}
278impl<V: AtomicElement> IntoR for NamedVector<HashMap<String, V>> {
283 type Error = std::convert::Infallible;
284 fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
285 Ok(self.into_sexp())
286 }
287 unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
288 self.try_into_sexp()
289 }
290 fn into_sexp(self) -> SEXP {
291 let (keys, values): (Vec<String>, Vec<V>) = self.0.into_iter().unzip();
292 let sexp = V::vec_to_sexp(values);
293 unsafe {
294 ffi::Rf_protect(sexp);
295 set_names_on_sexp(sexp, &keys);
296 ffi::Rf_unprotect(1);
297 }
298 sexp
299 }
300}
301
302impl<V: AtomicElement> IntoR for NamedVector<BTreeMap<String, V>> {
303 type Error = std::convert::Infallible;
304 fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
305 Ok(self.into_sexp())
306 }
307 unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
308 self.try_into_sexp()
309 }
310 fn into_sexp(self) -> SEXP {
311 let (keys, values): (Vec<String>, Vec<V>) = self.0.into_iter().unzip();
312 let sexp = V::vec_to_sexp(values);
313 unsafe {
314 ffi::Rf_protect(sexp);
315 set_names_on_sexp(sexp, &keys);
316 ffi::Rf_unprotect(1);
317 }
318 sexp
319 }
320}
321impl<V: AtomicElement> TryFromSexp for NamedVector<HashMap<String, V>> {
326 type Error = SexpError;
327
328 fn try_from_sexp(sexp: SEXP) -> Result<Self, Self::Error> {
329 let names = extract_names_strict(sexp)?;
330 let values = V::vec_from_sexp(sexp)?;
331
332 let mut map = HashMap::with_capacity(names.len());
333 for (k, v) in names.into_iter().zip(values) {
334 map.insert(k, v);
335 }
336 Ok(NamedVector(map))
337 }
338}
339
340impl<V: AtomicElement> TryFromSexp for NamedVector<BTreeMap<String, V>> {
341 type Error = SexpError;
342
343 fn try_from_sexp(sexp: SEXP) -> Result<Self, Self::Error> {
344 let names = extract_names_strict(sexp)?;
345 let values = V::vec_from_sexp(sexp)?;
346
347 let mut map = BTreeMap::new();
348 for (k, v) in names.into_iter().zip(values) {
349 map.insert(k, v);
350 }
351 Ok(NamedVector(map))
352 }
353}
354