extendr_api/wrapper/
strings.rs

1use 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    /// Create a new, empty list.
21    /// ```
22    /// use extendr_api::prelude::*;
23    /// test! {
24    ///     let strings = Strings::new(10);
25    ///     assert_eq!(strings.is_string(), true);
26    ///     assert_eq!(strings.len(), 10);
27    /// }
28    /// ```
29    pub fn new(size: usize) -> Strings {
30        let robj = Robj::alloc_vector(SEXPTYPE::STRSXP, size);
31        Self { robj }
32    }
33
34    /// Wrapper for creating string vector (STRSXP) objects.
35    /// ```
36    /// use extendr_api::prelude::*;
37    /// test! {
38    ///     let list = r!(Strings::from_values(&["x", "y", "z"]));
39    ///     assert_eq!(list.is_string(), true);
40    ///     assert_eq!(list.len(), 3);
41    /// }
42    /// ```
43    pub fn from_values<V>(values: V) -> Self
44    where
45        V: IntoIterator,
46        V::IntoIter: ExactSizeIterator,
47        V::Item: AsRef<str>,
48    {
49        single_threaded(|| unsafe {
50            let values = values.into_iter();
51            let maxlen = values.len();
52            let mut robj = Robj::alloc_vector(SEXPTYPE::STRSXP, maxlen);
53            let sexp = robj.get_mut();
54            for (i, v) in values.into_iter().take(maxlen).enumerate() {
55                let v = v.as_ref();
56                let ch = str_to_character(v);
57                SET_STRING_ELT(sexp, i as R_xlen_t, ch);
58            }
59            Self { robj }
60        })
61    }
62
63    /// This is a relatively expensive operation, so use a variable if using this in a loop.
64    pub fn as_slice<'a>(&self) -> &'a [Rstr] {
65        unsafe {
66            let data = STRING_PTR_RO(self.robj.get()) as *const Rstr;
67            let len = self.robj.len();
68            std::slice::from_raw_parts(data, len)
69        }
70    }
71
72    /// Get an element in a string vector.
73    pub fn elt(&self, i: usize) -> Rstr {
74        if i >= self.len() {
75            Rstr::na()
76        } else {
77            unsafe {
78                Robj::from_sexp(STRING_ELT(self.get(), i as R_xlen_t))
79                    .try_into()
80                    .unwrap()
81            }
82        }
83    }
84
85    /// Set a single element of this string vector.
86    pub fn set_elt(&mut self, i: usize, e: Rstr) {
87        single_threaded(|| unsafe {
88            if i < self.len() {
89                SET_STRING_ELT(self.robj.get_mut(), i as isize, e.get());
90            }
91        });
92    }
93
94    /// Get an iterator for this string vector.
95    pub fn iter(&self) -> impl Iterator<Item = &Rstr> {
96        self.as_slice().iter()
97    }
98
99    /// Return `TRUE` if the vector is sorted, `FALSE` if not, or `NA_BOOL` if unknown.
100    pub fn is_sorted(&self) -> Rbool {
101        unsafe { STRING_IS_SORTED(self.get()).into() }
102    }
103
104    /// Return `TRUE` if the vector has no `NA`s, `FALSE` if any, or `NA_BOOL` if unknown.
105    pub fn no_na(&self) -> Rbool {
106        unsafe { STRING_NO_NA(self.get()).into() }
107    }
108}
109
110impl Attributes for Strings {}
111
112impl<T: AsRef<str>> FromIterator<T> for Strings {
113    /// Convert an iterator to a Strings object.
114    fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
115        let iter_collect: Vec<_> = iter.into_iter().collect();
116        let len = iter_collect.len();
117
118        let mut robj = Strings::alloc_vector(SEXPTYPE::STRSXP, len);
119        crate::single_threaded(|| unsafe {
120            for (i, v) in iter_collect.into_iter().enumerate() {
121                SET_STRING_ELT(robj.get_mut(), i as isize, str_to_character(v.as_ref()));
122            }
123            Strings { robj }
124        })
125    }
126}
127
128impl<T> From<T> for Strings
129where
130    T: AsRef<str>,
131{
132    /// convert string-like objects into a Strings object.
133    fn from(value: T) -> Self {
134        Strings::from_values([value.as_ref()])
135    }
136}
137
138impl Deref for Strings {
139    type Target = [Rstr];
140
141    fn deref(&self) -> &Self::Target {
142        self.as_slice()
143    }
144}
145
146impl std::fmt::Debug for Strings {
147    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
148        if self.len() == 1 {
149            write!(f, "{:?}", self.elt(0))
150        } else {
151            f.debug_list().entries(self.iter()).finish()
152        }
153    }
154}
155
156impl From<Option<Strings>> for Robj {
157    fn from(value: Option<Strings>) -> Self {
158        match value {
159            Some(value_strings) => value_strings.into(),
160            None => nil_value(),
161        }
162    }
163}