Skip to content

Commit

Permalink
Rust traits Display, Hash and Eq exposed to Kotlin and Swift. (#…
Browse files Browse the repository at this point in the history
…1817)

This brings those language to parity with Python.
  • Loading branch information
mhammond authored Nov 6, 2023
1 parent 659d5d6 commit b369e7c
Show file tree
Hide file tree
Showing 6 changed files with 110 additions and 2 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@
- The `rust_future_continuation_callback_set` FFI function was removed. `rust_future_poll` now
inputs the callback pointer. External bindings authors will need to update their code.

### What's new?

- Rust traits `Display`, `Hash` and `Eq` exposed to Kotlin and Swift.

## v0.25.0 (backend crates: v0.25.0) - (_2023-10-18_)

[All changes in v0.25.0](https://github.com/mozilla/uniffi-rs/compare/v0.24.3...v0.25.0).
Expand Down
11 changes: 11 additions & 0 deletions fixtures/trait-methods/tests/bindings/test.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import uniffi.trait_methods.*

val m = TraitMethods("yo")
assert(m.toString() == "TraitMethods(yo)")

assert(m == TraitMethods("yo"))
assert(m != TraitMethods("yoyo"))

val map = mapOf(m to 1, TraitMethods("yoyo") to 2)
assert(map[m] == 1)
assert(map[TraitMethods("yoyo")] == 2)
12 changes: 12 additions & 0 deletions fixtures/trait-methods/tests/bindings/test.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import trait_methods

let m = TraitMethods(name: "yo")
assert(String(describing: m) == "TraitMethods(yo)")
assert(String(reflecting: m) == "TraitMethods { val: \"yo\" }")

// eq
assert(m == TraitMethods(name: "yo"))

// hash
var set: Set = [TraitMethods(name: "yo")]
assert(set.contains(TraitMethods(name: "yo")))
6 changes: 5 additions & 1 deletion fixtures/trait-methods/tests/test_generated_bindings.rs
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
uniffi::build_foreign_language_testcases!("tests/bindings/test.py",);
uniffi::build_foreign_language_testcases!(
"tests/bindings/test.py",
"tests/bindings/test.kts",
"tests/bindings/test.swift"
);
31 changes: 31 additions & 0 deletions uniffi_bindgen/src/bindings/kotlin/templates/ObjectTemplate.kt
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,37 @@ class {{ impl_class_name }}(
{% endif %}
{% endfor %}

{%- for tm in obj.uniffi_traits() %}
{%- match tm %}
{%- when UniffiTrait::Display { fmt } %}
override fun toString(): String =
callWithPointer {
{%- call kt::to_ffi_call_with_prefix("it", fmt) %}
}.let {
{{ fmt.return_type().unwrap()|lift_fn }}(it)
}
{%- when UniffiTrait::Eq { eq, ne } %}
{# only equals used #}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is {{ impl_class_name}}) return false
return callWithPointer {
{%- call kt::to_ffi_call_with_prefix("it", eq) %}
}.let {
{{ eq.return_type().unwrap()|lift_fn }}(it)
}
}
{%- when UniffiTrait::Hash { hash } %}
override fun hashCode(): Int =
callWithPointer {
{%- call kt::to_ffi_call_with_prefix("it", hash) %}
}.let {
{{ hash.return_type().unwrap()|lift_fn }}(it).toInt()
}
{%- else %}
{%- endmatch %}
{%- endfor %}

{% if !obj.alternate_constructors().is_empty() -%}
companion object {
{% for cons in obj.alternate_constructors() -%}
Expand Down
48 changes: 47 additions & 1 deletion uniffi_bindgen/src/bindings/swift/templates/ObjectTemplate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,21 @@

{% include "Protocol.swift" %}

public class {{ impl_class_name }}: {{ protocol_name }} {
public class {{ impl_class_name }}:
{%- for tm in obj.uniffi_traits() %}
{%- match tm %}
{%- when UniffiTrait::Display { fmt } %}
CustomStringConvertible,
{%- when UniffiTrait::Debug { fmt } %}
CustomDebugStringConvertible,
{%- when UniffiTrait::Eq { eq, ne } %}
Equatable,
{%- when UniffiTrait::Hash { hash } %}
Hashable,
{%- else %}
{%- endmatch %}
{%- endfor %}
{{ protocol_name }} {
fileprivate let pointer: UnsafeMutableRawPointer

// TODO: We'd like this to be `private` but for Swifty reasons,
Expand Down Expand Up @@ -88,6 +102,38 @@ public class {{ impl_class_name }}: {{ protocol_name }} {
{%- endmatch -%}
{%- endif -%}
{% endfor %}

{%- for tm in obj.uniffi_traits() %}
{%- match tm %}
{%- when UniffiTrait::Display { fmt } %}
public var description: String {
return {% call swift::try(fmt) %} {{ fmt.return_type().unwrap()|lift_fn }}(
{% call swift::to_ffi_call_with_prefix("self.pointer", fmt) %}
)
}
{%- when UniffiTrait::Debug { fmt } %}
public var debugDescription: String {
return {% call swift::try(fmt) %} {{ fmt.return_type().unwrap()|lift_fn }}(
{% call swift::to_ffi_call_with_prefix("self.pointer", fmt) %}
)
}
{%- when UniffiTrait::Eq { eq, ne } %}
public static func == (lhs: {{ impl_class_name }}, other: {{ impl_class_name }}) -> Bool {
return {% call swift::try(eq) %} {{ eq.return_type().unwrap()|lift_fn }}(
{% call swift::to_ffi_call_with_prefix("lhs.pointer", eq) %}
)
}
{%- when UniffiTrait::Hash { hash } %}
public func hash(into hasher: inout Hasher) {
let val = {% call swift::try(hash) %} {{ hash.return_type().unwrap()|lift_fn }}(
{% call swift::to_ffi_call_with_prefix("self.pointer", hash) %}
)
hasher.combine(val)
}
{%- else %}
{%- endmatch %}
{%- endfor %}

}

{%- if obj.is_trait_interface() %}
Expand Down

0 comments on commit b369e7c

Please sign in to comment.