Skip to content

Commit

Permalink
Merge pull request #189 from tauri-apps/feat/resource
Browse files Browse the repository at this point in the history
feat: add resource type
  • Loading branch information
JonasKruckenberg authored Sep 14, 2023
2 parents 0cd46db + dd85e41 commit 4fc443e
Show file tree
Hide file tree
Showing 17 changed files with 852 additions and 157 deletions.
5 changes: 4 additions & 1 deletion crates/core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,10 @@ pub struct TypeInfos {

impl TypeInfos {
#[must_use]
pub fn collect_from_functions(typedefs: &TypeDefArena, functions: &[Function]) -> Self {
pub fn collect_from_functions<'a>(
typedefs: &TypeDefArena,
functions: impl Iterator<Item = &'a Function>,
) -> Self {
let mut this = Self::default();

for func in functions {
Expand Down
55 changes: 48 additions & 7 deletions crates/gen-guest-js/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,22 @@ pub struct Builder {

impl GeneratorBuilder for Builder {
fn build(self, interface: Interface) -> Box<dyn Generate> {
let infos = TypeInfos::collect_from_functions(&interface.typedefs, &interface.functions);
let methods = interface
.typedefs
.iter()
.filter_map(|(_, typedef)| {
if let TypeDefKind::Resource(methods) = &typedef.kind {
Some(methods.iter())
} else {
None
}
})
.flatten();

let infos = TypeInfos::collect_from_functions(
&interface.typedefs,
interface.functions.iter().chain(methods),
);

let serde_utils =
SerdeUtils::collect_from_functions(&interface.typedefs, &interface.functions);
Expand Down Expand Up @@ -80,6 +95,7 @@ export async function {ident} ({params}) {{

fn print_resource(
&self,
mod_ident: &str,
docs: &str,
ident: &str,
functions: &[Function],
Expand All @@ -91,13 +107,32 @@ export async function {ident} ({params}) {{
.iter()
.map(|func| {
let docs = self.print_docs(func);
let mod_ident = mod_ident.to_snake_case();
let resource_ident = ident.to_snake_case();
let ident = func.ident.to_lower_camel_case();
let params = print_function_params(&func.params);

let deserialize_result = func
.result
.as_ref()
.map(|res| self.print_deserialize_function_result(res))
.unwrap_or_default();

let serialize_params = func
.params
.iter()
.map(|(ident, ty)| self.print_serialize_ty(&ident.to_lower_camel_case(), ty))
.collect::<Vec<_>>()
.join(";\n");

format!(
r#"
{docs}
r#"{docs}
async {ident} ({params}) {{
const out = []
serializeU32(out, this.#id);
{serialize_params}
await fetch('ipc://localhost/{mod_ident}::resource::{resource_ident}/{ident}', {{ method: "POST", body: Uint8Array.from(out), headers: {{ 'Content-Type': 'application/octet-stream' }} }}){deserialize_result}
}}
"#
)
Expand All @@ -106,9 +141,9 @@ async {ident} ({params}) {{

let deserialize = if info.contains(TypeInfo::RESULT) {
format!(
"deserialize(de) {{
"static deserialize(de) {{
const self = new {ident}();
self.#id = deserializeU64(de);
self.#id = deserializeU32(de);
return self
}}"
)
Expand All @@ -117,7 +152,7 @@ async {ident} ({params}) {{
};

format!(
"{docs}\nclass {ident} {{
"{docs}\nexport class {ident} {{
#id;
{functions}
{deserialize}
Expand Down Expand Up @@ -292,7 +327,13 @@ impl Generate for JavaScript {
.filter_map(|(id, typedef)| {
let info = self.infos[id];
if let TypeDefKind::Resource(functions) = &typedef.kind {
Some(self.print_resource(&typedef.docs, &typedef.ident, functions, info))
Some(self.print_resource(
&self.interface.ident,
&typedef.docs,
&typedef.ident,
functions,
info,
))
} else {
None
}
Expand Down
21 changes: 14 additions & 7 deletions crates/gen-guest-js/tests/lists.js
Original file line number Diff line number Diff line change
Expand Up @@ -335,35 +335,42 @@ serializeS64(out, val.c4)
}function serializeOtherVariant(out, val) {
if (val.A) {
serializeU32(out, 0);
return

return
}
if (val.B) {
serializeU32(out, 1);
return serializeU32(out, val.B)
serializeU32(out, val.B)
return
}
if (val.C) {
serializeU32(out, 2);
return serializeString(out, val.C)
serializeString(out, val.C)
return
}


throw new Error("unknown variant case")
}function serializeSomeVariant(out, val) {
if (val.A) {
serializeU32(out, 0);
return serializeString(out, val.A)
serializeString(out, val.A)
return
}
if (val.B) {
serializeU32(out, 1);
return

return
}
if (val.C) {
serializeU32(out, 2);
return serializeU32(out, val.C)
serializeU32(out, val.C)
return
}
if (val.D) {
serializeU32(out, 3);
return serializeList(out, (out, v) => serializeOtherVariant(out, v), val.D)
serializeList(out, (out, v) => serializeOtherVariant(out, v), val.D)
return
}


Expand Down
133 changes: 119 additions & 14 deletions crates/gen-guest-js/tests/resources.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,68 @@ class Deserializer {
return out
}
}
// function varint_max(bits) {
// const BITS_PER_BYTE = 8;
// const BITS_PER_VARINT_BYTE = 7;

// const roundup_bits = bits + (BITS_PER_BYTE - 1);

// return Math.floor(roundup_bits / BITS_PER_VARINT_BYTE);
// }

const varint_max = {
16: 3,
32: 5,
64: 10,
128: 19
}
function max_of_last_byte(type) {
let extra_bits = type % 7;
return (1 << extra_bits) - 1;
}

function de_varint(de, bits) {
let out = 0;

for (let i = 0; i < varint_max[bits]; i++) {
const val = de.pop();
const carry = val & 0x7F;
out |= carry << (7 * i);

if ((val & 0x80) === 0) {
if (i === varint_max[bits] - 1 && val > max_of_last_byte(bits)) {
throw new Error('deserialize bad variant')
} else {
return out
}
}
}

throw new Error('deserialize bad variant')
}

function de_varint_big(de, bits) {
let out = 0n;

for (let i = 0; i < varint_max[bits]; i++) {
const val = de.pop();
const carry = BigInt(val) & 0x7Fn;
out |= carry << (7n * BigInt(i));

if ((val & 0x80) === 0) {
if (i === varint_max[bits] - 1 && val > max_of_last_byte(bits)) {
throw new Error('deserialize bad variant')
} else {
return out
}
}
}

throw new Error('deserialize bad variant')
}
function deserializeU32(de) {
return de_varint(de, 32)
}


/**
Expand Down Expand Up @@ -52,59 +114,102 @@ export async function constructorB () {
}


class A {
export class A {
#id;

/**
/**
*/
async f1 () {
}
const out = []
serializeU32(out, this.#id);


await fetch('ipc://localhost/resources::resource::a/f1', { method: "POST", body: Uint8Array.from(out), headers: { 'Content-Type': 'application/octet-stream' } })
}
/**
* @param {number} a
*/
async f2 (a) {
}
const out = []
serializeU32(out, this.#id);
serializeU32(out, a)

await fetch('ipc://localhost/resources::resource::a/f2', { method: "POST", body: Uint8Array.from(out), headers: { 'Content-Type': 'application/octet-stream' } })
}
/**
* @param {number} a
* @param {number} b
*/
async f3 (a, b) {
const out = []
serializeU32(out, this.#id);
serializeU32(out, a);
serializeU32(out, b)

await fetch('ipc://localhost/resources::resource::a/f3', { method: "POST", body: Uint8Array.from(out), headers: { 'Content-Type': 'application/octet-stream' } })
}

deserialize(de) {
static deserialize(de) {
const self = new A();
self.#id = deserializeU64(de);
self.#id = deserializeU32(de);
return self
}
}
class B {
export class B {
#id;

/**
/**
* @returns {Promise<A>}
*/
async f1 () {
}
const out = []
serializeU32(out, this.#id);


await fetch('ipc://localhost/resources::resource::b/f1', { method: "POST", body: Uint8Array.from(out), headers: { 'Content-Type': 'application/octet-stream' } })
.then(r => r.arrayBuffer())
.then(bytes => {
const de = new Deserializer(new Uint8Array(bytes))

return A.deserialize(de)
})
}
/**
* @param {A} x
* @returns {Promise<Result<number, _>>}
*/
async f2 (x) {
}
const out = []
serializeU32(out, this.#id);
x.serialize(out)

await fetch('ipc://localhost/resources::resource::b/f2', { method: "POST", body: Uint8Array.from(out), headers: { 'Content-Type': 'application/octet-stream' } })
.then(r => r.arrayBuffer())
.then(bytes => {
const de = new Deserializer(new Uint8Array(bytes))

return deserializeResult(de, (de) => deserializeU32(de), () => {})
})
}
/**
* @param {A[] | null} x
* @returns {Promise<Result<A, _>>}
*/
async f3 (x) {
const out = []
serializeU32(out, this.#id);
serializeOption(out, (out, v) => serializeList(out, (out, v) => v.serialize(out), v), x)

await fetch('ipc://localhost/resources::resource::b/f3', { method: "POST", body: Uint8Array.from(out), headers: { 'Content-Type': 'application/octet-stream' } })
.then(r => r.arrayBuffer())
.then(bytes => {
const de = new Deserializer(new Uint8Array(bytes))

return deserializeResult(de, (de) => A.deserialize(de), () => {})
})
}

deserialize(de) {
static deserialize(de) {
const self = new B();
self.#id = deserializeU64(de);
self.#id = deserializeU32(de);
return self
}
}
Loading

0 comments on commit 4fc443e

Please sign in to comment.