Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ serde = "1.0.188"
quote = "1.0.33"
proc-macro2 = "1.0.66"
syn = "2.0.31"
tauri = { git = "https://github.com/tauri-apps/tauri" }

[dependencies]
clap.workspace = true
Expand Down
108 changes: 108 additions & 0 deletions crates/gen-guest-js/tests/streams.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
class Deserializer {
source
offset

constructor(bytes) {
this.source = bytes
this.offset = 0
}

pop() {
return this.source[this.offset++]
}

try_take_n(len) {
const out = this.source.slice(this.offset, this.offset + len)
this.offset += len
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 deserializeU64(de) {
return de_varint_big(de, 64)
}
function deserializeString(de) {
const sz = deserializeU64(de);

let bytes = de.try_take_n(Number(sz));

return __text_decoder.decode(bytes);
}
const __text_decoder = new TextDecoder('utf-8');


/**
* A function that returns a stream of strings
* @returns {Promise<string>}
*/
export async function stringStream () {
const out = []


return fetch('ipc://localhost/streams/string_stream', { 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 deserializeString(de)
})
}

20 changes: 17 additions & 3 deletions crates/gen-guest-rust/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,17 @@ impl RustWasm {
.iter()
.map(|(ident, _)| format_ident!("{}", ident));

quote! {
#sig {
::tauri_bindgen_guest_rust::invoke(#mod_ident, #ident, &(#(#param_idents),*)).await.unwrap()
if func.streaming {
quote! {
#sig {
::tauri_bindgen_guest_rust::open_stream(#mod_ident, #ident, &(#(#param_idents),*)).await.unwrap()
}
}
} else {
quote! {
#sig {
::tauri_bindgen_guest_rust::invoke(#mod_ident, #ident, &(#(#param_idents),*)).await.unwrap()
}
}
}
}
Expand Down Expand Up @@ -158,6 +166,12 @@ impl RustGenerator for RustWasm {
}
}
}

fn print_streaming_result(&self, _: &Function, inner: TokenStream) -> TokenStream {
quote! {
::tauri_bindgen_guest_rust::Streaming<#inner>
}
}
}

impl tauri_bindgen_core::Generate for RustWasm {
Expand Down
12 changes: 12 additions & 0 deletions crates/gen-guest-rust/tests/streams.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#[allow(unused_imports, unused_variables, dead_code)]
#[rustfmt::skip]
pub mod streams {
use ::tauri_bindgen_guest_rust::serde;
use ::tauri_bindgen_guest_rust::bitflags;
///A function that returns a stream of strings
pub async fn string_stream() -> ::tauri_bindgen_guest_rust::Streaming<String> {
::tauri_bindgen_guest_rust::start_stream("streams", "string_stream", &())
.await
.unwrap()
}
}
109 changes: 109 additions & 0 deletions crates/gen-guest-ts/tests/streams.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
// @ts-nocheck
class Deserializer {
source
offset

constructor(bytes) {
this.source = bytes
this.offset = 0
}

pop() {
return this.source[this.offset++]
}

try_take_n(len) {
const out = this.source.slice(this.offset, this.offset + len)
this.offset += len
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 deserializeU64(de) {
return de_varint_big(de, 64)
}
function deserializeString(de) {
const sz = deserializeU64(de);

let bytes = de.try_take_n(Number(sz));

return __text_decoder.decode(bytes);
}
const __text_decoder = new TextDecoder('utf-8');



/**
* A function that returns a stream of strings
*/
export async function stringStream () : Promise<string> {
const out = []


return fetch('ipc://localhost/streams/string_stream', { method: "POST", body: Uint8Array.from(out) })
.then(r => r.arrayBuffer())
.then(bytes => {
const de = new Deserializer(new Uint8Array(bytes))

return deserializeString(de)
}) as Promise<string>
}

21 changes: 20 additions & 1 deletion crates/gen-host/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,14 @@ impl RustGenerator for Host {
}
}
}

fn print_streaming_result(&self, func: &Function, _: TokenStream) -> TokenStream {
let ident = format_ident!("{}Stream", func.ident.to_upper_camel_case());

quote! {
Self::#ident
}
}
}

impl Host {
Expand All @@ -241,12 +249,23 @@ impl Host {
unsafe_: false,
private: true,
self_arg: Some(quote!(&self)),

func,
};

let stream = if func.streaming {
let stream_ident = format_ident!("{}Stream", func.ident.to_upper_camel_case());
let res =
self.print_function_result(func.result.as_ref().unwrap(), &BorrowMode::Owned);

Some(quote! { type #stream_ident: ::tauri_bindgen_host::Stream<Item = #res> + Send + 'static; })
} else {
None
};

let sig = self.print_function_signature(&sig, &BorrowMode::Owned, &BorrowMode::Owned);

quote! { #sig; }
quote! { #stream #sig; }
});

let sized = sized.then_some(quote!(: Sized));
Expand Down
35 changes: 35 additions & 0 deletions crates/gen-host/tests/streams.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#[allow(unused_imports, unused_variables, dead_code)]
#[rustfmt::skip]
pub mod streams {
use ::tauri_bindgen_host::serde;
use ::tauri_bindgen_host::bitflags;
pub trait Streams: Sized {
type StringStreamStream: ::tauri_bindgen_host::Stream<Item = String>
+ Send
+ 'static;
///A function that returns a stream of strings
fn string_stream(&self) -> Self::StringStreamStream;
}
pub fn add_to_router<T, U>(
router: &mut ::tauri_bindgen_host::ipc_router_wip::Router<T>,
get_cx: impl Fn(&T) -> &U + Send + Sync + 'static,
) -> Result<(), ::tauri_bindgen_host::ipc_router_wip::Error>
where
U: Streams + Send + Sync + 'static,
{
let wrapped_get_cx = ::std::sync::Arc::new(get_cx);
let get_cx = ::std::sync::Arc::clone(&wrapped_get_cx);
router
.func_wrap(
"streams",
"string_stream",
move |
ctx: ::tauri_bindgen_host::ipc_router_wip::Caller<T>,
| -> ::tauri_bindgen_host::anyhow::Result<String> {
let ctx = get_cx(ctx.data());
Ok(ctx.string_stream())
},
)?;
Ok(())
}
}
15 changes: 15 additions & 0 deletions crates/gen-markdown/tests/streams.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# streams



## Type definitions



## Functions

### Function string_stream

`func string_stream () -> string`

A function that returns a stream of strings
Loading