low_luajit/lua/type/string.rs
1use core::{ffi::CStr, ptr, slice, str};
2use crate::{AmountReturned, LuaState, ffi, map_ptr};
3
4/// String related functions.
5impl LuaState {
6 /// Returns `true` if the value at the given acceptable `index` is a string, `false` otherwise.
7 #[inline]
8 #[must_use]
9 pub fn is_string(&self, index: i32) -> bool {
10 // SAFETY: self.0 is always valid.
11 unsafe { ffi::lua_isstring(self.0, index) != 0 }
12 }
13
14 /// Converts the value at the given acceptable `index` to a string and returns it as a string slice.
15 ///
16 /// Only valid UTF-8 strings or numbers are accepted. Anything else will return `None`.
17 ///
18 /// This function *does* affect the element in the stack itself:
19 /// numbers will be replaced by the converted string of said number.
20 #[inline]
21 #[must_use]
22 pub fn to_string(&self, index: i32) -> Option<&str> {
23 str::from_utf8(self.to_byte_string(index)?).ok()
24 }
25
26 /// Indices that are not positive, not a pseudo-index, and go out of bounds may convert a number that's used elsewhere to a string.
27 /// This conversion usually leads to UB soon after, which is pretty bad.
28 ///
29 /// This function checks if the index is invalid in this specific context and panics if it is.
30 ///
31 /// As this panic is very niche, it is currently undocumented. This might change in the future.
32 #[inline]
33 fn assert_string_index(&self, index: i32) {
34 assert!(
35 index.is_positive() || index <= Self::REGISTRY_INDEX || self.is_index_valid(index) || !self.is_number(index),
36 concat!(
37 "cannot convert the number at index {} to a string, as there are only {} elements\n",
38 "(converting this out of bounds number would very likely cause UB)"
39 ),
40 index,
41 self.get_top()
42 );
43 }
44
45 /// Converts the value at the given acceptable `index` to a string and returns it as a byte slice.
46 ///
47 /// Only strings or numbers are accepted. Anything else will return `None`.
48 ///
49 /// This function *does* affect the element in the stack itself:
50 /// numbers will be replaced by the converted string of said number.
51 #[inline]
52 #[must_use]
53 pub fn to_byte_string(&self, index: i32) -> Option<&[u8]> {
54 self.assert_string_index(index);
55
56 // SAFETY: self.0 is always valid, len is initialized right before passing its pointer,
57 // and lua_tolstring returns valid for len amount of values string pointers when not null.
58 let mut len = 0;
59 let string = unsafe { ffi::lua_tolstring(self.0, index, &raw mut len) };
60 map_ptr!(string, unsafe { slice::from_raw_parts(string.cast(), len) })
61 }
62
63 /// Converts the value at the given acceptable `index` to a string and returns it as a `CStr`.
64 ///
65 /// Only strings or numbers are accepted. Anything else will return `None`.
66 ///
67 /// This function *does* affect the element in the stack itself:
68 /// numbers will be replaced by the converted string of said number.
69 #[inline]
70 #[must_use]
71 pub fn to_cstring(&self, index: i32) -> Option<&CStr> {
72 self.assert_string_index(index);
73
74 // SAFETY: self.0 is always valid and lua_tostring returns valid nul-terminated string pointers when not null.
75 let cstring = unsafe { ffi::lua_tostring(self.0, index) };
76 map_ptr!(cstring, unsafe { CStr::from_ptr(cstring) })
77 }
78
79 /// Pushes a copy of the given `string` onto the stack.
80 #[inline]
81 pub fn push_string(&mut self, string: impl AsRef<[u8]>) {
82 self.push_byte_string(string.as_ref());
83 }
84
85 /// Pushes a copy of the given `byte_string` onto the stack.
86 #[inline]
87 pub fn push_byte_string(&mut self, byte_string: &[u8]) {
88 // SAFETY: self.0is always valid, the pointer remains valid for the duration of this function,
89 // and the length is correct as it's returned from the array itself.
90 unsafe { ffi::lua_pushlstring(self.0, byte_string.as_ptr().cast(), byte_string.len()); }
91 }
92
93 /// Pushes a copy of the given `cstring` onto the stack.
94 ///
95 /// If `cstring` is `None`, `nil` will be pushed onto the stack instead.
96 #[inline]
97 pub fn push_cstring<'a>(&mut self, cstring: impl Into<Option<&'a CStr>>) {
98 // SAFETY: self.0 is always valid and the pointer either remains valid for the duration of this function
99 // or is null which lua_pushstring accepts.
100 unsafe { ffi::lua_pushstring(self.0, cstring.into().map_or(core::ptr::null(), CStr::as_ptr)); }
101 }
102
103 /// Checks if there's a valid UTF-8 string (or number convertible to string) at the given index `arg_n` and returns it as a string slice.
104 ///
105 /// If the check fails, [`LuaState::type_error`] is called using `arg_n`.
106 ///
107 /// This function has the same caveats as [`LuaState::to_string`].
108 ///
109 /// # Lua Errors
110 ///
111 /// Intentionally errors if the check fails.
112 #[inline]
113 pub fn check_string(&self, arg_n: i32) -> &str {
114 match str::from_utf8(self.check_byte_string(arg_n)) {
115 Ok(string) => string,
116 Err(_) => self.arg_error(arg_n, c"string contains invalid UTF-8"),
117 }
118 }
119
120 /// Checks if there's a string (or number convertible to string) at the given index `arg_n` and returns it as a byte slice.
121 ///
122 /// If the check fails, [`LuaState::type_error`] is called using `arg_n`.
123 ///
124 /// This function has the same caveats as [`LuaState::to_cstring`].
125 ///
126 /// # Lua Errors
127 ///
128 /// Intentionally errors if the check fails.
129 #[inline]
130 pub fn check_byte_string(&self, arg_n: i32) -> &[u8] {
131 self.assert_string_index(arg_n);
132
133 // SAFETY: self.0 is always valid, len is initialized right before passing its pointer,
134 // and string should always be non-null and valid for len amount of values.
135 let mut len = 0;
136 let string = unsafe { ffi::luaL_checklstring(self.0, arg_n, &raw mut len) };
137 unsafe { slice::from_raw_parts(string.cast(), len) }
138 }
139
140 /// Checks if there's a string (or number convertible to string) at the given index `arg_n` and returns it as a `CStr`.
141 ///
142 /// If the check fails, [`LuaState::type_error`] is called using `arg_n`.
143 ///
144 /// This function has the same caveats as [`LuaState::to_byte_string`].
145 ///
146 /// # Lua Errors
147 ///
148 /// Intentionally errors if the check fails.
149 #[inline]
150 pub fn check_cstring(&self, arg_n: i32) -> &CStr {
151 self.assert_string_index(arg_n);
152
153 // SAFETY: self.0 is always valid and the returned pointer should be non-null, nul terminated, and valid for all values.
154 unsafe { CStr::from_ptr(ffi::luaL_checkstring(self.0, arg_n)) }
155 }
156
157 /// Behaves exactly as [`LuaState::check_string`],
158 /// but returns `default` instead of erroring when the element at the given index `arg_n` is `nil` (or non-existing).
159 #[inline]
160 pub fn opt_string<'a>(&'a self, arg_n: i32, default: &'a str) -> &'a str {
161 self.assert_string_index(arg_n);
162
163 // SAFETY: self.0 is always valid, luaL_optlstring accepts a null pointer for the default value,
164 // and len is initialized right before passing its pointer.
165 let mut len = 0;
166 let string = unsafe { ffi::luaL_optlstring(self.0, arg_n, ptr::null(), &raw mut len) };
167
168 if string.is_null() {
169 return default;
170 }
171
172 // SAFETY: string should always be non-null and valid for len amount of values.
173 match str::from_utf8(unsafe { slice::from_raw_parts(string.cast(), len) }) {
174 Ok(string) => string,
175 Err(_) => self.arg_error(arg_n, c"string contains invalid UTF-8"),
176 }
177 }
178
179 /// Behaves exactly as [`LuaState::check_byte_string`],
180 /// but returns `default` instead of erroring when the element at the given index `arg_n` is `nil` (or non-existing).
181 #[inline]
182 pub fn opt_byte_string<'a>(&'a self, arg_n: i32, default: &'a CStr) -> &'a [u8] {
183 self.assert_string_index(arg_n);
184
185 // SAFETY: self.0 and default are always valid, len is initialized right before passing its pointer,
186 // and string should always be non-null and valid for len amount of values.
187 let mut len = 0;
188 let string = unsafe { ffi::luaL_optlstring(self.0, arg_n, default.as_ptr(), &raw mut len) };
189 unsafe { slice::from_raw_parts(string.cast(), len) }
190 }
191
192 /// Behaves exactly as [`LuaState::check_cstring`],
193 /// but returns `default` instead of erroring when the element at the given index `arg_n` is `nil` (or non-existing).
194 #[inline]
195 pub fn opt_cstring<'a>(&'a self, arg_n: i32, default: &'a CStr) -> &'a CStr {
196 self.assert_string_index(arg_n);
197
198 // SAFETY: self.0 and default are always valid, len is initialized right before passing its pointer,
199 // and string should always be non-null and valid for len amount of values.
200 unsafe { CStr::from_ptr(ffi::luaL_optstring(self.0, arg_n, default.as_ptr())) }
201 }
202
203 /// Finishes the currently called [`CFunction`], returning the given `string` to the caller.
204 ///
205 /// The `LuaState` wrapper is consumed to prevent invalidating the `AmountReturned` (the Lua state itself is left intact).
206 ///
207 /// [`CFunction`]: ./type.CFunction.html
208 #[inline]
209 pub fn return_string(self, string: impl AsRef<[u8]>) -> AmountReturned {
210 self.return_byte_string(string.as_ref())
211 }
212
213 /// Finishes the currently called [`CFunction`], returning the given `byte_string` to the caller.
214 ///
215 /// The `LuaState` wrapper is consumed to prevent invalidating the `AmountReturned` (the Lua state itself is left intact).
216 ///
217 /// [`CFunction`]: ./type.CFunction.html
218 #[inline]
219 pub fn return_byte_string(mut self, byte_string: &[u8]) -> AmountReturned {
220 self.push_byte_string(byte_string);
221
222 // SAFETY: There should always be at least one element in the stack.
223 unsafe { AmountReturned::unchecked(1) }
224 }
225
226 /// Finishes the currently called [`CFunction`], returning the given `cstring` to the caller.
227 ///
228 /// If `cstring` is `None`, `nil` will be returned to the caller instead.
229 ///
230 /// The `LuaState` wrapper is consumed to prevent invalidating the `AmountReturned` (the Lua state itself is left intact).
231 ///
232 /// [`CFunction`]: ./type.CFunction.html
233 #[inline]
234 pub fn return_cstring<'a>(mut self, cstring: impl Into<Option<&'a CStr>>) -> AmountReturned {
235 self.push_cstring(cstring);
236
237 // SAFETY: There should always be at least one element in the stack.
238 unsafe { AmountReturned::unchecked(1) }
239 }
240
241 /// Creates a copy of `string` where every occurrence of `pattern` is replaced by `replacement`.
242 ///
243 /// The resulting string is both pushed onto the stack and returned.
244 #[inline]
245 pub fn gsub(&mut self, string: &CStr, pattern: &CStr, replacement: &CStr) -> &CStr {
246 // SAFETY: self.0 and string, pattern, and replacement's pointers are always valid and luaL_gsub always returns a valid string pointer.
247 unsafe { CStr::from_ptr(ffi::luaL_gsub(self.0, string.as_ptr(), pattern.as_ptr(), replacement.as_ptr())) }
248 }
249}