Skip to content

Commit ffe2441

Browse files
committed
properly re-catch mlua errors
1 parent fb9f71b commit ffe2441

File tree

7 files changed

+78
-69
lines changed

7 files changed

+78
-69
lines changed

crates/bevy_mod_scripting_core/src/bindings/pretty_print.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,10 @@ impl ReflectReferencePrinter {
304304
}
305305
}
306306

307+
/// Alais for [`DisplayWithWorldAndDummy`] + [`std::fmt::Display`], ideally display should warn that it's not the full representation.
308+
pub trait DisplayWithWorldAndDummy: DisplayWithWorld + std::fmt::Display {}
309+
impl<T: DisplayWithWorld + std::fmt::Display> DisplayWithWorldAndDummy for T {}
310+
307311
/// For types which can't be pretty printed without world access.
308312
/// Implementors should try to print the best value they can, and never panick.
309313
pub trait DisplayWithWorld: std::fmt::Debug {
@@ -330,6 +334,8 @@ macro_rules! impl_dummy_display (
330334
};
331335
);
332336

337+
impl_dummy_display!(ReflectReference);
338+
333339
impl DisplayWithWorld for ReflectReference {
334340
fn display_with_world(&self, world: WorldGuard) -> String {
335341
ReflectReferencePrinter::new(self.clone()).pretty_print(world)
@@ -340,6 +346,8 @@ impl DisplayWithWorld for ReflectReference {
340346
}
341347
}
342348

349+
impl_dummy_display!(ReflectBaseType);
350+
343351
impl DisplayWithWorld for ReflectBaseType {
344352
fn display_with_world(&self, world: WorldGuard) -> String {
345353
let mut string = String::new();
@@ -371,6 +379,8 @@ impl DisplayWithWorld for TypeId {
371379
}
372380
}
373381

382+
impl_dummy_display!(ScriptValue);
383+
374384
impl DisplayWithWorld for ScriptValue {
375385
fn display_with_world(&self, world: WorldGuard) -> String {
376386
match self {

crates/bevy_mod_scripting_core/src/error.rs

Lines changed: 44 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -18,34 +18,22 @@ use thiserror::Error;
1818

1919
use crate::{
2020
bindings::{
21-
pretty_print::DisplayWithWorld, ReflectAllocationId, ReflectBase, ReflectBaseType,
22-
ReflectReference,
21+
pretty_print::{DisplayWithWorld, DisplayWithWorldAndDummy},
22+
ReflectAllocationId, ReflectBase, ReflectBaseType, ReflectReference,
2323
},
2424
impl_dummy_display,
2525
prelude::ScriptValue,
2626
};
2727

2828
pub type ScriptResult<T> = Result<T, ScriptError>;
2929

30-
#[derive(Error, Debug)]
31-
pub struct ScriptErrorWrapper(ScriptError);
32-
33-
impl std::fmt::Display for ScriptErrorWrapper {
34-
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
35-
write!(f, "{}", self.0)
36-
}
37-
}
38-
39-
impl From<ScriptError> for Box<dyn std::error::Error + Send + Sync + 'static> {
40-
fn from(val: ScriptError) -> Self {
41-
ScriptErrorWrapper(val).into()
42-
}
43-
}
4430
/// An error with an optional script Context
4531
#[derive(Debug, Clone, PartialEq, Reflect)]
4632
#[reflect(opaque)]
4733
pub struct ScriptError(pub Arc<ScriptErrorInner>);
4834

35+
impl std::error::Error for ScriptError {}
36+
4937
impl Deref for ScriptError {
5038
type Target = ScriptErrorInner;
5139

@@ -65,7 +53,7 @@ pub struct ScriptErrorInner {
6553
#[derive(Debug, Clone)]
6654
pub enum ErrorKind {
6755
Display(Arc<dyn std::error::Error + Send + Sync>),
68-
WithWorld(Arc<dyn DisplayWithWorld + Send + Sync>),
56+
WithWorld(Arc<dyn DisplayWithWorldAndDummy + Send + Sync>),
6957
}
7058

7159
impl DisplayWithWorld for ErrorKind {
@@ -81,7 +69,7 @@ impl Display for ErrorKind {
8169
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
8270
match self {
8371
ErrorKind::Display(e) => write!(f, "{}", e),
84-
ErrorKind::WithWorld(e) => write!(f, "{:?}", e),
72+
ErrorKind::WithWorld(e) => write!(f, "{}", e),
8573
}
8674
}
8775
}
@@ -92,31 +80,26 @@ impl PartialEq for ScriptErrorInner {
9280
}
9381
}
9482

95-
// impl<T: std::error::Error + Send + Sync + 'static> From<T> for ErrorKind {
96-
// fn from(value: T) -> Self {
97-
// ErrorKind::Display(Arc::new(value))
98-
// }
99-
// }
100-
101-
// impl<T: DisplayWithWorld + Send + Sync + 'static> From<T> for ErrorKind {
102-
// fn from(val: Box<dyn DisplayWithWorld + Send + Sync>) -> Self {
103-
// ErrorKind::WithWorld(Arc::from(val))
104-
// }
105-
// }
106-
10783
impl ScriptError {
10884
#[cfg(feature = "mlua_impls")]
10985
/// Destructures mlua error into a script error, taking care to preserve as much information as possible
11086
pub fn from_mlua_error(error: mlua::Error) -> Self {
11187
match error {
112-
mlua::Error::ExternalError(inner) => {
113-
if let Some(script_error) = inner.downcast_ref::<InteropError>() {
114-
script_error.clone().into()
88+
mlua::Error::CallbackError { traceback, cause }
89+
if matches!(cause.as_ref(), mlua::Error::ExternalError(_)) =>
90+
{
91+
let inner = cause.deref().clone();
92+
Self::from_mlua_error(inner).with_context(traceback)
93+
}
94+
e => {
95+
if let Some(inner) = e.downcast_ref::<InteropError>() {
96+
Self::new(inner.clone())
97+
} else if let Some(inner) = e.downcast_ref::<ScriptError>() {
98+
inner.clone()
11599
} else {
116-
Self::new_external(inner)
100+
Self::new_external(e)
117101
}
118102
}
119-
e => Self::new_external(e),
120103
}
121104
}
122105

@@ -128,7 +111,7 @@ impl ScriptError {
128111
}))
129112
}
130113

131-
pub fn new(reason: impl DisplayWithWorld + Send + Sync + 'static) -> Self {
114+
pub fn new(reason: impl DisplayWithWorldAndDummy + Send + Sync + 'static) -> Self {
132115
Self(Arc::new(ScriptErrorInner {
133116
script: None,
134117
reason: ErrorKind::WithWorld(Arc::new(reason)),
@@ -144,6 +127,14 @@ impl ScriptError {
144127
}))
145128
}
146129

130+
pub fn with_script<S: ToString>(self, script: S) -> Self {
131+
Self(Arc::new(ScriptErrorInner {
132+
script: Some(script.to_string()),
133+
context: self.0.context.clone(),
134+
reason: self.0.reason.clone(),
135+
}))
136+
}
137+
147138
pub fn with_appended_context<S: ToString>(self, context: S) -> Self {
148139
Self(Arc::new(ScriptErrorInner {
149140
script: self.0.script.clone(),
@@ -153,26 +144,16 @@ impl ScriptError {
153144
}
154145
}
155146

156-
// impl<T: std::error::Error + Send + Sync + 'static> From<T> for ScriptError {
157-
// fn from(value: T) -> Self {
158-
// let error_kind = ErrorKind::from(value);
159-
// Self::new_external(error_kind)
160-
// }
161-
// }
162-
163-
// impl<T: DisplayWithWorld + Send + Sync + 'static> From<T> for ScriptError {
164-
// fn from(value: T) -> Self {
165-
// let error_kind = ErrorKind::WithWorld(Arc::new(value));
166-
// Self::new_error(error_kind)
167-
// }
168-
// }
169-
170147
impl std::fmt::Display for ScriptError {
171148
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
172149
if let Some(script) = &self.0.script {
173-
write!(f, "error in script `{}`: {}", script, self.0.reason)
150+
write!(
151+
f,
152+
"error in script `{}`: {}.\n{}",
153+
script, self.0.reason, self.0.context
154+
)
174155
} else {
175-
write!(f, "error: {}", self.0.reason)
156+
write!(f, "error: {}.\n{}", self.0.reason, self.0.context)
176157
}
177158
}
178159
}
@@ -181,12 +162,17 @@ impl DisplayWithWorld for ScriptError {
181162
fn display_with_world(&self, world: crate::bindings::WorldGuard) -> String {
182163
if let Some(script) = &self.0.script {
183164
format!(
184-
"error in script `{}`: {}",
165+
"error in script `{}`: {}.\n{}",
185166
script,
186-
self.0.reason.display_with_world(world)
167+
self.0.reason.display_with_world(world),
168+
self.0.context
187169
)
188170
} else {
189-
format!("error: {}", self.0.reason.display_with_world(world))
171+
format!(
172+
"error: {}.\n{}",
173+
self.0.reason.display_with_world(world),
174+
self.0.context
175+
)
190176
}
191177
}
192178
}
@@ -350,8 +336,10 @@ impl InteropError {
350336
}
351337
}
352338

353-
#[derive(Debug)]
339+
impl_dummy_display!(InteropErrorInner);
340+
354341
/// For errors to do with reflection, type conversions or other interop issues
342+
#[derive(Debug)]
355343
pub enum InteropErrorInner {
356344
StaleWorldAccess,
357345
MissingWorld,

crates/bevy_mod_scripting_core/src/systems.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,8 @@ pub fn event_handler<L: IntoCallbackLabel, A: Args, C: Context, R: Runtime>(
187187
world,
188188
)
189189
.map_err(|e| {
190-
e.with_appended_context(format!("Script: {}, Entity: {:?}", script.id, entity))
190+
// println!("{e:?}");
191+
e.with_script(script.id.clone())
191192
});
192193

193194
push_err_and_continue!(errors, handler_result)

crates/languages/bevy_mod_scripting_lua/src/assets.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
// utils::BoxedFuture,
55
// };
66

7-
87
// use anyhow::Error;
98

109
// #[derive(Asset, TypePath, Debug)]
@@ -146,7 +145,6 @@
146145
// Ok(LuaFile { bytes })
147146
// }
148147

149-
150148
// #[cfg(feature = "teal")]
151149
// fn extensions(&self) -> &[&str] {
152150
// &["lua", "tl"]

crates/languages/bevy_mod_scripting_lua/src/bindings/reference.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ use bevy_mod_scripting_core::{
2222
ReflectAllocator, ReflectRefIter, ReflectReference, ReflectionPathExt, TypeIdSource,
2323
WorldCallbackAccess,
2424
},
25-
error::{FunctionError, ScriptError, ScriptResult, InteropError},
25+
error::{InteropError, ScriptError, ScriptResult},
2626
reflection_extensions::{PartialReflectExt, TypeIdExtensions},
2727
Either,
2828
};

crates/languages/bevy_mod_scripting_lua/src/bindings/world.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use bevy::prelude::{AppFunctionRegistry, Entity, World};
55

66
use bevy_mod_scripting_core::bindings::function::CallableWithAccess;
77
use bevy_mod_scripting_core::bindings::WorldGuard;
8-
use bevy_mod_scripting_core::error::{FunctionError, InteropError};
8+
use bevy_mod_scripting_core::error::InteropError;
99
use bevy_mod_scripting_core::{
1010
bindings::{ReflectReference, ScriptTypeRegistration, WorldAccessGuard, WorldCallbackAccess},
1111
error::ScriptError,

crates/languages/bevy_mod_scripting_lua/src/lib.rs

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -69,11 +69,17 @@ impl<A: LuaEventArg> Plugin for LuaScriptingPlugin<A> {
6969
// let reflect_reference = ReflectReference::new_allocated(entity, &mut allocator);
7070
// <Entity as LuaProxied>::Proxy::from(reflect_reference)
7171
// });
72-
context.globals().set(
73-
"entity",
74-
LuaReflectReference(<Entity>::allocate(Box::new(entity), world)),
75-
)?;
76-
context.globals().set("script_id", script_id.clone())?;
72+
context
73+
.globals()
74+
.set(
75+
"entity",
76+
LuaReflectReference(<Entity>::allocate(Box::new(entity), world)),
77+
)
78+
.map_err(ScriptError::from_mlua_error)?;
79+
context
80+
.globals()
81+
.set("script_id", script_id.clone())
82+
.map_err(ScriptError::from_mlua_error)?;
7783
// context.globals().set("entity", lua_entity)?;
7884
Ok(())
7985
});
@@ -110,7 +116,10 @@ pub fn lua_context_load(
110116
.iter()
111117
.try_for_each(|init| init(script_id, Entity::from_raw(0), context))?;
112118

113-
context.load(content).exec()?;
119+
context
120+
.load(content)
121+
.exec()
122+
.map_err(ScriptError::from_mlua_error)?;
114123
Ok(())
115124
})?;
116125

@@ -159,7 +168,9 @@ pub fn lua_handler<A: Args + for<'l> IntoLuaMulti<'l>>(
159168
Err(_) => return Ok(()),
160169
};
161170

162-
handler.call::<_, ()>(args)?;
171+
handler
172+
.call::<_, ()>(args)
173+
.map_err(ScriptError::from_mlua_error)?;
163174
Ok(())
164175
})
165176
}
@@ -173,7 +184,8 @@ pub fn with_world<F: FnOnce(&mut Lua) -> Result<(), ScriptError>>(
173184
WorldCallbackAccess::with_callback_access(world, |guard| {
174185
context
175186
.globals()
176-
.set("world", LuaReflectReference(ReflectReference::new_world()))?;
187+
.set("world", LuaReflectReference(ReflectReference::new_world()))
188+
.map_err(ScriptError::from_mlua_error)?;
177189
context.set_app_data(guard.clone());
178190
f(context)
179191
})

0 commit comments

Comments
 (0)