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}