extendr_api/wrapper/
strings.rs1use super::*;
2use extendr_ffi::{
3 R_xlen_t, SET_STRING_ELT, STRING_ELT, STRING_IS_SORTED, STRING_NO_NA, STRING_PTR_RO,
4};
5use std::convert::From;
6use std::iter::FromIterator;
7
8#[derive(PartialEq, Clone)]
9pub struct Strings {
10 pub(crate) robj: Robj,
11}
12
13impl Default for Strings {
14 fn default() -> Self {
15 Strings::new(0)
16 }
17}
18
19impl Strings {
20 pub fn new(size: usize) -> Strings {
30 let robj = Robj::alloc_vector(SEXPTYPE::STRSXP, size);
31 Self { robj }
32 }
33
34 pub fn new_with_na(len: usize) -> Strings {
36 let iter = (0..len).map(|_| Rstr::na());
37 Strings::from_values(iter)
38 }
39 pub fn from_values<V>(values: V) -> Self
49 where
50 V: IntoIterator,
51 V::IntoIter: ExactSizeIterator,
52 V::Item: AsRef<str>,
53 {
54 single_threaded(|| unsafe {
55 let values = values.into_iter();
56 let maxlen = values.len();
57 let mut robj = Robj::alloc_vector(SEXPTYPE::STRSXP, maxlen);
58 let sexp = robj.get_mut();
59 for (i, v) in values.into_iter().take(maxlen).enumerate() {
60 let v = v.as_ref();
61 let ch = str_to_character(v);
62 SET_STRING_ELT(sexp, i as R_xlen_t, ch);
63 }
64 Self { robj }
65 })
66 }
67
68 pub fn as_slice<'a>(&self) -> &'a [Rstr] {
70 unsafe {
71 let data = STRING_PTR_RO(self.robj.get()) as *const Rstr;
72 let len = self.robj.len();
73 std::slice::from_raw_parts(data, len)
74 }
75 }
76
77 pub fn elt(&self, i: usize) -> Rstr {
79 if i >= self.len() {
80 Rstr::na()
81 } else {
82 unsafe {
83 Robj::from_sexp(STRING_ELT(self.get(), i as R_xlen_t))
84 .try_into()
85 .unwrap()
86 }
87 }
88 }
89
90 pub fn set_elt(&mut self, i: usize, e: Rstr) {
92 single_threaded(|| unsafe {
93 if i < self.len() {
94 SET_STRING_ELT(self.robj.get_mut(), i as isize, e.get());
95 }
96 });
97 }
98
99 pub fn iter(&self) -> impl Iterator<Item = &Rstr> {
101 self.as_slice().iter()
102 }
103
104 pub fn is_sorted(&self) -> Rbool {
106 unsafe { STRING_IS_SORTED(self.get()).into() }
107 }
108
109 pub fn no_na(&self) -> Rbool {
111 unsafe { STRING_NO_NA(self.get()).into() }
112 }
113}
114
115impl Attributes for Strings {}
116
117impl<T: AsRef<str>> FromIterator<T> for Strings {
118 fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
120 let iter_collect: Vec<_> = iter.into_iter().collect();
121 let len = iter_collect.len();
122
123 let mut robj = Strings::alloc_vector(SEXPTYPE::STRSXP, len);
124 crate::single_threaded(|| unsafe {
125 for (i, v) in iter_collect.into_iter().enumerate() {
126 SET_STRING_ELT(robj.get_mut(), i as isize, str_to_character(v.as_ref()));
127 }
128 Strings { robj }
129 })
130 }
131}
132
133impl<T> From<T> for Strings
134where
135 T: AsRef<str>,
136{
137 fn from(value: T) -> Self {
139 Strings::from_values([value.as_ref()])
140 }
141}
142
143impl Deref for Strings {
144 type Target = [Rstr];
145
146 fn deref(&self) -> &Self::Target {
147 self.as_slice()
148 }
149}
150
151impl std::fmt::Debug for Strings {
152 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
153 if self.len() == 1 {
154 write!(f, "{:?}", self.elt(0))
155 } else {
156 f.debug_list().entries(self.iter()).finish()
157 }
158 }
159}
160
161impl From<Option<Strings>> for Robj {
162 fn from(value: Option<Strings>) -> Self {
163 match value {
164 Some(value_strings) => value_strings.into(),
165 None => nil_value(),
166 }
167 }
168}
169
170#[cfg(test)]
171mod tests {
172 use super::*;
173 use crate as extendr_api;
174
175 #[test]
176 fn new() {
177 test! {
178 let vec = Strings::new(10);
179 assert_eq!(vec.is_string(), true);
180 assert_eq!(vec.len(), 10);
181 }
182 }
183
184 #[test]
185 fn new_with_na() {
186 use crate::na::CanBeNA;
187 test! {
188 let vec = Strings::new_with_na(10);
189 let manual_vec = (0..10).into_iter().map(|_| Rstr::na()).collect::<Strings>();
190 assert_eq!(vec, manual_vec);
191 assert_eq!(vec.len(), manual_vec.len());
192 }
193 }
194}