diff --git a/docs/manual/src/udl/interfaces.md b/docs/manual/src/udl/interfaces.md index 465dab869a..cf1e85eed9 100644 --- a/docs/manual/src/udl/interfaces.md +++ b/docs/manual/src/udl/interfaces.md @@ -74,6 +74,25 @@ func display(list: TodoListProtocol) { } ``` +## Static Methods + +Interface methods can be marked with the `static` keyword to mark them as belonging to the interface +itself rather than to a particular instance. Static methods are commonly used to make named alternatives to +the default constructor, like this: + +```idl +interface TodoList { + // The default constructor makes an empty list. + constructor(); + // This static method is a shortcut for making a new TodoList from a list of items. + static TodoList new_from_items(sequence) + ... +``` + +UniFFI will expose an appropriate static-method, class-method or similar in the foreign language binding, +and will connect it to the Rust method of the same name on the underlying Rust struct. + + ## Concurrent Access Since interfaces represent mutable data, uniffi has to take extra care diff --git a/examples/sprites/src/lib.rs b/examples/sprites/src/lib.rs index 3721e37a21..2b1a6b65b3 100644 --- a/examples/sprites/src/lib.rs +++ b/examples/sprites/src/lib.rs @@ -39,6 +39,12 @@ impl Sprite { } } + fn new_relative_to(reference: Point, direction: Vector) -> Sprite { + Sprite { + current_position: translate(&reference, direction), + } + } + fn get_position(&self) -> Point { self.current_position.clone() } diff --git a/examples/sprites/src/sprites.udl b/examples/sprites/src/sprites.udl index e76c33ac08..8f49f19df0 100644 --- a/examples/sprites/src/sprites.udl +++ b/examples/sprites/src/sprites.udl @@ -14,8 +14,8 @@ dictionary Vector { }; interface Sprite { - // Should be an optional, but I had to test nullable args :) constructor(Point? initial_position); + static Sprite new_relative_to(Point reference, Vector direction); Point get_position(); void move_to(Point position); void move_by(Vector direction); diff --git a/examples/sprites/tests/bindings/test_sprites.kts b/examples/sprites/tests/bindings/test_sprites.kts index a023e11ab5..42451f28dd 100644 --- a/examples/sprites/tests/bindings/test_sprites.kts +++ b/examples/sprites/tests/bindings/test_sprites.kts @@ -13,9 +13,13 @@ s.moveBy(Vector(-4.0, 2.0)) assert( s.getPosition() == Point(-3.0, 4.0) ) s.destroy() -try { +try { s.moveBy(Vector(0.0, 0.0)) assert(false) { "Should not be able to call anything after `destroy`" } } catch(e: IllegalStateException) { assert(true) -} \ No newline at end of file +} + +val srel = Sprite.newRelativeTo(Point(0.0, 1.0), Vector(1.0, 1.5)) +assert( srel.getPosition() == Point(1.0, 2.5) ) + diff --git a/examples/sprites/tests/bindings/test_sprites.py b/examples/sprites/tests/bindings/test_sprites.py index 45c130c8c9..5142c2fc42 100644 --- a/examples/sprites/tests/bindings/test_sprites.py +++ b/examples/sprites/tests/bindings/test_sprites.py @@ -11,3 +11,7 @@ s.move_by(Vector(-4, 2)) assert s.get_position() == Point(-3, 4) + +srel = Sprite.new_relative_to(Point(0, 1), Vector(1, 1.5)) +assert srel.get_position() == Point(1, 2.5) + diff --git a/examples/sprites/tests/bindings/test_sprites.swift b/examples/sprites/tests/bindings/test_sprites.swift index 8ff51ac597..d5428ac679 100644 --- a/examples/sprites/tests/bindings/test_sprites.swift +++ b/examples/sprites/tests/bindings/test_sprites.swift @@ -12,4 +12,5 @@ assert( s.getPosition() == Point(x: 1, y: 2)) s.moveBy(direction: Vector(dx: -4, dy: 2)) assert( s.getPosition() == Point(x: -3, y: 4)) - +let srel = Sprite.newRelativeTo(reference: Point(x: 0.0, y: 1.0), direction: Vector(dx: 1, dy: 1.5)) +assert( srel.getPosition() == Point(x: 1.0, y: 2.5) ) diff --git a/examples/sprites/tests/test_generated_bindings.rs b/examples/sprites/tests/test_generated_bindings.rs index d9539977de..fa4354c096 100644 --- a/examples/sprites/tests/test_generated_bindings.rs +++ b/examples/sprites/tests/test_generated_bindings.rs @@ -1,7 +1,7 @@ uniffi_macros::build_foreign_language_testcases!( "src/sprites.udl", [ - // "tests/bindings/test_sprites.py", + "tests/bindings/test_sprites.py", "tests/bindings/test_sprites.kts", "tests/bindings/test_sprites.swift", ] diff --git a/uniffi_bindgen/src/bindings/gecko_js/templates/InterfaceTemplate.cpp b/uniffi_bindgen/src/bindings/gecko_js/templates/InterfaceTemplate.cpp index f1843a15ea..398eb9f590 100644 --- a/uniffi_bindgen/src/bindings/gecko_js/templates/InterfaceTemplate.cpp +++ b/uniffi_bindgen/src/bindings/gecko_js/templates/InterfaceTemplate.cpp @@ -64,6 +64,9 @@ already_AddRefed<{{ obj.name()|class_name_cpp(context) }}> {{ obj.name()|class_n {%- endfor %} {%- for meth in obj.methods() %} +{% if meth.is_static() %} +MOZ_STATIC_ASSERT(false, "Sorry the gecko-js backend does not yet support static methods"); +{% endif %} {% match meth.cpp_return_type() %}{% when Some with (type_) %}{{ type_|ret_type_cpp(context) }}{% else %}void{% endmatch %} {{ obj.name()|class_name_cpp(context) }}::{{ meth.name()|fn_name_cpp }}( {%- for arg in meth.cpp_arguments() %} diff --git a/uniffi_bindgen/src/bindings/gecko_js/templates/WebIDLTemplate.webidl b/uniffi_bindgen/src/bindings/gecko_js/templates/WebIDLTemplate.webidl index eddd93fdec..05830dfcff 100644 --- a/uniffi_bindgen/src/bindings/gecko_js/templates/WebIDLTemplate.webidl +++ b/uniffi_bindgen/src/bindings/gecko_js/templates/WebIDLTemplate.webidl @@ -66,7 +66,7 @@ interface {{ obj.name()|class_name_webidl(context) }} { {%- if meth.throws().is_some() %} [Throws] {% endif %} - {%- match meth.webidl_return_type() -%}{%- when Some with (type_) %}{{ type_|type_webidl(context) }}{% when None %}void{% endmatch %} {{ meth.name()|fn_name_webidl }}( + {%- match meth.webidl_return_type() -%}{%- when Some with (type_) %}{{ type_|type_webidl(context) }}{% when None %}void{% endmatch %} {% if meth.is_static() %}static {% endif -%} {{ meth.name()|fn_name_webidl }}( {%- for arg in meth.arguments() %} {% if arg.optional() -%}optional{%- else -%}{%- endif %} {{ arg.webidl_type()|type_webidl(context) }} {{ arg.name() }} {%- match arg.webidl_default_value() %} diff --git a/uniffi_bindgen/src/bindings/kotlin/templates/ObjectTemplate.kt b/uniffi_bindgen/src/bindings/kotlin/templates/ObjectTemplate.kt index c2ab9a223f..84d7e0af61 100644 --- a/uniffi_bindgen/src/bindings/kotlin/templates/ObjectTemplate.kt +++ b/uniffi_bindgen/src/bindings/kotlin/templates/ObjectTemplate.kt @@ -1,10 +1,12 @@ public interface {{ obj.name()|class_name_kt }}Interface { {% for meth in obj.methods() -%} + {%- if ! meth.is_static() -%} fun {{ meth.name()|fn_name_kt }}({% call kt::arg_list_decl(meth) %}) {%- match meth.return_type() -%} {%- when Some with (return_type) %}: {{ return_type|type_kt -}} {%- else -%} - {%- endmatch %} + {%- endmatch -%} + {%- endif %} {% endfor %} } @@ -38,7 +40,9 @@ class {{ obj.name()|class_name_kt }}( } } + {# // Instance methods #} {% for meth in obj.methods() -%} + {%- if ! meth.is_static() -%} {%- match meth.return_type() -%} {%- when Some with (return_type) -%} @@ -48,12 +52,37 @@ class {{ obj.name()|class_name_kt }}( }.let { {{ "it"|lift_kt(return_type) }} } - + {%- when None -%} override fun {{ meth.name()|fn_name_kt }}({% call kt::arg_list_protocol(meth) %}) = callWithHandle { - {%- call kt::to_ffi_call_with_prefix("it", meth) %} + {%- call kt::to_ffi_call_with_prefix("it", meth) %} } {% endmatch %} + {%- endif -%} {% endfor %} + + companion object { + internal fun lift(handle: Long): {{ obj.name()|class_name_kt }} { + return {{ obj.name()|class_name_kt }}(handle) + } + + {# // Static methods, if any #} + {% for meth in obj.methods() -%} + {%- if meth.is_static() -%} + {%- match meth.return_type() -%} + + {%- when Some with (return_type) -%} + fun {{ meth.name()|fn_name_kt }}({% call kt::arg_list_decl(meth) %}): {{ return_type|type_kt }} { + val _retval = {% call kt::to_ffi_call(meth) %} + return {{ "_retval"|lift_kt(return_type) }} + } + + {%- when None -%} + fun {{ meth.name()|fn_name_kt }}({% call kt::arg_list_decl(meth) %}) = + {% call kt::to_ffi_call(meth) %} + {% endmatch %} + {%- endif -%} + {% endfor %} + } } diff --git a/uniffi_bindgen/src/bindings/python/gen_python.rs b/uniffi_bindgen/src/bindings/python/gen_python.rs index 6c086c5995..dc922879f9 100644 --- a/uniffi_bindgen/src/bindings/python/gen_python.rs +++ b/uniffi_bindgen/src/bindings/python/gen_python.rs @@ -181,7 +181,7 @@ mod filters { Type::Boolean => format!("(True if {} else False)", nm), Type::String => format!("{}.consumeIntoString()", nm), Type::Enum(name) => format!("{}({})", class_name_py(name)?, nm), - Type::Object(_) => panic!("No support for lifting objects, yet"), + Type::Object(name) => format!("liftObject({}, {})", class_name_py(name)?, nm), Type::CallbackInterface(_) => panic!("No support for lifting callback interfaces, yet"), Type::Error(_) => panic!("No support for lowering errors, yet"), Type::Record(_) | Type::Optional(_) | Type::Sequence(_) | Type::Map(_) => format!( diff --git a/uniffi_bindgen/src/bindings/python/templates/Helpers.py b/uniffi_bindgen/src/bindings/python/templates/Helpers.py new file mode 100644 index 0000000000..2b5bafd777 --- /dev/null +++ b/uniffi_bindgen/src/bindings/python/templates/Helpers.py @@ -0,0 +1,19 @@ +# Miscellaneous "private" helpers. +# +# These are things that we need to have available for internal use, +# but that we don't want to expose as part of the public API classes, +# even as "hidden" methods. + +{% if ci.iter_object_definitions().len() > 0 %} +def liftObject(cls, handle): + """Helper to create an object instace from a handle. + + This bypasses the usual __init__() logic for the given class + and just directly creates an instance with the given handle. + It's used to support factory functions and methods, which need + to return instances without invoking a (Python-level) constructor. + """ + obj = cls.__new__(cls) + obj._handle = handle + return obj +{% endif %} \ No newline at end of file diff --git a/uniffi_bindgen/src/bindings/python/templates/ObjectTemplate.py b/uniffi_bindgen/src/bindings/python/templates/ObjectTemplate.py index 930e670635..a583d0cb3d 100644 --- a/uniffi_bindgen/src/bindings/python/templates/ObjectTemplate.py +++ b/uniffi_bindgen/src/bindings/python/templates/ObjectTemplate.py @@ -14,6 +14,24 @@ def __del__(self): ) {% for meth in obj.methods() -%} + {%- if meth.is_static() -%} + {%- match meth.return_type() -%} + + {%- when Some with (return_type) -%} + @staticmethod + def {{ meth.name()|fn_name_py }}({% call py::arg_list_decl(meth) %}): + {%- call py::coerce_args_extra_indent(meth) %} + _retval = {% call py::to_ffi_call(meth) %} + return {{ "_retval"|lift_py(return_type) }} + + {%- when None -%} + @staticmethod + def {{ meth.name()|fn_name_py }}({% call py::arg_list_decl(meth) %}): + {%- call py::coerce_args_extra_indent(meth) %} + {% call py::to_ffi_call(meth) %} + {% endmatch %} + + {%- else -%} {%- match meth.return_type() -%} {%- when Some with (return_type) -%} @@ -27,4 +45,5 @@ def {{ meth.name()|fn_name_py }}(self, {% call py::arg_list_decl(meth) %}): {%- call py::coerce_args_extra_indent(meth) %} {% call py::to_ffi_call_with_prefix("self._handle", meth) %} {% endmatch %} + {% endif %} {% endfor %} diff --git a/uniffi_bindgen/src/bindings/python/templates/wrapper.py b/uniffi_bindgen/src/bindings/python/templates/wrapper.py index f462604539..cd10d00f7f 100644 --- a/uniffi_bindgen/src/bindings/python/templates/wrapper.py +++ b/uniffi_bindgen/src/bindings/python/templates/wrapper.py @@ -22,6 +22,7 @@ {% include "RustBufferTemplate.py" %} {% include "RustBufferStream.py" %} {% include "RustBufferBuilder.py" %} +{% include "Helpers.py" %} # Error definitions {% include "ErrorTemplate.py" %} diff --git a/uniffi_bindgen/src/bindings/swift/templates/ObjectTemplate.swift b/uniffi_bindgen/src/bindings/swift/templates/ObjectTemplate.swift index 672f7b23b3..427d346796 100644 --- a/uniffi_bindgen/src/bindings/swift/templates/ObjectTemplate.swift +++ b/uniffi_bindgen/src/bindings/swift/templates/ObjectTemplate.swift @@ -1,17 +1,23 @@ public protocol {{ obj.name() }}Protocol { {% for meth in obj.methods() -%} + {%- if ! meth.is_static() %} func {{ meth.name()|fn_name_swift }}({% call swift::arg_list_protocol(meth) %}) {% call swift::throws(meth) -%} {%- match meth.return_type() -%} {%- when Some with (return_type) %} -> {{ return_type|type_swift -}} {%- else -%} {%- endmatch %} + {%- endif %} {% endfor %} } public class {{ obj.name() }}: {{ obj.name() }}Protocol { private let handle: UInt64 + init(fromRawHandle handle: UInt64) { + self.handle = handle + } + {%- for cons in obj.constructors() %} public init({% call swift::arg_list_decl(cons) -%}) {% call swift::throws(cons) %} { self.handle = {% call swift::to_ffi_call(cons) %} @@ -24,8 +30,27 @@ public class {{ obj.name() }}: {{ obj.name() }}Protocol { } } - // TODO: Maybe merge the two templates (i.e the one with a return type and the one without) + static func lift(_ handle: UInt64) throws -> {{ obj.name()|class_name_swift }} { + {{ obj.name()|class_name_swift }}(fromRawHandle: handle) + } + + {# // TODO: Maybe merge the two templates (i.e the one with a return type and the one without) #} {% for meth in obj.methods() -%} + {%- if meth.is_static() %} + {%- match meth.return_type() -%} + + {%- when Some with (return_type) -%} + public static func {{ meth.name()|fn_name_swift }}({% call swift::arg_list_decl(meth) %}) {% call swift::throws(meth) %} -> {{ return_type|type_swift }} { + let _retval = {% call swift::to_ffi_call(meth) %} + return {% call swift::try(meth) %} {{ "_retval"|lift_swift(return_type) }} + } + + {%- when None -%} + public static func {{ meth.name()|fn_name_swift }}({% call swift::arg_list_decl(meth) %}) {% call swift::throws(meth) %} { + {% call swift::to_ffi_call(meth) %} + } + {%- endmatch %} + {%- else -%} {%- match meth.return_type() -%} {%- when Some with (return_type) -%} @@ -39,5 +64,6 @@ public class {{ obj.name() }}: {{ obj.name() }}Protocol { {% call swift::to_ffi_call_with_prefix("self.handle", meth) %} } {%- endmatch %} + {%- endif %} {% endfor %} } \ No newline at end of file diff --git a/uniffi_bindgen/src/interface/function.rs b/uniffi_bindgen/src/interface/function.rs index 6cf9a67e1b..f26b6532d6 100644 --- a/uniffi_bindgen/src/interface/function.rs +++ b/uniffi_bindgen/src/interface/function.rs @@ -112,9 +112,6 @@ impl APIConverter for weedle::namespace::NamespaceMember<'_> { impl APIConverter for weedle::namespace::OperationNamespaceMember<'_> { fn convert(&self, ci: &mut ComponentInterface) -> Result { let return_type = ci.resolve_return_type_expression(&self.return_type)?; - if let Some(Type::Object(_)) = return_type { - bail!("Objects cannot currently be returned from functions"); - } Ok(Function { name: match self.identifier { None => bail!("anonymous functions are not supported {:?}", self), diff --git a/uniffi_bindgen/src/interface/object.rs b/uniffi_bindgen/src/interface/object.rs index 940de7045d..581098455e 100644 --- a/uniffi_bindgen/src/interface/object.rs +++ b/uniffi_bindgen/src/interface/object.rs @@ -253,7 +253,7 @@ impl APIConverter for weedle::interface::ConstructorInterfaceMember } } -// Represents an instance method for an object type. +// Represents a static or instance method for an object type. // // The in FFI, this will be a function whose first argument is a handle for an // instance of the corresponding object type. @@ -263,6 +263,7 @@ pub struct Method { pub(super) object_name: String, pub(super) return_type: Option, pub(super) arguments: Vec, + pub(super) static_: bool, pub(super) ffi_func: FFIFunction, pub(super) attributes: MethodAttributes, } @@ -280,19 +281,27 @@ impl Method { self.return_type.as_ref() } + pub fn is_static(&self) -> bool { + self.static_ + } + pub fn ffi_func(&self) -> &FFIFunction { &self.ffi_func } - pub fn first_argument(&self) -> Argument { - Argument { - name: "handle".to_string(), - // TODO: ideally we'd get this via `ci.resolve_type_expression` so that it - // is contained in the proper `TypeUniverse`, but this works for now. - type_: Type::Object(self.object_name.clone()), - by_ref: false, - optional: false, - default: None, + pub fn first_argument(&self) -> Option { + if self.static_ { + None + } else { + Some(Argument { + name: "handle".to_string(), + // TODO: ideally we'd get this via `ci.resolve_type_expression` so that it + // is contained in the proper `TypeUniverse`, but this works for now. + type_: Type::Object(self.object_name.clone()), + by_ref: false, + optional: false, + default: None, + }) } } @@ -306,7 +315,8 @@ impl Method { self.ffi_func.name.push_str(obj_prefix); self.ffi_func.name.push_str("_"); self.ffi_func.name.push_str(&self.name); - self.ffi_func.arguments = vec![self.first_argument()] + self.ffi_func.arguments = self + .first_argument() .iter() .chain(self.arguments.iter()) .map(Into::into) @@ -337,22 +347,30 @@ impl APIConverter for weedle::interface::OperationInterfaceMember<'_> { if self.special.is_some() { bail!("special operations not supported"); } - if let Some(weedle::interface::StringifierOrStatic::Stringifier(_)) = self.modifier { - bail!("stringifiers are not supported"); - } + let static_ = match self.modifier { + None => false, + Some(weedle::interface::StringifierOrStatic::Static(_)) => true, + Some(weedle::interface::StringifierOrStatic::Stringifier(_)) => { + bail!("stringifiers are not supported") + } + }; let return_type = ci.resolve_return_type_expression(&self.return_type)?; - if let Some(Type::Object(_)) = return_type { - bail!("Objects cannot currently be returned from functions"); - } Ok(Method { name: match self.identifier { None => bail!("anonymous methods are not supported {:?}", self), - Some(id) => id.0.to_string(), + Some(id) => { + let name = id.0.to_string(); + if name == "new" { + bail!("the method name \"new\" is reserved for the default constructor"); + } + name + } }, // We don't know the name of the containing `Object` at this point, fill it in later. object_name: Default::default(), arguments: self.args.body.list.convert(ci)?, return_type, + static_, ffi_func: Default::default(), attributes: match &self.attributes { Some(attr) => MethodAttributes::try_from(attr)?, @@ -413,4 +431,36 @@ mod test { Ok(()) } + + #[test] + fn test_static_vs_instance_methods() -> Result<()> { + const UDL: &str = r#" + namespace test{}; + interface Testing { + u32 instance_method(); + static u32 static_method(); + }; + "#; + let ci = ComponentInterface::from_webidl(UDL).unwrap(); + assert_eq!(ci.iter_object_definitions().len(), 1); + + let obj = ci.get_object_definition("Testing").unwrap(); + assert_eq!(obj.methods().len(), 2); + + let method = obj.methods()[0]; + assert_eq!(method.name(), "instance_method"); + assert!(!method.is_static()); + assert_eq!(method.arguments.len(), 0); + assert!(method.first_argument().is_some()); + assert_eq!(method.ffi_func.arguments.len(), 1); + + let method = obj.methods()[1]; + assert_eq!(method.name(), "static_method"); + assert!(method.is_static()); + assert_eq!(method.arguments.len(), 0); + assert!(method.first_argument().is_none()); + assert_eq!(method.ffi_func.arguments.len(), 0); + + Ok(()) + } } diff --git a/uniffi_bindgen/src/templates/ObjectTemplate.rs b/uniffi_bindgen/src/templates/ObjectTemplate.rs index a8664adb54..94773094e9 100644 --- a/uniffi_bindgen/src/templates/ObjectTemplate.rs +++ b/uniffi_bindgen/src/templates/ObjectTemplate.rs @@ -48,6 +48,42 @@ uniffi::deps::lazy_static::lazy_static! { uniffi::deps::log::debug!("{{ meth.ffi_func().name() }}"); // If the method does not have the same signature as declared in the UDL, then // this attempt to call it will fail with a (somewhat) helpful compiler error. + {% if meth.is_static() %} + {% call rs::to_rs_static_method_call(obj, meth) %} + {% else %} {% call rs::to_rs_method_call(obj, meth) %} + {% endif %} } {% endfor %} + +// We proide a `ViaFfi` impl for each object, but the only thing it can currently +// do is lower an owned instance into a handle. This is useful for returning new +// instances of the object from static methods, and we know it is safe and sound +// because Rust's ownership system ensures that: +// * the thing we're operating on is an owned instance (not a reference) and +// so we know that nothing else in the program is looking at it. +// * the ownership is irrevokably transferred to the handlemap, and hence to +// the foreign language code, which takes responsibility for it from there. +// +// Other operations are not yet supported, since they would involve much more +// complicated semantics around references. +unsafe impl uniffi::ViaFfi for {{ obj.name() }} { + type FfiType = u64; + + fn lower(self) -> Self::FfiType { + // Note that this consumes `self`, transferring ownership to the handlemap. + {{ handle_map }}.insert(self).into_u64() + } + + fn try_lift(_v: Self::FfiType) -> uniffi::deps::anyhow::Result { + uniffi::deps::anyhow::bail!("Lifting object types is not yet supported"); + } + + fn write(&self, _buf: &mut B) { + panic!("Writing object types is not yet supported"); + } + + fn try_read(_buf: &mut B) -> uniffi::deps::anyhow::Result { + uniffi::deps::anyhow::bail!("Reading object types is not yet supported"); + } +} \ No newline at end of file diff --git a/uniffi_bindgen/src/templates/macros.rs b/uniffi_bindgen/src/templates/macros.rs index f708963801..0a9ab24517 100644 --- a/uniffi_bindgen/src/templates/macros.rs +++ b/uniffi_bindgen/src/templates/macros.rs @@ -65,18 +65,33 @@ use uniffi::UniffiMethodCall; {%- endif -%} {% match meth.throws() -%} {% when Some with (e) -%} -{{ this_handle_map }}.method_call_with_result(err, {{ meth.first_argument().name() }}, |obj| -> Result<{% call return_type_func(meth) %}, {{e}}> { +{{ this_handle_map }}.method_call_with_result(err, {{ meth.first_argument().unwrap().name() }}, |obj| -> Result<{% call return_type_func(meth) %}, {{e}}> { let _retval = {{ obj.name() }}::{%- call to_rs_call_with_prefix("obj", meth) -%}?; Ok({% call ret(meth) %}) }) {% else -%} -{{ this_handle_map }}.method_call_with_output(err, {{ meth.first_argument().name() }}, |obj| { +{{ this_handle_map }}.method_call_with_output(err, {{ meth.first_argument().unwrap().name() }}, |obj| { let _retval = {{ obj.name() }}::{%- call to_rs_call_with_prefix("obj", meth) -%}; {% call ret(meth) %} }) {% endmatch -%} {% endmacro -%} +{% macro to_rs_static_method_call(obj, meth) -%} +{% match meth.throws() -%} +{% when Some with (e) -%} +uniffi::deps::ffi_support::call_with_result(err, || -> Result<{% call return_type_func(meth) %}, {{e}}> { + let _retval = {{ obj.name() }}::{% call to_rs_call(meth) %}?; + Ok({% call ret(meth) %}) +}) +{% else -%} +uniffi::deps::ffi_support::call_with_output(err, || { + let _retval = {{ obj.name() }}::{% call to_rs_call(meth) %}; + {% call ret(meth) %} +}) +{% endmatch -%} +{% endmacro -%} + {% macro to_rs_function_call(func) %} {% match func.throws() %} {% when Some with (e) %}