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 ffi::Rf_translateCharUTF8;
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 c_str = unsafe { Rf_translateCharUTF8(charsxp) };
260 if c_str.is_null() {
261 return Err(SexpError::InvalidValue(
262 "NamedVector does not allow NA names".to_string(),
263 ));
264 }
265
266 let name = unsafe { std::ffi::CStr::from_ptr(c_str) }
267 .to_str()
268 .unwrap_or("");
269
270 if name.is_empty() {
272 return Err(SexpError::InvalidValue(
273 "NamedVector does not allow empty names".to_string(),
274 ));
275 }
276
277 if !seen.insert(name.to_string()) {
279 return Err(SexpError::DuplicateName(name.to_string()));
280 }
281
282 result.push(name.to_string());
283 }
284
285 Ok(result)
286}
287impl<V: AtomicElement> IntoR for NamedVector<HashMap<String, V>> {
292 type Error = std::convert::Infallible;
293 fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
294 Ok(self.into_sexp())
295 }
296 unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
297 self.try_into_sexp()
298 }
299 fn into_sexp(self) -> SEXP {
300 let (keys, values): (Vec<String>, Vec<V>) = self.0.into_iter().unzip();
301 let sexp = V::vec_to_sexp(values);
302 unsafe {
303 ffi::Rf_protect(sexp);
304 set_names_on_sexp(sexp, &keys);
305 ffi::Rf_unprotect(1);
306 }
307 sexp
308 }
309}
310
311impl<V: AtomicElement> IntoR for NamedVector<BTreeMap<String, V>> {
312 type Error = std::convert::Infallible;
313 fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
314 Ok(self.into_sexp())
315 }
316 unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
317 self.try_into_sexp()
318 }
319 fn into_sexp(self) -> SEXP {
320 let (keys, values): (Vec<String>, Vec<V>) = self.0.into_iter().unzip();
321 let sexp = V::vec_to_sexp(values);
322 unsafe {
323 ffi::Rf_protect(sexp);
324 set_names_on_sexp(sexp, &keys);
325 ffi::Rf_unprotect(1);
326 }
327 sexp
328 }
329}
330impl<V: AtomicElement> TryFromSexp for NamedVector<HashMap<String, V>> {
335 type Error = SexpError;
336
337 fn try_from_sexp(sexp: SEXP) -> Result<Self, Self::Error> {
338 let names = extract_names_strict(sexp)?;
339 let values = V::vec_from_sexp(sexp)?;
340
341 let mut map = HashMap::with_capacity(names.len());
342 for (k, v) in names.into_iter().zip(values) {
343 map.insert(k, v);
344 }
345 Ok(NamedVector(map))
346 }
347}
348
349impl<V: AtomicElement> TryFromSexp for NamedVector<BTreeMap<String, V>> {
350 type Error = SexpError;
351
352 fn try_from_sexp(sexp: SEXP) -> Result<Self, Self::Error> {
353 let names = extract_names_strict(sexp)?;
354 let values = V::vec_from_sexp(sexp)?;
355
356 let mut map = BTreeMap::new();
357 for (k, v) in names.into_iter().zip(values) {
358 map.insert(k, v);
359 }
360 Ok(NamedVector(map))
361 }
362}
363