1use core::{ffi, slice};
4use std::borrow::Cow;
5
6use oxi_luajit as lua;
7
8use crate::NonOwning;
9
10#[derive(Eq, Ord, PartialOrd)]
17#[repr(C)]
18pub struct String {
19 pub(super) data: *mut ffi::c_char,
20 pub(super) size: usize,
21}
22
23impl Default for String {
24 #[inline]
25 fn default() -> Self {
26 Self::new()
27 }
28}
29
30impl core::fmt::Debug for String {
31 #[inline]
32 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
33 self.to_string_lossy().as_ref().fmt(f)
34 }
35}
36
37impl core::fmt::Display for String {
38 #[inline]
39 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
40 self.to_string_lossy().as_ref().fmt(f)
41 }
42}
43
44impl String {
45 #[inline]
46 pub fn as_bytes(&self) -> &[u8] {
47 if self.data.is_null() {
48 &[]
49 } else {
50 assert!(self.len() <= isize::MAX as usize);
51 unsafe { slice::from_raw_parts(self.data as *const u8, self.size) }
52 }
53 }
54
55 #[inline]
57 pub fn as_ptr(&self) -> *const ffi::c_char {
58 self.data as _
59 }
60
61 #[inline]
65 pub fn from_bytes(bytes: &[u8]) -> Self {
66 let data =
67 unsafe { libc::malloc(bytes.len() + 1) as *mut ffi::c_char };
68
69 unsafe {
70 libc::memcpy(
71 data as *mut _,
72 bytes.as_ptr() as *const _,
73 bytes.len(),
74 )
75 };
76
77 unsafe { *data.add(bytes.len()) = 0 };
78
79 Self { data: data as *mut _, size: bytes.len() }
80 }
81
82 #[inline]
84 pub fn is_empty(&self) -> bool {
85 self.len() == 0
86 }
87
88 #[inline]
90 pub fn len(&self) -> usize {
91 self.size
92 }
93
94 #[inline]
96 pub fn new() -> Self {
97 Self { data: core::ptr::null_mut(), size: 0 }
98 }
99
100 #[inline]
102 #[doc(hidden)]
103 pub fn non_owning(&self) -> NonOwning<'_, String> {
104 NonOwning::new(Self { ..*self })
105 }
106
107 #[inline]
111 pub fn to_string_lossy(&self) -> Cow<'_, str> {
112 std::string::String::from_utf8_lossy(self.as_bytes())
113 }
114}
115
116impl Clone for String {
117 #[inline]
118 fn clone(&self) -> Self {
119 Self::from_bytes(self.as_bytes())
120 }
121}
122
123impl Drop for String {
124 fn drop(&mut self) {
125 }
131}
132
133impl From<&str> for String {
134 #[inline]
135 fn from(s: &str) -> Self {
136 Self::from_bytes(s.as_bytes())
137 }
138}
139
140impl From<char> for String {
141 #[inline]
142 fn from(ch: char) -> Self {
143 ch.to_string().as_str().into()
144 }
145}
146
147impl From<&std::path::Path> for String {
148 #[inline]
149 fn from(path: &std::path::Path) -> Self {
150 path.display().to_string().as_str().into()
151 }
152}
153
154#[cfg(not(windows))]
155impl From<String> for std::path::PathBuf {
156 #[inline]
157 fn from(nstr: String) -> Self {
158 use std::os::unix::ffi::OsStrExt;
159 std::ffi::OsStr::from_bytes(nstr.as_bytes()).to_owned().into()
160 }
161}
162
163#[cfg(windows)]
164impl From<String> for std::path::PathBuf {
165 #[inline]
166 fn from(nstr: String) -> Self {
167 std::string::String::from_utf8_lossy(nstr.as_bytes())
168 .into_owned()
169 .into()
170 }
171}
172
173impl PartialEq<Self> for String {
174 #[inline]
175 fn eq(&self, other: &Self) -> bool {
176 self.as_bytes() == other.as_bytes()
177 }
178}
179
180impl PartialEq<str> for String {
181 #[inline]
182 fn eq(&self, other: &str) -> bool {
183 self.as_bytes() == other.as_bytes()
184 }
185}
186
187impl PartialEq<String> for str {
188 #[inline]
189 fn eq(&self, other: &String) -> bool {
190 other == self
191 }
192}
193
194impl PartialEq<&str> for String {
195 #[inline]
196 fn eq(&self, other: &&str) -> bool {
197 self.as_bytes() == other.as_bytes()
198 }
199}
200
201impl PartialEq<String> for &str {
202 #[inline]
203 fn eq(&self, other: &String) -> bool {
204 other == self
205 }
206}
207
208impl PartialEq<std::string::String> for String {
209 #[inline]
210 fn eq(&self, other: &std::string::String) -> bool {
211 self.as_bytes() == other.as_bytes()
212 }
213}
214
215impl core::hash::Hash for String {
216 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
217 self.as_bytes().hash(state);
218 self.size.hash(state);
219 }
220}
221
222impl lua::Pushable for String {
223 #[inline]
224 unsafe fn push(
225 self,
226 lstate: *mut lua::ffi::lua_State,
227 ) -> Result<ffi::c_int, lua::Error> {
228 lua::ffi::lua_pushlstring(lstate, self.as_ptr(), self.len());
229 Ok(1)
230 }
231}
232
233impl lua::Poppable for String {
234 #[inline]
235 unsafe fn pop(
236 lstate: *mut lua::ffi::lua_State,
237 ) -> Result<Self, lua::Error> {
238 use lua::ffi::*;
239
240 if lua_gettop(lstate) < 0 {
241 return Err(lua::Error::PopEmptyStack);
242 }
243
244 let ty = lua_type(lstate, -1);
245
246 if ty != LUA_TSTRING && ty != LUA_TNUMBER {
247 return Err(lua::Error::pop_wrong_type::<Self>(LUA_TSTRING, ty));
248 }
249
250 let mut len = 0;
251 let ptr = lua_tolstring(lstate, -1, &mut len);
252
253 assert!(!ptr.is_null());
256
257 let slice = std::slice::from_raw_parts(ptr as *const u8, len);
258 let s = String::from_bytes(slice);
259
260 lua_pop(lstate, 1);
261
262 Ok(s)
263 }
264}
265
266#[cfg(feature = "serde")]
267mod serde {
268 use std::fmt;
269
270 use serde::de::{self, Deserialize, Deserializer, Visitor};
271
272 impl<'de> Deserialize<'de> for super::String {
273 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
274 where
275 D: Deserializer<'de>,
276 {
277 struct StringVisitor;
278
279 impl<'de> Visitor<'de> for StringVisitor {
280 type Value = crate::String;
281
282 fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
283 f.write_str("either a string of a byte vector")
284 }
285
286 fn visit_bytes<E>(self, b: &[u8]) -> Result<Self::Value, E>
287 where
288 E: de::Error,
289 {
290 Ok(crate::String::from_bytes(b))
291 }
292
293 fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
294 where
295 E: de::Error,
296 {
297 Ok(crate::String::from(s))
298 }
299 }
300
301 deserializer.deserialize_str(StringVisitor)
302 }
303 }
304}
305
306#[cfg(test)]
307mod tests {
308 use super::*;
309
310 #[test]
311 fn as_bytes() {
312 let s = String::from("hello");
313 assert_eq!(s.as_bytes(), &[b'h', b'e', b'l', b'l', b'o'][..]);
314 }
315
316 #[test]
317 fn partial_eq() {
318 let lhs = String::from("foo bar baz");
319 let rhs = String::from("foo bar baz");
320 assert_eq!(lhs, rhs);
321
322 let lhs = String::from("foo bar baz");
323 let rhs = std::string::String::from("bar foo baz");
324 assert_ne!(lhs, rhs);
325
326 let lhs = String::from("€");
327 let rhs = "€";
328 assert_eq!(lhs, rhs);
329 }
330
331 #[test]
332 fn clone() {
333 let lhs = String::from("abc");
334 let rhs = lhs.clone();
335
336 assert_eq!(lhs, rhs);
337 }
338
339 #[test]
340 fn from_string() {
341 let foo = std::string::String::from("foo bar baz");
342
343 let lhs = String::from(foo.as_str());
344 let rhs = String::from(foo.as_str());
345
346 assert_eq!(lhs, rhs);
347 }
348}