factorio_mlua/error.rs
1#![allow(clippy::wrong_self_convention)]
2
3use std::error::Error as StdError;
4use std::fmt;
5use std::io::Error as IoError;
6use std::net::AddrParseError;
7use std::result::Result as StdResult;
8use std::str::Utf8Error;
9use std::string::String as StdString;
10use std::sync::Arc;
11
12/// Error type returned by `mlua` methods.
13#[derive(Debug, Clone)]
14#[non_exhaustive]
15pub enum Error {
16 /// Syntax error while parsing Lua source code.
17 SyntaxError {
18 /// The error message as returned by Lua.
19 message: StdString,
20 /// `true` if the error can likely be fixed by appending more input to the source code.
21 ///
22 /// This is useful for implementing REPLs as they can query the user for more input if this
23 /// is set.
24 incomplete_input: bool,
25 },
26 /// Lua runtime error, aka `LUA_ERRRUN`.
27 ///
28 /// The Lua VM returns this error when a builtin operation is performed on incompatible types.
29 /// Among other things, this includes invoking operators on wrong types (such as calling or
30 /// indexing a `nil` value).
31 RuntimeError(StdString),
32 /// Lua memory error, aka `LUA_ERRMEM`
33 ///
34 /// The Lua VM returns this error when the allocator does not return the requested memory, aka
35 /// it is an out-of-memory error.
36 MemoryError(StdString),
37 /// Lua garbage collector error, aka `LUA_ERRGCMM`.
38 ///
39 /// The Lua VM returns this error when there is an error running a `__gc` metamethod.
40 #[cfg(any(feature = "lua53", feature = "lua52", feature = "lua-factorio", doc))]
41 #[cfg_attr(docsrs, doc(cfg(any(feature = "lua53", feature = "lua52", feature = "lua-factorio"))))]
42 GarbageCollectorError(StdString),
43 /// Potentially unsafe action in safe mode.
44 SafetyError(StdString),
45 /// Setting memory limit is not available.
46 ///
47 /// This error can only happen when Lua state was not created by us and does not have the
48 /// custom allocator attached.
49 MemoryLimitNotAvailable,
50 /// Main thread is not available.
51 ///
52 /// This error can only happen in Lua5.1/LuaJIT module mode, when module loaded within a coroutine.
53 /// These Lua versions does not have `LUA_RIDX_MAINTHREAD` registry key.
54 MainThreadNotAvailable,
55 /// A mutable callback has triggered Lua code that has called the same mutable callback again.
56 ///
57 /// This is an error because a mutable callback can only be borrowed mutably once.
58 RecursiveMutCallback,
59 /// Either a callback or a userdata method has been called, but the callback or userdata has
60 /// been destructed.
61 ///
62 /// This can happen either due to to being destructed in a previous __gc, or due to being
63 /// destructed from exiting a `Lua::scope` call.
64 CallbackDestructed,
65 /// Not enough stack space to place arguments to Lua functions or return values from callbacks.
66 ///
67 /// Due to the way `mlua` works, it should not be directly possible to run out of stack space
68 /// during normal use. The only way that this error can be triggered is if a `Function` is
69 /// called with a huge number of arguments, or a rust callback returns a huge number of return
70 /// values.
71 StackError,
72 /// Too many arguments to `Function::bind`
73 BindError,
74 /// A Rust value could not be converted to a Lua value.
75 ToLuaConversionError {
76 /// Name of the Rust type that could not be converted.
77 from: &'static str,
78 /// Name of the Lua type that could not be created.
79 to: &'static str,
80 /// A message indicating why the conversion failed in more detail.
81 message: Option<StdString>,
82 },
83 /// A Lua value could not be converted to the expected Rust type.
84 FromLuaConversionError {
85 /// Name of the Lua type that could not be converted.
86 from: &'static str,
87 /// Name of the Rust type that could not be created.
88 to: &'static str,
89 /// A string containing more detailed error information.
90 message: Option<StdString>,
91 },
92 /// [`Thread::resume`] was called on an inactive coroutine.
93 ///
94 /// A coroutine is inactive if its main function has returned or if an error has occurred inside
95 /// the coroutine.
96 ///
97 /// [`Thread::status`] can be used to check if the coroutine can be resumed without causing this
98 /// error.
99 ///
100 /// [`Thread::resume`]: crate::Thread::resume
101 /// [`Thread::status`]: crate::Thread::status
102 CoroutineInactive,
103 /// An [`AnyUserData`] is not the expected type in a borrow.
104 ///
105 /// This error can only happen when manually using [`AnyUserData`], or when implementing
106 /// metamethods for binary operators. Refer to the documentation of [`UserDataMethods`] for
107 /// details.
108 ///
109 /// [`AnyUserData`]: crate::AnyUserData
110 /// [`UserDataMethods`]: crate::UserDataMethods
111 UserDataTypeMismatch,
112 /// An [`AnyUserData`] borrow failed because it has been destructed.
113 ///
114 /// This error can happen either due to to being destructed in a previous __gc, or due to being
115 /// destructed from exiting a `Lua::scope` call.
116 ///
117 /// [`AnyUserData`]: crate::AnyUserData
118 UserDataDestructed,
119 /// An [`AnyUserData`] immutable borrow failed because it is already borrowed mutably.
120 ///
121 /// This error can occur when a method on a [`UserData`] type calls back into Lua, which then
122 /// tries to call a method on the same [`UserData`] type. Consider restructuring your API to
123 /// prevent these errors.
124 ///
125 /// [`AnyUserData`]: crate::AnyUserData
126 /// [`UserData`]: crate::UserData
127 UserDataBorrowError,
128 /// An [`AnyUserData`] mutable borrow failed because it is already borrowed.
129 ///
130 /// This error can occur when a method on a [`UserData`] type calls back into Lua, which then
131 /// tries to call a method on the same [`UserData`] type. Consider restructuring your API to
132 /// prevent these errors.
133 ///
134 /// [`AnyUserData`]: crate::AnyUserData
135 /// [`UserData`]: crate::UserData
136 UserDataBorrowMutError,
137 /// A [`MetaMethod`] operation is restricted (typically for `__gc` or `__metatable`).
138 ///
139 /// [`MetaMethod`]: crate::MetaMethod
140 MetaMethodRestricted(StdString),
141 /// A [`MetaMethod`] (eg. `__index` or `__newindex`) has invalid type.
142 ///
143 /// [`MetaMethod`]: crate::MetaMethod
144 MetaMethodTypeError {
145 method: StdString,
146 type_name: &'static str,
147 message: Option<StdString>,
148 },
149 /// A [`RegistryKey`] produced from a different Lua state was used.
150 ///
151 /// [`RegistryKey`]: crate::RegistryKey
152 MismatchedRegistryKey,
153 /// A Rust callback returned `Err`, raising the contained `Error` as a Lua error.
154 CallbackError {
155 /// Lua call stack backtrace.
156 traceback: StdString,
157 /// Original error returned by the Rust code.
158 cause: Arc<Error>,
159 },
160 /// A Rust panic that was previously resumed, returned again.
161 ///
162 /// This error can occur only when a Rust panic resumed previously was recovered
163 /// and returned again.
164 PreviouslyResumedPanic,
165 /// Serialization error.
166 #[cfg(feature = "serialize")]
167 #[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
168 SerializeError(StdString),
169 /// Deserialization error.
170 #[cfg(feature = "serialize")]
171 #[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
172 DeserializeError(StdString),
173 /// A custom error.
174 ///
175 /// This can be used for returning user-defined errors from callbacks.
176 ///
177 /// Returning `Err(ExternalError(...))` from a Rust callback will raise the error as a Lua
178 /// error. The Rust code that originally invoked the Lua code then receives a `CallbackError`,
179 /// from which the original error (and a stack traceback) can be recovered.
180 ExternalError(Arc<dyn StdError + Send + Sync>),
181}
182
183/// A specialized `Result` type used by `mlua`'s API.
184pub type Result<T> = StdResult<T, Error>;
185
186#[cfg(not(tarpaulin_include))]
187impl fmt::Display for Error {
188 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
189 match *self {
190 Error::SyntaxError { ref message, .. } => write!(fmt, "syntax error: {}", message),
191 Error::RuntimeError(ref msg) => write!(fmt, "runtime error: {}", msg),
192 Error::MemoryError(ref msg) => {
193 write!(fmt, "memory error: {}", msg)
194 }
195 #[cfg(any(feature = "lua53", feature = "lua52", feature = "lua-factorio"))]
196 Error::GarbageCollectorError(ref msg) => {
197 write!(fmt, "garbage collector error: {}", msg)
198 }
199 Error::SafetyError(ref msg) => {
200 write!(fmt, "safety error: {}", msg)
201 },
202 Error::MemoryLimitNotAvailable => {
203 write!(fmt, "setting memory limit is not available")
204 }
205 Error::MainThreadNotAvailable => {
206 write!(fmt, "main thread is not available in Lua 5.1")
207 }
208 Error::RecursiveMutCallback => write!(fmt, "mutable callback called recursively"),
209 Error::CallbackDestructed => write!(
210 fmt,
211 "a destructed callback or destructed userdata method was called"
212 ),
213 Error::StackError => write!(
214 fmt,
215 "out of Lua stack, too many arguments to a Lua function or too many return values from a callback"
216 ),
217 Error::BindError => write!(
218 fmt,
219 "too many arguments to Function::bind"
220 ),
221 Error::ToLuaConversionError { from, to, ref message } => {
222 write!(fmt, "error converting {} to Lua {}", from, to)?;
223 match *message {
224 None => Ok(()),
225 Some(ref message) => write!(fmt, " ({})", message),
226 }
227 }
228 Error::FromLuaConversionError { from, to, ref message } => {
229 write!(fmt, "error converting Lua {} to {}", from, to)?;
230 match *message {
231 None => Ok(()),
232 Some(ref message) => write!(fmt, " ({})", message),
233 }
234 }
235 Error::CoroutineInactive => write!(fmt, "cannot resume inactive coroutine"),
236 Error::UserDataTypeMismatch => write!(fmt, "userdata is not expected type"),
237 Error::UserDataDestructed => write!(fmt, "userdata has been destructed"),
238 Error::UserDataBorrowError => write!(fmt, "userdata already mutably borrowed"),
239 Error::UserDataBorrowMutError => write!(fmt, "userdata already borrowed"),
240 Error::MetaMethodRestricted(ref method) => write!(fmt, "metamethod {} is restricted", method),
241 Error::MetaMethodTypeError { ref method, type_name, ref message } => {
242 write!(fmt, "metamethod {} has unsupported type {}", method, type_name)?;
243 match *message {
244 None => Ok(()),
245 Some(ref message) => write!(fmt, " ({})", message),
246 }
247 }
248 Error::MismatchedRegistryKey => {
249 write!(fmt, "RegistryKey used from different Lua state")
250 }
251 Error::CallbackError { ref cause, ref traceback } => {
252 writeln!(fmt, "callback error")?;
253 // Trace errors down to the root
254 let (mut cause, mut full_traceback) = (cause, None);
255 while let Error::CallbackError { cause: ref cause2, traceback: ref traceback2 } = **cause {
256 cause = cause2;
257 full_traceback = Some(traceback2);
258 }
259 if let Some(full_traceback) = full_traceback {
260 let traceback = traceback.trim_start_matches("stack traceback:");
261 let traceback = traceback.trim_start().trim_end();
262 // Try to find local traceback within the full traceback
263 if let Some(pos) = full_traceback.find(traceback) {
264 write!(fmt, "{}", &full_traceback[..pos])?;
265 writeln!(fmt, ">{}", &full_traceback[pos..].trim_end())?;
266 } else {
267 writeln!(fmt, "{}", full_traceback.trim_end())?;
268 }
269 } else {
270 writeln!(fmt, "{}", traceback.trim_end())?;
271 }
272 write!(fmt, "caused by: {}", cause)
273 }
274 Error::PreviouslyResumedPanic => {
275 write!(fmt, "previously resumed panic returned again")
276 }
277 #[cfg(feature = "serialize")]
278 Error::SerializeError(ref err) => {
279 write!(fmt, "serialize error: {}", err)
280 },
281 #[cfg(feature = "serialize")]
282 Error::DeserializeError(ref err) => {
283 write!(fmt, "deserialize error: {}", err)
284 },
285 Error::ExternalError(ref err) => write!(fmt, "{}", err),
286 }
287 }
288}
289
290impl StdError for Error {
291 fn source(&self) -> Option<&(dyn StdError + 'static)> {
292 match *self {
293 // An error type with a source error should either return that error via source or
294 // include that source's error message in its own Display output, but never both.
295 // https://blog.rust-lang.org/inside-rust/2021/07/01/What-the-error-handling-project-group-is-working-towards.html
296 // Given that we include source to fmt::Display implementation for `CallbackError`, this call returns nothing.
297 Error::CallbackError { .. } => None,
298 Error::ExternalError(ref err) => err.source(),
299 _ => None,
300 }
301 }
302}
303
304impl Error {
305 pub fn external<T: Into<Box<dyn StdError + Send + Sync>>>(err: T) -> Error {
306 Error::ExternalError(err.into().into())
307 }
308}
309
310pub trait ExternalError {
311 fn to_lua_err(self) -> Error;
312}
313
314impl<E: Into<Box<dyn StdError + Send + Sync>>> ExternalError for E {
315 fn to_lua_err(self) -> Error {
316 Error::external(self)
317 }
318}
319
320pub trait ExternalResult<T> {
321 fn to_lua_err(self) -> Result<T>;
322}
323
324impl<T, E> ExternalResult<T> for StdResult<T, E>
325where
326 E: ExternalError,
327{
328 fn to_lua_err(self) -> Result<T> {
329 self.map_err(|e| e.to_lua_err())
330 }
331}
332
333impl std::convert::From<AddrParseError> for Error {
334 fn from(err: AddrParseError) -> Self {
335 Error::external(err)
336 }
337}
338
339impl std::convert::From<IoError> for Error {
340 fn from(err: IoError) -> Self {
341 Error::external(err)
342 }
343}
344
345impl std::convert::From<Utf8Error> for Error {
346 fn from(err: Utf8Error) -> Self {
347 Error::external(err)
348 }
349}
350
351#[cfg(feature = "serialize")]
352impl serde::ser::Error for Error {
353 fn custom<T: fmt::Display>(msg: T) -> Self {
354 Self::SerializeError(msg.to_string())
355 }
356}
357
358#[cfg(feature = "serialize")]
359impl serde::de::Error for Error {
360 fn custom<T: fmt::Display>(msg: T) -> Self {
361 Self::DeserializeError(msg.to_string())
362 }
363}