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