extendr_api/wrapper/
rstr.rs
1use super::*;
2use extendr_ffi::{R_BlankString, R_NaString, R_NilValue, Rf_xlength, R_CHAR, SEXPTYPE, TYPEOF};
3#[derive(Clone)]
16pub struct Rstr {
17 pub(crate) robj: Robj,
18}
19
20pub(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 pub fn from_string(val: &str) -> Self {
42 Rstr {
43 robj: Robj::from_sexp(str_to_character(val)),
44 }
45 }
46
47 pub fn as_str(&self) -> &str {
50 self.into()
51 }
52}
53
54impl AsRef<str> for Rstr {
55 fn as_ref(&self) -> &str {
57 self.as_str()
58 }
59}
60
61impl From<String> for Rstr {
62 fn from(s: String) -> Self {
64 Rstr::from_string(&s)
65 }
66}
67
68impl From<&str> for Rstr {
69 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 fn deref(&self) -> &Self::Target {
99 self.as_str()
100 }
101}
102
103impl PartialEq<Rstr> for Rstr {
105 fn eq(&self, other: &Rstr) -> bool {
106 unsafe { self.robj.get() == other.robj.get() }
107 }
108}
109
110impl PartialEq<str> for Rstr {
113 fn eq(&self, other: &str) -> bool {
115 self.as_str() == other
116 }
117}
118
119impl PartialEq<Rstr> for &str {
120 fn eq(&self, other: &Rstr) -> bool {
122 *self == other.as_str()
123 }
124}
125
126impl PartialEq<&str> for Rstr {
127 fn eq(&self, other: &&str) -> bool {
129 self.as_str() == *other
130 }
131}
132
133impl PartialEq<Rstr> for &&str {
134 fn eq(&self, other: &Rstr) -> bool {
136 **self == other.as_str()
137 }
138}
139
140impl std::fmt::Debug for Rstr {
141 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
142 if self.is_na() {
143 write!(f, "NA_CHARACTER")
144 } else {
145 let s = self.as_str();
146 write!(f, "{:?}", s)
147 }
148 }
149}
150
151impl std::fmt::Display for Rstr {
152 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
153 let s = self.as_str();
154 write!(f, "{}", s)
155 }
156}
157
158impl CanBeNA for Rstr {
159 fn is_na(&self) -> bool {
160 unsafe { self.robj.get() == R_NaString }
161 }
162
163 fn na() -> Self {
164 unsafe {
165 Self {
166 robj: Robj::from_sexp(R_NaString),
167 }
168 }
169 }
170}