extendr_api/wrapper/
rstr.rs

1use super::*;
2
3/// Wrapper for creating CHARSXP objects.
4/// These are used only as the contents of a character
5/// vector.
6///
7/// ```
8/// use extendr_api::prelude::*;
9/// test! {
10///     let chr = r!(Rstr::from_string("xyz"));
11///     assert_eq!(chr.as_char().unwrap().as_str(), "xyz");
12/// }
13/// ```
14///
15#[derive(Clone)]
16pub struct Rstr {
17    pub(crate) robj: Robj,
18}
19
20/// Returns a rust string-slice based on the provided `SEXP`, which is of type
21/// [`SEXPTYPE::CHARSXP`]. Note that the length of a `CHARSXP` is exactly
22/// the number of non-null bytes in said R character.
23pub(crate) unsafe fn charsxp_to_str(charsxp: SEXP) -> Option<&'static str> {
24    assert_eq!(TYPEOF(charsxp), SEXPTYPE::CHARSXP);
25    if charsxp == R_NilValue {
26        None
27    } else if charsxp == R_NaString {
28        Some(<&str>::na())
29    } else if charsxp == R_BlankString {
30        Some("")
31    } else {
32        let length = Rf_xlength(charsxp);
33        let all_bytes =
34            std::slice::from_raw_parts(R_CHAR(charsxp).cast(), length.try_into().unwrap());
35        Some(std::str::from_utf8_unchecked(all_bytes))
36    }
37}
38
39impl Rstr {
40    /// Make a character object from a string.
41    pub fn from_string(val: &str) -> Self {
42        Rstr {
43            robj: Robj::from_sexp(str_to_character(val)),
44        }
45    }
46
47    /// Get the string from a character object.
48    /// If the string is NA, then the special na_str() is returned.
49    pub fn as_str(&self) -> &str {
50        self.into()
51    }
52}
53
54impl AsRef<str> for Rstr {
55    /// Treat a Rstr as a string slice.
56    fn as_ref(&self) -> &str {
57        self.as_str()
58    }
59}
60
61impl From<String> for Rstr {
62    /// Convert a String to a Rstr.
63    fn from(s: String) -> Self {
64        Rstr::from_string(&s)
65    }
66}
67
68impl From<&str> for Rstr {
69    /// Convert a string slice to a Rstr.
70    fn from(s: &str) -> Self {
71        Rstr::from_string(s)
72    }
73}
74
75impl From<&Rstr> for &str {
76    fn from(value: &Rstr) -> Self {
77        unsafe {
78            let charsxp = value.robj.get();
79            rstr::charsxp_to_str(charsxp).unwrap()
80        }
81    }
82}
83
84impl From<Option<String>> for Rstr {
85    fn from(value: Option<String>) -> Self {
86        if let Some(string) = value {
87            Self::from(string)
88        } else {
89            Self { robj: na_string() }
90        }
91    }
92}
93
94impl Deref for Rstr {
95    type Target = str;
96
97    /// Treat `Rstr` like `&str`.
98    fn deref(&self) -> &Self::Target {
99        self.as_str()
100    }
101}
102
103impl<T> PartialEq<T> for Rstr
104where
105    T: AsRef<str>,
106{
107    /// Compare a `Rstr` with a `Rstr`.
108    fn eq(&self, other: &T) -> bool {
109        self.as_str() == other.as_ref()
110    }
111}
112
113impl PartialEq<str> for Rstr {
114    /// Compare a `Rstr` with a string slice.
115    fn eq(&self, other: &str) -> bool {
116        self.as_str() == other
117    }
118}
119
120impl std::fmt::Debug for Rstr {
121    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
122        if self.is_na() {
123            write!(f, "NA_CHARACTER")
124        } else {
125            let s = self.as_str();
126            write!(f, "{:?}", s)
127        }
128    }
129}
130
131impl std::fmt::Display for Rstr {
132    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
133        let s = self.as_str();
134        write!(f, "{}", s)
135    }
136}
137
138impl CanBeNA for Rstr {
139    fn is_na(&self) -> bool {
140        unsafe { self.robj.get() == R_NaString }
141    }
142
143    fn na() -> Self {
144        unsafe {
145            Self {
146                robj: Robj::from_sexp(R_NaString),
147            }
148        }
149    }
150}