From 034f4ff683df6b63b8eda1fab56538e6a4c55485 Mon Sep 17 00:00:00 2001 From: Kegan Dougal <7190048+kegsay@users.noreply.github.com> Date: Wed, 20 Mar 2024 16:37:38 +0000 Subject: [PATCH] Fix several issues dealing with the error interface Generate pointers to structs consistently, rather than using the `error` interface in places. This fixes two issues: - fix an issue where nested variants did not work correctly when the nested variant was an error. - fix an issue where a callback could not be used with an error (#36) Signed-off-by: Kegan Dougal <7190048+kegsay@users.noreply.github.com> --- bindgen/src/gen_go/mod.rs | 10 +++++++++- bindgen/templates/ErrorTemplate.go | 8 ++++---- binding_tests/errors_test.go | 10 ++++++++++ fixtures/errors/src/errors.udl | 9 +++++++++ fixtures/errors/src/lib.rs | 21 +++++++++++++++++++++ 5 files changed, 53 insertions(+), 5 deletions(-) diff --git a/bindgen/src/gen_go/mod.rs b/bindgen/src/gen_go/mod.rs index 181827f..61d0766 100644 --- a/bindgen/src/gen_go/mod.rs +++ b/bindgen/src/gen_go/mod.rs @@ -370,7 +370,7 @@ impl GoCodeOracle { pub mod filters { use super::*; - fn oracle() -> &'static GoCodeOracle { + pub fn oracle() -> &'static GoCodeOracle { &GoCodeOracle } @@ -578,4 +578,12 @@ impl<'a> TypeRenderer<'a> { pub fn cgo_callback_fn(&self, name: &str, module_path: &str) -> String { format!("{module_path}_cgo_{name}") } + + pub fn field_type_name(&self, field: &Field) -> String { + let name = filters::oracle().find(&field.as_type()).type_label(); + match self.ci.is_name_used_as_error(&name) { + true => format!("*{name}"), + false => name.to_string(), + } + } } diff --git a/bindgen/templates/ErrorTemplate.go b/bindgen/templates/ErrorTemplate.go index 3a58c13..69a7e66 100644 --- a/bindgen/templates/ErrorTemplate.go +++ b/bindgen/templates/ErrorTemplate.go @@ -30,7 +30,7 @@ type {{ variant_class_name }} struct { message string {%- else %} {%- for field in variant.fields() %} - {{ field.name()|error_field_name }} {{ field|type_name}} + {{ field.name()|error_field_name }} {{ self.field_type_name(field) }} {%- endfor %} {%- endif %} } @@ -39,7 +39,7 @@ type {{ variant_class_name }} struct { func New{{ variant_class_name }}( {%- if !e.is_flat() %} {%- for field in variant.fields() %} - {{ field.name()|var_name }} {{ field|type_name}}, + {{ field.name()|var_name }} {{ self.field_type_name(field) }}, {%- endfor %} {%- endif %} ) *{{ type_name.clone() }} { @@ -81,14 +81,14 @@ type {{ e|ffi_converter_name }} struct{} var {{ e|ffi_converter_name }}INSTANCE = {{ e|ffi_converter_name }}{} func (c {{ e|ffi_converter_name }}) Lift(eb RustBufferI) error { - return LiftFromRustBuffer[error](c, eb) + return LiftFromRustBuffer[*{{ type_name|class_name }}](c, eb) } func (c {{ e|ffi_converter_name }}) Lower(value *{{ type_name|class_name }}) RustBuffer { return LowerIntoRustBuffer[*{{ type_name|class_name }}](c, value) } -func (c {{ e|ffi_converter_name }}) Read(reader io.Reader) error { +func (c {{ e|ffi_converter_name }}) Read(reader io.Reader) *{{ type_name|class_name }} { errorID := readUint32(reader) {%- if e.is_flat() %} diff --git a/binding_tests/errors_test.go b/binding_tests/errors_test.go index 478f927..f39a5ae 100644 --- a/binding_tests/errors_test.go +++ b/binding_tests/errors_test.go @@ -151,3 +151,13 @@ func TestErrorNamedError(t *testing.T) { assert.ErrorAs(t, err, &expectedError) assert.Equal(t, "it's an error", expectedError.Unwrap().(*errors.ErrorNamedErrorError).Error_) } + +func TestNestedError(t *testing.T) { + assert.Equal(t, nil, errors.TryNested(false)) + err := errors.TryNested(true) + var expectedError *errors.NestedError + assert.ErrorAs(t, err, &expectedError) + var expectedNestedError *errors.NestedErrorNested + assert.ErrorAs(t, expectedError.Unwrap(), &expectedNestedError) + assert.Equal(t, "ValidationError: UnknownError", expectedNestedError.Source.Error()) +} diff --git a/fixtures/errors/src/errors.udl b/fixtures/errors/src/errors.udl index fcbd984..dba01d3 100644 --- a/fixtures/errors/src/errors.udl +++ b/fixtures/errors/src/errors.udl @@ -19,6 +19,11 @@ enum BoobyTrapError { "HotDoorKnob", }; +[Error] +interface NestedError { + Nested(ValidationError source); +}; + [Error] interface ValidationError { InvalidUser(i32 user_id); @@ -40,6 +45,10 @@ interface ComplexError { Option(i32? id_a, i32? id_b); }; +callback interface Callback { + void do_something(BoobyTrapError error); +}; + dictionary Vec2 { double x; double y; diff --git a/fixtures/errors/src/lib.rs b/fixtures/errors/src/lib.rs index 2c60467..87b0d55 100644 --- a/fixtures/errors/src/lib.rs +++ b/fixtures/errors/src/lib.rs @@ -45,6 +45,12 @@ pub enum ComplexError { }, } +#[derive(Debug, thiserror::Error)] +pub enum NestedError { + #[error(transparent)] + Nested { source: ValidationError }, +} + #[derive(Debug)] pub struct Vec2 { x: f64, @@ -57,6 +63,21 @@ impl Vec2 { } } +#[uniffi::export] +fn try_nested(trip: bool) -> Result<(), NestedError> { + if trip { + Err(NestedError::Nested { + source: ValidationError::UnknownError, + }) + } else { + Ok(()) + } +} + +pub trait Callback { + fn do_something(&self, error: BoobyTrapError); +} + fn try_void(trip: bool) -> Result<(), BoobyTrapError> { if trip { Err(BoobyTrapError::IceSlip)