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 /// Converts the value at the given acceptable `index` to a string and returns it as a byte slice.
27 ///
28 /// Only strings or numbers are accepted. Anything else will return `None`.
29 ///
30 /// This function *does* affect the element in the stack itself:
31 /// numbers will be replaced by the converted string of said number.
32 #[inline]
33 #[must_use]
34 pub fn to_byte_string(&self, index: i32) -> Option<&[u8]> {
35 // SAFETY: self.0 is always valid, len is initialized right before passing its pointer,
36 // and lua_tolstring returns valid for len amount of values string pointers when not null.
37 let mut len = 0;
38 let string = unsafe { ffi::lua_tolstring(self.0, index, &raw mut len) };
39 map_ptr!(string, unsafe { slice::from_raw_parts(string.cast(), len) })
40 }
41
42 /// Converts the value at the given acceptable `index` to a string and returns it as a `CStr`.
43 ///
44 /// Only strings or numbers are accepted. Anything else will return `None`.
45 ///
46 /// This function *does* affect the element in the stack itself:
47 /// numbers will be replaced by the converted string of said number.
48 #[inline]
49 #[must_use]
50 pub fn to_cstring(&self, index: i32) -> Option<&CStr> {
51 // SAFETY: self.0 is always valid and lua_tostring returns valid nul-terminated string pointers when not null.
52 let cstring = unsafe { ffi::lua_tostring(self.0, index) };
53 map_ptr!(cstring, unsafe { CStr::from_ptr(cstring) })
54 }
55
56 /// Pushes a copy of the given `string` onto the stack.
57 #[inline]
58 pub fn push_string(&mut self, string: impl AsRef<[u8]>) {
59 self.push_byte_string(string.as_ref());
60 }
61
62 /// Pushes a copy of the given `byte_string` onto the stack.
63 #[inline]
64 pub fn push_byte_string(&mut self, byte_string: &[u8]) {
65 // SAFETY: self.0is always valid, the pointer remains valid for the duration of this function,
66 // and the length is correct as it's returned from the array itself.
67 unsafe { ffi::lua_pushlstring(self.0, byte_string.as_ptr().cast(), byte_string.len()); }
68 }
69
70 /// Pushes a copy of the given `cstring` onto the stack.
71 ///
72 /// If `cstring` is `None`, `nil` will be pushed onto the stack instead.
73 #[inline]
74 pub fn push_cstring<'a>(&mut self, cstring: impl Into<Option<&'a CStr>>) {
75 // SAFETY: self.0 is always valid and the pointer either remains valid for the duration of this function
76 // or is null which lua_pushstring accepts.
77 unsafe { ffi::lua_pushstring(self.0, cstring.into().map_or(core::ptr::null(), CStr::as_ptr)); }
78 }
79
80 /// 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.
81 ///
82 /// If the check fails, [`LuaState::type_error`] is called using `arg_n`.
83 ///
84 /// This function has the same caveats as [`LuaState::to_string`].
85 ///
86 /// # Lua Errors
87 ///
88 /// Intentionally errors if the check fails.
89 #[inline]
90 pub fn check_string(&self, arg_n: i32) -> &str {
91 match str::from_utf8(self.check_byte_string(arg_n)) {
92 Ok(string) => string,
93 Err(_) => self.arg_error(arg_n, c"string contains invalid UTF-8"),
94 }
95 }
96
97 /// Checks if there's a string (or number convertible to string) at the given index `arg_n` and returns it as a byte slice.
98 ///
99 /// If the check fails, [`LuaState::type_error`] is called using `arg_n`.
100 ///
101 /// This function has the same caveats as [`LuaState::to_cstring`].
102 ///
103 /// # Lua Errors
104 ///
105 /// Intentionally errors if the check fails.
106 #[inline]
107 pub fn check_byte_string(&self, arg_n: i32) -> &[u8] {
108 // SAFETY: self.0 is always valid, len is initialized right before passing its pointer,
109 // and string should always be non-null and valid for len amount of values.
110 let mut len = 0;
111 let string = unsafe { ffi::luaL_checklstring(self.0, arg_n, &raw mut len) };
112 unsafe { slice::from_raw_parts(string.cast(), len) }
113 }
114
115 /// Checks if there's a string (or number convertible to string) at the given index `arg_n` and returns it as a `CStr`.
116 ///
117 /// If the check fails, [`LuaState::type_error`] is called using `arg_n`.
118 ///
119 /// This function has the same caveats as [`LuaState::to_byte_string`].
120 ///
121 /// # Lua Errors
122 ///
123 /// Intentionally errors if the check fails.
124 #[inline]
125 pub fn check_cstring(&self, arg_n: i32) -> &CStr {
126 // SAFETY: self.0 is always valid and the returned pointer should be non-null, nul terminated, and valid for all values.
127 unsafe { CStr::from_ptr(ffi::luaL_checkstring(self.0, arg_n)) }
128 }
129
130 /// Behaves exactly as [`LuaState::check_string`],
131 /// but returns `default` instead of erroring when the element at the given index `arg_n` is `nil` (or non-existing).
132 #[inline]
133 pub fn opt_string<'a>(&'a self, arg_n: i32, default: &'a str) -> &'a str {
134 // SAFETY: self.0 is always valid, luaL_optlstring accepts a null pointer for the default value,
135 // and len is initialized right before passing its pointer.
136 let mut len = 0;
137 let string = unsafe { ffi::luaL_optlstring(self.0, arg_n, ptr::null(), &raw mut len) };
138
139 if string.is_null() {
140 return default;
141 }
142
143 // SAFETY: string should always be non-null and valid for len amount of values.
144 match str::from_utf8(unsafe { slice::from_raw_parts(string.cast(), len) }) {
145 Ok(string) => string,
146 Err(_) => self.arg_error(arg_n, c"string contains invalid UTF-8"),
147 }
148 }
149
150 /// Behaves exactly as [`LuaState::check_byte_string`],
151 /// but returns `default` instead of erroring when the element at the given index `arg_n` is `nil` (or non-existing).
152 #[inline]
153 pub fn opt_byte_string<'a>(&'a self, arg_n: i32, default: &'a CStr) -> &'a [u8] {
154 // SAFETY: self.0 and default are always valid, len is initialized right before passing its pointer,
155 // and string should always be non-null and valid for len amount of values.
156 let mut len = 0;
157 let string = unsafe { ffi::luaL_optlstring(self.0, arg_n, default.as_ptr(), &raw mut len) };
158 unsafe { slice::from_raw_parts(string.cast(), len) }
159 }
160
161 /// Behaves exactly as [`LuaState::check_cstring`],
162 /// but returns `default` instead of erroring when the element at the given index `arg_n` is `nil` (or non-existing).
163 #[inline]
164 pub fn opt_cstring<'a>(&'a self, arg_n: i32, default: &'a CStr) -> &'a CStr {
165 // SAFETY: self.0 and default are always valid, len is initialized right before passing its pointer,
166 // and string should always be non-null and valid for len amount of values.
167 unsafe { CStr::from_ptr(ffi::luaL_optstring(self.0, arg_n, default.as_ptr())) }
168 }
169
170 /// Finishes the currently called [`CFunction`], returning the given `string` to the caller.
171 ///
172 /// The `LuaState` wrapper is consumed to prevent invalidating the `AmountReturned` (the Lua state itself is left intact).
173 ///
174 /// [`CFunction`]: ./type.CFunction.html
175 #[inline]
176 pub fn return_string(self, string: impl AsRef<[u8]>) -> AmountReturned {
177 self.return_byte_string(string.as_ref())
178 }
179
180 /// Finishes the currently called [`CFunction`], returning the given `byte_string` to the caller.
181 ///
182 /// The `LuaState` wrapper is consumed to prevent invalidating the `AmountReturned` (the Lua state itself is left intact).
183 ///
184 /// [`CFunction`]: ./type.CFunction.html
185 #[inline]
186 pub fn return_byte_string(mut self, byte_string: &[u8]) -> AmountReturned {
187 self.push_byte_string(byte_string);
188
189 // SAFETY: There should always be at least one element in the stack.
190 unsafe { AmountReturned::unchecked(1) }
191 }
192
193 /// Finishes the currently called [`CFunction`], returning the given `cstring` to the caller.
194 ///
195 /// If `cstring` is `None`, `nil` will be returned to the caller instead.
196 ///
197 /// The `LuaState` wrapper is consumed to prevent invalidating the `AmountReturned` (the Lua state itself is left intact).
198 ///
199 /// [`CFunction`]: ./type.CFunction.html
200 #[inline]
201 pub fn return_cstring<'a>(mut self, cstring: impl Into<Option<&'a CStr>>) -> AmountReturned {
202 self.push_cstring(cstring);
203
204 // SAFETY: There should always be at least one element in the stack.
205 unsafe { AmountReturned::unchecked(1) }
206 }
207
208 /// Creates a copy of `string` where every occurrence of `pattern` is replaced by `replacement`.
209 ///
210 /// The resulting string is both pushed onto the stack and returned.
211 #[inline]
212 pub fn gsub(&mut self, string: &CStr, pattern: &CStr, replacement: &CStr) -> &CStr {
213 // SAFETY: self.0 and string, pattern, and replacement's pointers are always valid and luaL_gsub always returns a valid string pointer.
214 unsafe { CStr::from_ptr(ffi::luaL_gsub(self.0, string.as_ptr(), pattern.as_ptr(), replacement.as_ptr())) }
215 }
216}