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