Skip to main content

miniextendr_api/into_r/
collections.rs

1//! Collection conversions (HashMap, BTreeMap, HashSet, BTreeSet) to R.
2//!
3//! - `HashMap<String, V>` / `BTreeMap<String, V>` → named R list
4//! - `HashSet<T>` / `BTreeSet<T>` → unnamed R vector (via Vec intermediary)
5
6use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
7use std::hash::Hash;
8
9use crate::ffi::SexpExt;
10use crate::into_r::{IntoR, str_to_charsxp, str_to_charsxp_unchecked};
11
12macro_rules! impl_map_into_r {
13    ($(#[$meta:meta])* $map_ty:ident) => {
14        $(#[$meta])*
15        impl<V: IntoR> IntoR for $map_ty<String, V> {
16            type Error = crate::into_r_error::IntoRError;
17            fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
18                Ok(self.into_sexp())
19            }
20            unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
21                Ok(unsafe { self.into_sexp_unchecked() })
22            }
23            fn into_sexp(self) -> crate::ffi::SEXP {
24                map_to_named_list(self.into_iter())
25            }
26            unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
27                unsafe { map_to_named_list_unchecked(self.into_iter()) }
28            }
29        }
30    };
31}
32
33impl_map_into_r!(
34    /// Convert HashMap<String, V> to R named list (VECSXP).
35    HashMap
36);
37impl_map_into_r!(
38    /// Convert BTreeMap<String, V> to R named list (VECSXP).
39    BTreeMap
40);
41
42/// Helper to convert an iterator of (String, V) pairs to a named R list.
43fn map_to_named_list<V: IntoR>(
44    iter: impl ExactSizeIterator<Item = (String, V)>,
45) -> crate::ffi::SEXP {
46    unsafe {
47        let n: crate::ffi::R_xlen_t = iter
48            .len()
49            .try_into()
50            .expect("map length exceeds isize::MAX");
51        let list = crate::ffi::Rf_allocVector(crate::ffi::SEXPTYPE::VECSXP, n);
52        crate::ffi::Rf_protect(list);
53
54        // Allocate names vector
55        let names = crate::ffi::Rf_allocVector(crate::ffi::SEXPTYPE::STRSXP, n);
56        crate::ffi::Rf_protect(names);
57
58        for (i, (key, value)) in iter.enumerate() {
59            let idx: crate::ffi::R_xlen_t = i.try_into().expect("index exceeds isize::MAX");
60            // Set list element
61            list.set_vector_elt(idx, value.into_sexp());
62
63            // Set name
64            let charsxp = str_to_charsxp(&key);
65            names.set_string_elt(idx, charsxp);
66        }
67
68        // Attach names attribute
69        list.set_names(names);
70
71        crate::ffi::Rf_unprotect(2);
72        list
73    }
74}
75
76/// Unchecked version of [`map_to_named_list`].
77unsafe fn map_to_named_list_unchecked<V: IntoR>(
78    iter: impl ExactSizeIterator<Item = (String, V)>,
79) -> crate::ffi::SEXP {
80    unsafe {
81        let n: crate::ffi::R_xlen_t = iter
82            .len()
83            .try_into()
84            .expect("map length exceeds isize::MAX");
85        let list = crate::ffi::Rf_allocVector_unchecked(crate::ffi::SEXPTYPE::VECSXP, n);
86        crate::ffi::Rf_protect(list);
87
88        let names = crate::ffi::Rf_allocVector_unchecked(crate::ffi::SEXPTYPE::STRSXP, n);
89        crate::ffi::Rf_protect(names);
90
91        for (i, (key, value)) in iter.enumerate() {
92            let idx: crate::ffi::R_xlen_t = i.try_into().expect("index exceeds isize::MAX");
93            list.set_vector_elt_unchecked(idx, value.into_sexp_unchecked());
94
95            let charsxp = str_to_charsxp_unchecked(&key);
96            names.set_string_elt_unchecked(idx, charsxp);
97        }
98
99        list.set_attr_unchecked(crate::ffi::SEXP::names_symbol(), names);
100
101        crate::ffi::Rf_unprotect(2);
102        list
103    }
104}
105
106/// Convert `HashSet<T>` to R vector.
107impl<T> IntoR for HashSet<T>
108where
109    T: crate::ffi::RNativeType + Eq + Hash,
110{
111    type Error = std::convert::Infallible;
112    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
113        Ok(self.into_sexp())
114    }
115    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
116        Ok(unsafe { self.into_sexp_unchecked() })
117    }
118    fn into_sexp(self) -> crate::ffi::SEXP {
119        let vec: Vec<T> = self.into_iter().collect();
120        vec.into_sexp()
121    }
122    unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
123        let vec: Vec<T> = self.into_iter().collect();
124        unsafe { vec.into_sexp_unchecked() }
125    }
126}
127
128/// Convert `BTreeSet<T>` to R vector.
129impl<T> IntoR for BTreeSet<T>
130where
131    T: crate::ffi::RNativeType + Ord,
132{
133    type Error = std::convert::Infallible;
134    fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
135        Ok(self.into_sexp())
136    }
137    unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
138        Ok(unsafe { self.into_sexp_unchecked() })
139    }
140    fn into_sexp(self) -> crate::ffi::SEXP {
141        let vec: Vec<T> = self.into_iter().collect();
142        vec.into_sexp()
143    }
144    unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
145        let vec: Vec<T> = self.into_iter().collect();
146        unsafe { vec.into_sexp_unchecked() }
147    }
148}
149
150macro_rules! impl_set_string_into_r {
151    ($(#[$meta:meta])* $set_ty:ident) => {
152        $(#[$meta])*
153        impl IntoR for $set_ty<String> {
154            type Error = crate::into_r_error::IntoRError;
155            fn try_into_sexp(self) -> Result<crate::ffi::SEXP, Self::Error> {
156                Ok(self.into_sexp())
157            }
158            unsafe fn try_into_sexp_unchecked(self) -> Result<crate::ffi::SEXP, Self::Error> {
159                Ok(unsafe { self.into_sexp_unchecked() })
160            }
161            fn into_sexp(self) -> crate::ffi::SEXP {
162                let vec: Vec<String> = self.into_iter().collect();
163                vec.into_sexp()
164            }
165            unsafe fn into_sexp_unchecked(self) -> crate::ffi::SEXP {
166                let vec: Vec<String> = self.into_iter().collect();
167                unsafe { vec.into_sexp_unchecked() }
168            }
169        }
170    };
171}
172
173impl_set_string_into_r!(
174    /// Convert `HashSet<String>` to R character vector.
175    HashSet
176);
177impl_set_string_into_r!(
178    /// Convert `BTreeSet<String>` to R character vector.
179    BTreeSet
180);
181// endregion