1use crate::error::{Error, Result};
4use crate::state::{Lua, LuaRef};
5use crate::sync::{NotSync, XRc, NOT_SYNC};
6use crate::sys::*;
7
8#[derive(Clone)]
16pub struct LuaString {
17 pub(crate) reference: XRc<LuaRef>,
18 pub(crate) _not_sync: NotSync,
19}
20
21impl LuaString {
22 pub(crate) fn from_ref(reference: LuaRef) -> LuaString {
23 LuaString {
24 reference: XRc::new(reference),
25 _not_sync: NOT_SYNC,
26 }
27 }
28
29 pub(crate) unsafe fn push_to_stack(&self) {
31 self.reference.push();
32 }
33
34 pub fn as_bytes(&self) -> Vec<u8> {
39 let state = self.reference.state();
40 unsafe {
41 self.reference.push();
42 let mut len = 0usize;
43 let p = lua_tolstring(state, -1, &mut len);
44 let bytes = if p.is_null() {
45 Vec::new()
46 } else {
47 core::slice::from_raw_parts(p as *const u8, len).to_vec()
48 };
49 lua_pop(state, 1);
50 bytes
51 }
52 }
53
54 pub fn to_str(&self) -> Result<String> {
58 let bytes = self.as_bytes();
59 String::from_utf8(bytes).map_err(|e| Error::FromLuaConversionError {
60 from: "string",
61 to: "String".to_string(),
62 message: Some(format!("invalid utf-8: {e}")),
63 })
64 }
65
66 pub fn to_string_lossy(&self) -> String {
70 String::from_utf8_lossy(&self.as_bytes()).into_owned()
71 }
72
73 pub fn as_bytes_with_nul(&self) -> Vec<u8> {
76 let mut v = self.as_bytes();
77 v.push(0);
78 v
79 }
80
81 pub fn to_pointer(&self) -> *const std::ffi::c_void {
84 let state = self.reference.state();
85 unsafe {
86 self.reference.push();
87 let p = lua_topointer(state, -1);
88 lua_pop(state, 1);
89 p
90 }
91 }
92
93 pub fn display(&self) -> LuaStringDisplay {
96 LuaStringDisplay(self.to_string_lossy())
97 }
98}
99
100pub struct LuaStringDisplay(String);
102
103impl std::fmt::Display for LuaStringDisplay {
104 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
105 f.write_str(&self.0)
106 }
107}
108
109impl std::fmt::Debug for LuaString {
110 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
111 let bytes = self.as_bytes();
114 match std::str::from_utf8(&bytes) {
115 Ok(s) => write!(f, "{s:?}"),
116 Err(_) => {
117 f.write_str("b\"")?;
118 for &b in &bytes {
119 match b {
120 b'\0' => f.write_str("\\0")?,
121 b'\r' => f.write_str("\\r")?,
122 b'\n' => f.write_str("\\n")?,
123 b'\t' => f.write_str("\\t")?,
124 b'\\' => f.write_str("\\\\")?,
125 b'"' => f.write_str("\\\"")?,
126 0x20..=0x7e => f.write_str(&(b as char).to_string())?,
127 _ => write!(f, "\\x{b:02x}")?,
128 }
129 }
130 f.write_str("\"")
131 }
132 }
133 }
134}
135
136impl PartialEq for LuaString {
137 fn eq(&self, other: &Self) -> bool {
138 self.as_bytes() == other.as_bytes()
139 }
140}
141
142impl Eq for LuaString {}
143
144impl PartialOrd for LuaString {
145 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
146 Some(self.cmp(other))
147 }
148}
149
150impl Ord for LuaString {
151 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
152 self.as_bytes().cmp(&other.as_bytes())
153 }
154}
155
156impl std::hash::Hash for LuaString {
157 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
158 self.as_bytes().hash(state);
159 }
160}
161
162macro_rules! impl_str_eq {
165 ($($ty:ty => $conv:expr),* $(,)?) => {$(
166 impl PartialEq<$ty> for LuaString {
167 fn eq(&self, other: &$ty) -> bool {
168 let f: fn(&$ty) -> &[u8] = $conv;
169 self.as_bytes() == f(other)
170 }
171 }
172 impl PartialOrd<$ty> for LuaString {
173 fn partial_cmp(&self, other: &$ty) -> Option<std::cmp::Ordering> {
174 let f: fn(&$ty) -> &[u8] = $conv;
175 Some(self.as_bytes().as_slice().cmp(f(other)))
176 }
177 }
178 )*};
179}
180
181impl_str_eq! {
182 str => |s| s.as_bytes(),
183 String => |s| s.as_bytes(),
184 [u8] => |s| s,
185 Vec<u8> => |s| s.as_slice(),
186}
187
188impl PartialEq<&str> for LuaString {
189 fn eq(&self, other: &&str) -> bool {
190 self.as_bytes() == other.as_bytes()
191 }
192}
193impl PartialOrd<&str> for LuaString {
194 fn partial_cmp(&self, other: &&str) -> Option<std::cmp::Ordering> {
195 Some(self.as_bytes().as_slice().cmp(other.as_bytes()))
196 }
197}
198
199impl<const N: usize> PartialEq<&[u8; N]> for LuaString {
200 fn eq(&self, other: &&[u8; N]) -> bool {
201 self.as_bytes() == other.as_slice()
202 }
203}
204impl<const N: usize> PartialOrd<&[u8; N]> for LuaString {
205 fn partial_cmp(&self, other: &&[u8; N]) -> Option<std::cmp::Ordering> {
206 Some(self.as_bytes().as_slice().cmp(other.as_slice()))
207 }
208}
209
210impl PartialEq<std::borrow::Cow<'_, [u8]>> for LuaString {
211 fn eq(&self, other: &std::borrow::Cow<'_, [u8]>) -> bool {
212 self.as_bytes() == other.as_ref()
213 }
214}
215
216pub(crate) fn create_string(lua: &Lua, bytes: &[u8]) -> LuaString {
219 let state = lua.state();
220 unsafe {
221 lua_pushlstring(state, bytes.as_ptr() as *const c_char, bytes.len());
222 LuaString::from_ref(lua.pop_ref())
223 }
224}