Skip to content

Commit

Permalink
WIP Add support for tagged unions.
Browse files Browse the repository at this point in the history
  • Loading branch information
rfk committed Feb 8, 2021
1 parent c5e8456 commit ac6395e
Show file tree
Hide file tree
Showing 8 changed files with 74 additions and 13 deletions.
3 changes: 1 addition & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ Things that are implemented so far:

* Primitive numeric types, equivalents to those offered by Rust (`u32`, `f64`, etc).
* Strings (which are always UTF-8, like Rust's `String`).
* C-style enums (just the discriminant, no associated data).
* Enums, including enums associated data (aka "Tagged Unions").
* C-style structs containing named fields (we call these *records*).
* Sequences of all of the above (like Rust's `Vec<T>`).
* Optional instances of all of the above (like Rust's `Option<T>`).
Expand All @@ -98,7 +98,6 @@ Things that are implemented so far:

Things that are not implemented yet:

* Enums with associated data.
* Union types.
* Efficient access to binary data (like Rust's `Vec<u8>`).
* Passing object references to functions or methods.
Expand Down
2 changes: 2 additions & 0 deletions docs/manual/src/internals/lifting_and_lowering.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ Calling this function from foreign language code involves the following steps:
| `sequence<T>` | `RustBuffer` struct pointing to serialized bytes |
| `record<string, T>` | `RustBuffer` struct pointing to serialized bytes |
| `enum` | `uint32_t` indicating variant, numbered in declaration order starting from 1 |
| `[TaggedUnion] interface` | `RustBuffer` struct pointing to serialized bytes |
| `dictionary` | `RustBuffer` struct pointing to serialized bytes |
| `interface` | `uint64_t` opaque integer handle |

Expand All @@ -85,6 +86,7 @@ The details of this format are internal only and may change between versions of
| `sequence<T>` | Serialized `i32` item count followed by serialized items; each item is a serialized `T` |
| `record<string, T>` | Serialized `i32` item count followed by serialized items; each item is a serialized `string` followed by a serialized `T` |
| `enum` | Serialized `u32` indicating variant, numbered in declaration order starting from 1 |
| `[TaggedUnion] interface` | Serialized `u32` indicating variant as per `enum`, followed by the serialized value of each field, in declaration order |
| `dictionary` | The serialized value of each field, in declaration order |
| `interface` | *Cannot currently be serialized* |

Expand Down
24 changes: 23 additions & 1 deletion docs/manual/src/udl/enumerations.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Enumerations

An enumeration defined in Rust code as

```rust
enum Animal {
Dog,
Expand All @@ -17,4 +18,25 @@ enum Animal {
};
```

Note that enumerations with associated data are not yet supported.
Enumerations with associated data require a different syntax,
due to the limitations of using WebIDL as the basis for UniFFI's interface language.
An enum like this in Rust:

```rust
enum IpAddr {
V4 {q1: u8, q2: u8, q3: u8, q4: u8},
V6 {addr: string},
}
```

Can be exposed in the UDL file with:

```idl
[TaggedUnion]
interface IpAddr {
V4(u8 q1, u8 q2, u8 q3, u8 q4);
V6(string addr);
};
```

Only enums with named fields are supported by this syntax.
9 changes: 8 additions & 1 deletion examples/rondpoint/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,13 @@ pub enum Enumeration {
Trois,
}

#[derive(Debug, Clone)]
pub enum EnumerationAvecDonnees {
Zero,
Un { premier: u32 },
Deux { premier: u32, second: String },
}

#[allow(non_camel_case_types)]
#[allow(non_snake_case)]
pub struct minusculeMAJUSCULEDict {
Expand All @@ -54,7 +61,7 @@ fn copie_enumerations(e: Vec<Enumeration>) -> Vec<Enumeration> {
e
}

fn copie_carte(e: HashMap<String, Enumeration>) -> HashMap<String, Enumeration> {
fn copie_carte(e: HashMap<String, EnumerationAvecDonnees>) -> HashMap<String, EnumerationAvecDonnees> {
e
}

Expand Down
9 changes: 8 additions & 1 deletion examples/rondpoint/src/rondpoint.udl
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ namespace rondpoint {
Dictionnaire copie_dictionnaire(Dictionnaire d);
Enumeration copie_enumeration(Enumeration e);
sequence<Enumeration> copie_enumerations(sequence<Enumeration> e);
record<DOMString, Enumeration> copie_carte(record<DOMString, Enumeration> c);
record<DOMString, EnumerationAvecDonnees> copie_carte(record<DOMString, EnumerationAvecDonnees> c);
boolean switcheroo(boolean b);
};

Expand All @@ -20,6 +20,13 @@ enum Enumeration {
"Trois",
};

[TaggedUnion]
interface EnumerationAvecDonnees {
Zero();
Un(u32 premier);
Deux(u32 premier, string second);
};

dictionary Dictionnaire {
Enumeration un;
boolean deux;
Expand Down
10 changes: 9 additions & 1 deletion examples/rondpoint/tests/bindings/test_rondpoint.kts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,15 @@ assert(dico == copyDico)

assert(copieEnumeration(Enumeration.DEUX) == Enumeration.DEUX)
assert(copieEnumerations(listOf(Enumeration.UN, Enumeration.DEUX)) == listOf(Enumeration.UN, Enumeration.DEUX))
assert(copieCarte(mapOf("1" to Enumeration.UN, "2" to Enumeration.DEUX)) == mapOf("1" to Enumeration.UN, "2" to Enumeration.DEUX))
assert(copieCarte(mapOf(
"0" to EnumerationAvecDonnees.ZERO(),
"1" to EnumerationAvecDonnees.UN(1),
"2" to EnumerationAvecDonnees.DEUX(2, "deux")
)) == mapOf(
"0" to EnumerationAvecDonnees.ZERO(),
"1" to EnumerationAvecDonnees.UN(1),
"2" to EnumerationAvecDonnees.DEUX(2, "deux")
))

assert(switcheroo(false))

Expand Down
20 changes: 14 additions & 6 deletions examples/rondpoint/tests/bindings/test_rondpoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,15 @@

assert copie_enumeration(Enumeration.DEUX) == Enumeration.DEUX
assert copie_enumerations([Enumeration.UN, Enumeration.DEUX]) == [Enumeration.UN, Enumeration.DEUX]
assert copie_carte({"1": Enumeration.UN, "2": Enumeration.DEUX}) == {"1": Enumeration.UN, "2": Enumeration.DEUX}
assert copie_carte({
"0": EnumerationAvecDonnees.ZERO(),
"1": EnumerationAvecDonnees.UN(1),
"2": EnumerationAvecDonnees.DEUX(2, "deux"),
}) == {
"0": EnumerationAvecDonnees.ZERO(),
"1": EnumerationAvecDonnees.UN(1),
"2": EnumerationAvecDonnees.DEUX(2, "deux"),
}

assert switcheroo(False) is True

Expand All @@ -19,9 +27,9 @@
rt = Retourneur()

def affirmAllerRetour(vals, identique):
for v in vals:
id_v = identique(v)
assert id_v == v, f"Round-trip failure: {v} => {id_v}"
for v in vals:
id_v = identique(v)
assert id_v == v, f"Round-trip failure: {v} => {id_v}"

MIN_I8 = -1 * 2**7
MAX_I8 = 2**7 - 1
Expand Down Expand Up @@ -87,8 +95,8 @@ def affirmAllerRetour(vals, identique):

def affirmEnchaine(vals, toString, rustyStringify=lambda v: str(v).lower()):
for v in vals:
str_v = toString(v)
assert rustyStringify(v) == str_v, f"String compare error {v} => {str_v}"
str_v = toString(v)
assert rustyStringify(v) == str_v, f"String compare error {v} => {str_v}"

# Test the efficacy of the string transport from rust. If this fails, but everything else
# works, then things are very weird.
Expand Down
10 changes: 9 additions & 1 deletion examples/rondpoint/tests/bindings/test_rondpoint.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,15 @@ assert(dico == copyDico)

assert(copieEnumeration(e: .deux) == .deux)
assert(copieEnumerations(e: [.un, .deux]) == [.un, .deux])
assert(copieCarte(c: ["1": .un, "2": .deux]) == ["1": .un, "2": .deux])
assert(copieCarte(c:
["0": .zero,
"1": .un(premier: 1),
"2": .deux(premier: 2, second: "deux")
]) == [
"0": .zero,
"1": .un(premier: 1),
"2": .deux(premier: 2, second: "deux")
])

assert(switcheroo(b: false))

Expand Down

0 comments on commit ac6395e

Please sign in to comment.