Revision control

Copy as Markdown

Other Tools

class RustCallStatus < FFI::Struct
layout :code, :int8,
:error_buf, RustBuffer
def code
self[:code]
end
def error_buf
self[:error_buf]
end
def to_s
"RustCallStatus(code=#{self[:code]})"
end
end
# These match the values from the uniffi::rustcalls module
CALL_SUCCESS = 0
CALL_ERROR = 1
CALL_PANIC = 2
{%- for e in ci.enum_definitions() %}
{% if ci.is_name_used_as_error(e.name()) %}
{% if e.is_flat() %}
class {{ e.name()|class_name_rb }}
{%- for variant in e.variants() %}
{{ variant.name()|class_name_rb }} = Class.new StandardError
{%- endfor %}
{% else %}
module {{ e.name()|class_name_rb }}
{%- for variant in e.variants() %}
class {{ variant.name()|class_name_rb }} < StandardError
def initialize({% for field in variant.fields() %}{{ field.name()|var_name_rb }}{% if !loop.last %}, {% endif %}{% endfor %})
{%- for field in variant.fields() %}
@{{ field.name()|var_name_rb }} = {{ field.name()|var_name_rb }}
{%- endfor %}
super()
end
{%- if variant.has_fields() %}
attr_reader {% for field in variant.fields() %}:{{ field.name()|var_name_rb }}{% if !loop.last %}, {% endif %}{% endfor %}
{% endif %}
def to_s
"#{self.class.name}({% for field in variant.fields() %}{{ field.name()|var_name_rb }}=#{@{{ field.name()|var_name_rb }}.inspect}{% if !loop.last %}, {% endif %}{% endfor %})"
end
end
{%- endfor %}
{% endif %}
end
{% endif %}
{%- endfor %}
# Map error modules to the RustBuffer method name that reads them
ERROR_MODULE_TO_READER_METHOD = {
{%- for e in ci.enum_definitions() %}
{% if ci.is_name_used_as_error(e.name()) %}
{%- let typ=ci.get_type(e.name()).unwrap() %}
{%- let canonical_type_name = canonical_name(typ.borrow()).borrow()|class_name_rb %}
{{ e.name()|class_name_rb }} => :read{{ canonical_type_name }},
{% endif %}
{%- endfor %}
}
private_constant :ERROR_MODULE_TO_READER_METHOD, :CALL_SUCCESS, :CALL_ERROR, :CALL_PANIC,
:RustCallStatus
def self.consume_buffer_into_error(error_module, rust_buffer)
rust_buffer.consumeWithStream do |stream|
reader_method = ERROR_MODULE_TO_READER_METHOD[error_module]
return stream.send(reader_method)
end
end
class InternalError < StandardError
end
def self.rust_call(fn_name, *args)
# Call a rust function
rust_call_with_error(nil, fn_name, *args)
end
def self.rust_call_with_error(error_module, fn_name, *args)
# Call a rust function and handle errors
#
# Use this when the rust function returns a Result<>. error_module must be the error_module that corresponds to that Result.
# Note: RustCallStatus.new zeroes out the struct, which is exactly what we
# want to pass to Rust (code=0, error_buf=RustBuffer(len=0, capacity=0,
# data=NULL))
status = RustCallStatus.new
args << status
result = UniFFILib.public_send(fn_name, *args)
case status.code
when CALL_SUCCESS
result
when CALL_ERROR
if error_module.nil?
status.error_buf.free
raise InternalError, "CALL_ERROR with no error_module set"
else
raise consume_buffer_into_error(error_module, status.error_buf)
end
when CALL_PANIC
# When the rust code sees a panic, it tries to construct a RustBuffer
# with the message. But if that code panics, then it just sends back
# an empty buffer.
if status.error_buf.len > 0
raise InternalError, status.error_buf.consumeIntoString()
else
raise InternalError, "Rust panic"
end
else
raise InternalError, "Unknown call status: #{status.code}"
end
end
private_class_method :consume_buffer_into_error