Skip to main content

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}