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            Robj::from_sexp(unsafe { STRING_ELT(self.get(), i as R_xlen_t) })
78                .try_into()
79                .unwrap()
80        }
81    }
82
83    /// Set a single element of this string vector.
84    pub fn set_elt(&mut self, i: usize, e: Rstr) {
85        single_threaded(|| unsafe {
86            if i < self.len() {
87                SET_STRING_ELT(self.robj.get_mut(), i as isize, e.get());
88            }
89        });
90    }
91
92    /// Get an iterator for this string vector.
93    pub fn iter(&self) -> impl Iterator<Item = &Rstr> {
94        self.as_slice().iter()
95    }
96
97    /// Return `TRUE` if the vector is sorted, `FALSE` if not, or `NA_BOOL` if unknown.
98    pub fn is_sorted(&self) -> Rbool {
99        unsafe { STRING_IS_SORTED(self.get()).into() }
100    }
101
102    /// Return `TRUE` if the vector has no `NA`s, `FALSE` if any, or `NA_BOOL` if unknown.
103    pub fn no_na(&self) -> Rbool {
104        unsafe { STRING_NO_NA(self.get()).into() }
105    }
106}
107
108impl Attributes for Strings {}
109
110impl<T: AsRef<str>> FromIterator<T> for Strings {
111    /// Convert an iterator to a Strings object.
112    fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
113        let iter_collect: Vec<_> = iter.into_iter().collect();
114        let len = iter_collect.len();
115
116        let mut robj = Strings::alloc_vector(SEXPTYPE::STRSXP, len);
117        crate::single_threaded(|| unsafe {
118            for (i, v) in iter_collect.into_iter().enumerate() {
119                SET_STRING_ELT(robj.get_mut(), i as isize, str_to_character(v.as_ref()));
120            }
121            Strings { robj }
122        })
123    }
124}
125
126impl<T> From<T> for Strings
127where
128    T: AsRef<str>,
129{
130    /// convert string-like objects into a Strings object.
131    fn from(value: T) -> Self {
132        Strings::from_values([value.as_ref()])
133    }
134}
135
136impl Deref for Strings {
137    type Target = [Rstr];
138
139    fn deref(&self) -> &Self::Target {
140        self.as_slice()
141    }
142}
143
144impl std::fmt::Debug for Strings {
145    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
146        if self.len() == 1 {
147            write!(f, "{:?}", self.elt(0))
148        } else {
149            f.debug_list().entries(self.iter()).finish()
150        }
151    }
152}
153
154impl From<Option<Strings>> for Robj {
155    fn from(value: Option<Strings>) -> Self {
156        match value {
157            Some(value_strings) => value_strings.into(),
158            None => nil_value(),
159        }
160    }
161}