summaryrefslogtreecommitdiffstats
path: root/third_party/rust/uniffi_bindgen/src/bindings/python/templates/CallbackInterfaceTemplate.py
blob: e0e926757a7febcce343cf2e5ab6bc24d5e889a3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
{%- let cbi = ci|get_callback_interface_definition(id) %}
{%- let foreign_callback = format!("foreignCallback{}", canonical_type_name) %}

{% if self.include_once_check("CallbackInterfaceRuntime.py") %}{% include "CallbackInterfaceRuntime.py" %}{% endif %}

# Declaration and _UniffiConverters for {{ type_name }} Callback Interface

class {{ type_name }}:
    {% for meth in cbi.methods() -%}
    def {{ meth.name()|fn_name }}(self, {% call py::arg_list_decl(meth) %}):
        raise NotImplementedError

    {% endfor %}

def py_{{ foreign_callback }}(handle, method, args_data, args_len, buf_ptr):
    {% for meth in cbi.methods() -%}
    {% let method_name = format!("invoke_{}", meth.name())|fn_name %}
    def {{ method_name }}(python_callback, args_stream, buf_ptr):
        {#- Unpacking args from the _UniffiRustBuffer #}
        def makeCall():
            {#- Calling the concrete callback object #}
            {%- if meth.arguments().len() != 0 -%}
            return python_callback.{{ meth.name()|fn_name }}(
                {% for arg in meth.arguments() -%}
                {{ arg|read_fn }}(args_stream)
                {%- if !loop.last %}, {% endif %}
                {% endfor -%}
            )
            {%- else %}
            return python_callback.{{ meth.name()|fn_name }}()
            {%- endif %}

        def makeCallAndHandleReturn():
            {%- match meth.return_type() %}
            {%- when Some(return_type) %}
            rval = makeCall()
            with _UniffiRustBuffer.alloc_with_builder() as builder:
                {{ return_type|write_fn }}(rval, builder)
                buf_ptr[0] = builder.finalize()
            {%- when None %}
            makeCall()
            {%- endmatch %}
            return _UNIFFI_CALLBACK_SUCCESS

        {%- match meth.throws_type() %}
        {%- when None %}
        return makeCallAndHandleReturn()
        {%- when Some(err) %}
        try:
            return makeCallAndHandleReturn()
        except {{ err|type_name }} as e:
            # Catch errors declared in the UDL file
            with _UniffiRustBuffer.alloc_with_builder() as builder:
                {{ err|write_fn }}(e, builder)
                buf_ptr[0] = builder.finalize()
            return _UNIFFI_CALLBACK_ERROR
        {%- endmatch %}

    {% endfor %}

    cb = {{ ffi_converter_name }}.lift(handle)
    if not cb:
        raise InternalError("No callback in handlemap; this is a uniffi bug")

    if method == IDX_CALLBACK_FREE:
        {{ ffi_converter_name }}.drop(handle)
        # Successfull return
        # See docs of ForeignCallback in `uniffi_core/src/ffi/foreigncallbacks.rs`
        return _UNIFFI_CALLBACK_SUCCESS

    {% for meth in cbi.methods() -%}
    {% let method_name = format!("invoke_{}", meth.name())|fn_name -%}
    if method == {{ loop.index }}:
        # Call the method and handle any errors
        # See docs of ForeignCallback in `uniffi_core/src/ffi/foreigncallbacks.rs` for details
        try:
            return {{ method_name }}(cb, _UniffiRustBufferStream(args_data, args_len), buf_ptr)
        except BaseException as e:
            # Catch unexpected errors
            try:
                # Try to serialize the exception into a String
                buf_ptr[0] = {{ Type::String.borrow()|lower_fn }}(repr(e))
            except:
                # If that fails, just give up
                pass
            return _UNIFFI_CALLBACK_UNEXPECTED_ERROR
    {% endfor %}

    # This should never happen, because an out of bounds method index won't
    # ever be used. Once we can catch errors, we should return an InternalException.
    # https://github.com/mozilla/uniffi-rs/issues/351

    # An unexpected error happened.
    # See docs of ForeignCallback in `uniffi_core/src/ffi/foreigncallbacks.rs`
    return _UNIFFI_CALLBACK_UNEXPECTED_ERROR

# We need to keep this function reference alive:
# if they get GC'd while in use then UniFFI internals could attempt to call a function
# that is in freed memory.
# That would be...uh...bad. Yeah, that's the word. Bad.
{{ foreign_callback }} = _UNIFFI_FOREIGN_CALLBACK_T(py_{{ foreign_callback }})
_rust_call(lambda err: _UniffiLib.{{ cbi.ffi_init_callback().name() }}({{ foreign_callback }}, err))

# The _UniffiConverter which transforms the Callbacks in to Handles to pass to Rust.
{{ ffi_converter_name }} = _UniffiConverterCallbackInterface({{ foreign_callback }})