Skip to content

Commit 646630a

Browse files
Dheatly23Bromeon
authored andcommitted
Add Erased Convert Error Type
1 parent e7dd8b8 commit 646630a

File tree

6 files changed

+141
-89
lines changed

6 files changed

+141
-89
lines changed

godot-core/src/builtin/array.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -929,7 +929,7 @@ impl<T: GodotType> GodotFfiVariant for Array<T> {
929929
expected: Self::variant_type(),
930930
actual: variant.get_type(),
931931
}
932-
.into_error(variant));
932+
.into_error(variant.clone()));
933933
}
934934

935935
let array = unsafe {

godot-core/src/builtin/meta/call_error.rs

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
66
*/
77

8-
use crate::builtin::meta::{CallContext, ConvertError, ToGodot};
8+
use crate::builtin::meta::{CallContext, ConvertError, ErasedConvertError, ToGodot};
99
use crate::builtin::Variant;
1010
use crate::sys;
1111
use godot_ffi::{join_debug, VariantType};
@@ -314,7 +314,10 @@ impl CallError {
314314
function_name: call_ctx.function_name.to_string(),
315315
call_expr: format!("{call_ctx}()"),
316316
reason: reason.into(),
317-
source: source.map(|e| SourceError::Convert(Box::new(e))),
317+
source: source.map(|e| SourceError::Convert {
318+
value: e.value().map_or_else(String::new, |v| format!("{:?}", v)),
319+
erased_error: e.into(),
320+
}),
318321
}
319322
}
320323

@@ -342,7 +345,15 @@ impl CallError {
342345
// };
343346

344347
let source_str = match &self.source {
345-
Some(SourceError::Convert(e)) if with_source => format!("\n Source: {}", e),
348+
Some(SourceError::Convert {
349+
erased_error,
350+
value,
351+
}) if with_source => {
352+
format!(
353+
"\n Source: {erased_error}{}{value}",
354+
if value.is_empty() { "" } else { ": " },
355+
)
356+
}
346357
Some(SourceError::Call(e)) if with_source => format!("\n Source: {}", e.message(true)),
347358
_ => String::new(),
348359
};
@@ -360,7 +371,9 @@ impl fmt::Display for CallError {
360371
impl Error for CallError {
361372
fn source(&self) -> Option<&(dyn Error + 'static)> {
362373
match self.source.as_ref() {
363-
Some(SourceError::Convert(e)) => deref_to::<ConvertError>(e),
374+
Some(SourceError::Convert {
375+
erased_error: e, ..
376+
}) => deref_to::<ErasedConvertError>(e),
364377
Some(SourceError::Call(e)) => deref_to::<CallError>(e),
365378
None => None,
366379
}
@@ -372,7 +385,10 @@ impl Error for CallError {
372385

373386
#[derive(Debug)]
374387
enum SourceError {
375-
Convert(Box<ConvertError>),
388+
Convert {
389+
erased_error: ErasedConvertError,
390+
value: String,
391+
},
376392
Call(Box<CallError>),
377393
}
378394

godot-core/src/builtin/meta/godot_convert/convert_error.rs

Lines changed: 101 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,11 @@ use std::fmt;
1010

1111
use godot_ffi::VariantType;
1212

13-
use crate::builtin::{array_inner, meta::ClassName};
13+
use crate::builtin::{
14+
array_inner,
15+
meta::{ClassName, ToGodot},
16+
Variant,
17+
};
1418

1519
type Cause = Box<dyn Error + Send + Sync>;
1620

@@ -20,8 +24,7 @@ type Cause = Box<dyn Error + Send + Sync>;
2024
#[derive(Debug)]
2125
pub struct ConvertError {
2226
kind: ErrorKind,
23-
cause: Option<Cause>,
24-
value_str: Option<String>,
27+
value: Option<Variant>,
2528
}
2629

2730
impl ConvertError {
@@ -30,77 +33,71 @@ impl ConvertError {
3033
/// If you don't need a custom message, consider using [`ConvertError::default()`] instead.
3134
pub fn new(user_message: impl Into<String>) -> Self {
3235
Self {
33-
kind: ErrorKind::Custom(Some(user_message.into())),
34-
cause: None,
35-
value_str: None,
36+
kind: ErrorKind::Custom(Some(user_message.into().into())),
37+
..Default::default()
3638
}
3739
}
3840

3941
/// Create a new custom error for a conversion with the value that failed to convert.
4042
pub(crate) fn with_kind_value<V>(kind: ErrorKind, value: V) -> Self
4143
where
42-
V: fmt::Debug,
44+
V: ToGodot,
4345
{
4446
Self {
4547
kind,
46-
cause: None,
47-
value_str: Some(format!("{value:?}")),
48+
value: Some(value.to_variant()),
4849
}
4950
}
5051

51-
/// Create a new custom error with a rust-error as an underlying cause for the conversion error.
52-
#[doc(hidden)]
53-
pub fn with_cause<C>(cause: C) -> Self
52+
/// Create a new custom error wrapping an [`Error`].
53+
pub fn with_error<E>(error: E) -> Self
5454
where
55-
C: Into<Cause>,
55+
E: Into<Box<dyn Error + Send + Sync>>,
5656
{
5757
Self {
58-
cause: Some(cause.into()),
58+
kind: ErrorKind::Custom(Some(error.into())),
5959
..Default::default()
6060
}
6161
}
6262

63-
/// Create a new custom error with a rust-error as an underlying cause for the conversion error, and the
64-
/// value that failed to convert.
65-
#[doc(hidden)]
66-
pub fn with_cause_value<C, V>(cause: C, value: V) -> Self
63+
/// Create a new custom error wrapping an [`Error`] and the value that failed to convert.
64+
pub fn with_error_value<E, V>(error: E, value: V) -> Self
6765
where
68-
C: Into<Cause>,
69-
V: fmt::Debug,
66+
E: Into<Box<dyn Error + Send + Sync>>,
67+
V: ToGodot,
7068
{
7169
Self {
72-
cause: Some(cause.into()),
73-
value_str: Some(format!("{value:?}")),
74-
..Default::default()
70+
kind: ErrorKind::Custom(Some(error.into())),
71+
value: Some(value.to_variant()),
7572
}
7673
}
7774

7875
/// Returns the rust-error that caused this error, if one exists.
79-
pub fn cause(&self) -> Option<&(dyn Error + Send + Sync)> {
80-
self.cause.as_deref()
76+
pub fn cause(&self) -> Option<&(dyn Error + Send + Sync + 'static)> {
77+
match &self.kind {
78+
ErrorKind::Custom(Some(cause)) => Some(&**cause),
79+
_ => None,
80+
}
8181
}
8282

83-
/// Returns a string representation of the value that failed to convert, if one exists.
84-
pub fn value_str(&self) -> Option<&str> {
85-
self.value_str.as_deref()
83+
/// Returns a reference of the value that failed to convert, if one exists.
84+
pub fn value(&self) -> Option<&Variant> {
85+
self.value.as_ref()
8686
}
8787

88-
fn description(&self) -> Option<String> {
89-
self.kind.description()
88+
/// Converts error into generic error type. It is useful to send error across thread.
89+
/// Do note that some data might get lost during conversion.
90+
pub fn into_erased(self) -> impl Error + Send + Sync {
91+
ErasedConvertError::from(self)
9092
}
9193
}
9294

9395
impl fmt::Display for ConvertError {
9496
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
95-
match (self.description(), self.cause.as_ref()) {
96-
(Some(desc), Some(cause)) => write!(f, "{desc}: {cause}")?,
97-
(Some(desc), None) => write!(f, "{desc}")?,
98-
(None, Some(cause)) => write!(f, "{cause}")?,
99-
(None, None) => write!(f, "unknown error: {:?}", self.kind)?,
100-
}
97+
write!(f, "{}", self.kind)?;
10198

102-
if let Some(value) = self.value_str.as_ref() {
103-
write!(f, ": {value}")?;
99+
if let Some(value) = &self.value {
100+
write!(f, ": {value:?}")?;
104101
}
105102

106103
Ok(())
@@ -109,9 +106,7 @@ impl fmt::Display for ConvertError {
109106

110107
impl Error for ConvertError {
111108
fn source(&self) -> Option<&(dyn Error + 'static)> {
112-
self.cause
113-
.as_ref()
114-
.map(|cause| &**cause as &(dyn Error + 'static))
109+
self.cause().map(|v| v as &(dyn Error + 'static))
115110
}
116111
}
117112

@@ -122,27 +117,54 @@ impl Default for ConvertError {
122117
fn default() -> Self {
123118
Self {
124119
kind: ErrorKind::Custom(None),
125-
cause: None,
126-
value_str: None,
120+
value: None,
127121
}
128122
}
129123
}
130124

131-
#[derive(Eq, PartialEq, Debug)]
125+
/// Erased type of [`ConvertError`].
126+
#[derive(Debug)]
127+
pub(crate) struct ErasedConvertError {
128+
kind: ErrorKind,
129+
}
130+
131+
impl From<ConvertError> for ErasedConvertError {
132+
fn from(v: ConvertError) -> Self {
133+
let ConvertError { kind, .. } = v;
134+
Self { kind }
135+
}
136+
}
137+
138+
impl fmt::Display for ErasedConvertError {
139+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
140+
write!(f, "{}", self.kind)
141+
}
142+
}
143+
144+
impl Error for ErasedConvertError {
145+
fn source(&self) -> Option<&(dyn Error + 'static)> {
146+
match &self.kind {
147+
ErrorKind::Custom(Some(cause)) => Some(&**cause),
148+
_ => None,
149+
}
150+
}
151+
}
152+
153+
#[derive(Debug)]
132154
pub(crate) enum ErrorKind {
133155
FromGodot(FromGodotError),
134156
FromFfi(FromFfiError),
135157
FromVariant(FromVariantError),
136-
Custom(Option<String>),
158+
Custom(Option<Cause>),
137159
}
138160

139-
impl ErrorKind {
140-
fn description(&self) -> Option<String> {
161+
impl fmt::Display for ErrorKind {
162+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
141163
match self {
142-
Self::FromGodot(from_godot) => Some(from_godot.description()),
143-
Self::FromVariant(from_variant) => Some(from_variant.description()),
144-
Self::FromFfi(from_ffi) => Some(from_ffi.description()),
145-
Self::Custom(description) => description.clone(),
164+
Self::FromGodot(from_godot) => write!(f, "{from_godot}"),
165+
Self::FromVariant(from_variant) => write!(f, "{from_variant}"),
166+
Self::FromFfi(from_ffi) => write!(f, "{from_ffi}"),
167+
Self::Custom(cause) => write!(f, "{cause:?}"),
146168
}
147169
}
148170
}
@@ -162,23 +184,27 @@ pub(crate) enum FromGodotError {
162184
impl FromGodotError {
163185
pub fn into_error<V>(self, value: V) -> ConvertError
164186
where
165-
V: fmt::Debug,
187+
V: ToGodot,
166188
{
167189
ConvertError::with_kind_value(ErrorKind::FromGodot(self), value)
168190
}
191+
}
169192

170-
fn description(&self) -> String {
193+
impl fmt::Display for FromGodotError {
194+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
171195
match self {
172196
Self::BadArrayType { expected, actual } => {
173197
if expected.variant_type() != actual.variant_type() {
174198
return if expected.is_typed() {
175-
format!(
199+
write!(
200+
f,
176201
"expected array of type {:?}, got array of type {:?}",
177202
expected.variant_type(),
178203
actual.variant_type()
179204
)
180205
} else {
181-
format!(
206+
write!(
207+
f,
182208
"expected untyped array, got array of type {:?}",
183209
actual.variant_type()
184210
)
@@ -191,14 +217,15 @@ impl FromGodotError {
191217
"BadArrayType with expected == got, this is a gdext bug"
192218
);
193219

194-
format!(
220+
write!(
221+
f,
195222
"expected array of class {}, got array of class {}",
196223
expected.class_name(),
197224
actual.class_name()
198225
)
199226
}
200-
Self::InvalidEnum => "invalid engine enum value".into(),
201-
Self::ZeroInstanceId => "`InstanceId` cannot be 0".into(),
227+
Self::InvalidEnum => write!(f, "invalid engine enum value"),
228+
Self::ZeroInstanceId => write!(f, "`InstanceId` cannot be 0"),
202229
}
203230
}
204231
}
@@ -220,15 +247,19 @@ pub(crate) enum FromFfiError {
220247
impl FromFfiError {
221248
pub fn into_error<V>(self, value: V) -> ConvertError
222249
where
223-
V: fmt::Debug,
250+
V: ToGodot,
224251
{
225252
ConvertError::with_kind_value(ErrorKind::FromFfi(self), value)
226253
}
254+
}
227255

228-
fn description(&self) -> String {
256+
impl fmt::Display for FromFfiError {
257+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
229258
let target = match self {
230-
Self::NullRawGd => return "`Gd` cannot be null".into(),
231-
Self::WrongObjectType => return "given object cannot be cast to target type".into(),
259+
Self::NullRawGd => return write!(f, "`Gd` cannot be null"),
260+
Self::WrongObjectType => {
261+
return write!(f, "given object cannot be cast to target type")
262+
}
232263
Self::I32 => "i32",
233264
Self::I16 => "i16",
234265
Self::I8 => "i8",
@@ -237,7 +268,7 @@ impl FromFfiError {
237268
Self::U8 => "u8",
238269
};
239270

240-
format!("`{target}` cannot store the given value")
271+
write!(f, "`{target}` cannot store the given value")
241272
}
242273
}
243274

@@ -257,25 +288,27 @@ pub(crate) enum FromVariantError {
257288
impl FromVariantError {
258289
pub fn into_error<V>(self, value: V) -> ConvertError
259290
where
260-
V: fmt::Debug,
291+
V: ToGodot,
261292
{
262293
ConvertError::with_kind_value(ErrorKind::FromVariant(self), value)
263294
}
295+
}
264296

265-
fn description(&self) -> String {
297+
impl fmt::Display for FromVariantError {
298+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
266299
match self {
267300
Self::BadType { expected, actual } => {
268301
// Note: wording is the same as in CallError::failed_param_conversion_engine()
269-
format!("expected type `{expected:?}`, got `{actual:?}`")
302+
write!(f, "expected type `{expected:?}`, got `{actual:?}`")
270303
}
271304
Self::WrongClass { expected } => {
272-
format!("expected class `{expected}`")
305+
write!(f, "expected class `{expected}`")
273306
}
274307
}
275308
}
276309
}
277310

278311
fn __ensure_send_sync() {
279312
fn check<T: Send + Sync>() {}
280-
check::<ConvertError>();
313+
check::<ErasedConvertError>();
281314
}

0 commit comments

Comments
 (0)