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
2,220 changes: 1,158 additions & 1,062 deletions Cargo.lock

Large diffs are not rendered by default.

18 changes: 9 additions & 9 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name = "tauri-specta"
description = "Completely typesafe Tauri commands"
version = "2.0.0-rc.21"
authors = ["Oscar Beaumont <[email protected]>"]
edition = "2021"
edition = "2024"
license = "MIT"
include = ["/src", "/README.md", "/LICENCE"]
repository = "https://github.com/specta-rs/tauri-specta"
Expand All @@ -20,7 +20,7 @@ rustdoc-args = ["--cfg", "docsrs"]
[features]
default = []
derive = ["dep:tauri-specta-macros"]
javascript = ["dep:specta-jsdoc"]
javascript = ["dep:specta-typescript"]
typescript = ["dep:specta-typescript"]

[lints]
Expand All @@ -30,7 +30,6 @@ workspace = true
# Public
specta = { workspace = true, features = ["function"] }
specta-typescript = { workspace = true, optional = true }
specta-jsdoc = { workspace = true, optional = true }
tauri-specta-macros = { version = "=2.0.0-rc.16", optional = true, path = "./macros" }
serde = "1"
serde_json = "1"
Expand Down Expand Up @@ -61,13 +60,14 @@ todo = { level = "warn", priority = -1 }
panic_in_result_fn = { level = "warn", priority = -1 }

[workspace.dependencies]
tauri = { version = "^2.1.1" }
tauri-build = { version = "2.0" }
tauri-plugin = { version = "2.0" }
tauri = { version = "^2.9.3" }
tauri-build = { version = "2.5" }
tauri-plugin = { version = "2.5" }
specta = { version = "=2.0.0-rc.22" }
specta-typescript = { version = "0.0.9" }
specta-jsdoc = { version = "0.0.9" }

# TODO: Remove this
[patch.crates-io]
# Waiting for release of https://github.com/tauri-apps/tauri/pull/12371
tauri = { git = "https://github.com/tauri-apps/tauri", rev = "75d56e8364fb203387bb5a5235f316dd7dfa6acd" }
specta = { path = "../specta/specta" }
specta-serde = { path = "../specta/specta-serde" }
specta-typescript = { path = "../specta/specta-typescript" }
4 changes: 1 addition & 3 deletions examples/app/src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ description = "A Tauri App"
authors = ["you"]
license = ""
repository = ""
edition = "2021"
rust-version = "1.57"
edition = "2024"
publish = false

[build-dependencies]
Expand All @@ -19,7 +18,6 @@ serde = { version = "1.0", features = ["derive"] }
tauri = { workspace = true, features = [] }
tauri-specta = { path = "../../../", features = ["derive", "typescript", "javascript"] }
specta-typescript = { workspace = true }
specta-jsdoc = { workspace = true }
tauri-plugin-os = "^2.0.0-alpha.2"
thiserror = "1"

Expand Down
8 changes: 2 additions & 6 deletions examples/app/src-tauri/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,19 +142,15 @@ fn main() {
#[cfg(debug_assertions)]
builder
.export(
Typescript::default()
.formatter(specta_typescript::formatter::prettier)
.header("/* eslint-disable */"),
Typescript::default().header("/* eslint-disable */"),
"../src/bindings.ts",
)
.expect("Failed to export typescript bindings");

#[cfg(debug_assertions)]
builder
.export(
specta_jsdoc::JSDoc::default()
.formatter(specta_typescript::formatter::prettier)
.header("/* eslint-disable */"),
specta_typescript::JSDoc::default().header("/* eslint-disable */"),
"../src/bindings-jsdoc.js",
)
.expect("Failed to export typescript bindings");
Expand Down
75 changes: 16 additions & 59 deletions examples/app/src/bindings-jsdoc.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,32 +4,15 @@
/** user-defined commands **/

export const commands = {
/**
* HELLO
* WORLD
* !!!!
* @param { string } myName
* @returns { Promise<string> }
*/
async helloWorld(myName) {
async helloWorld(myName) {
return await TAURI_INVOKE("hello_world", { myName });
},
/**
* @returns { Promise<string> }
*/
async goodbyeWorld() {
return await TAURI_INVOKE("goodbye_world");
},
/**
* @param { string } myName
* @returns { Promise<string> }
*/
async asyncHelloWorld(myName) {
return await TAURI_INVOKE("async_hello_world", { myName });
},
/**
* @returns { Promise<Result<string, number>> }
*/
async hasError() {
try {
return { status: "ok", data: await TAURI_INVOKE("has_error") };
Expand All @@ -38,28 +21,15 @@ async hasError() {
else return { status: "error", error: e };
}
},
/**
* @returns { Promise<MyStruct> }
*/
async someStruct() {
return await TAURI_INVOKE("some_struct");
},
/**
* @returns { Promise<void> }
*/
async generic() {
await TAURI_INVOKE("generic");
},
/**
* @deprecated This is a deprecated function
* @returns { Promise<void> }
*/
async deprecated() {
await TAURI_INVOKE("deprecated");
},
/**
* @returns { Promise<Result<null, MyError>> }
*/
async typesafeErrorsUsingThiserror() {
try {
return { status: "ok", data: await TAURI_INVOKE("typesafe_errors_using_thiserror") };
Expand All @@ -68,9 +38,6 @@ async typesafeErrorsUsingThiserror() {
else return { status: "error", error: e };
}
},
/**
* @returns { Promise<Result<null, MyError2>> }
*/
async typesafeErrorsUsingThiserrorWithValue() {
try {
return { status: "ok", data: await TAURI_INVOKE("typesafe_errors_using_thiserror_with_value") };
Expand All @@ -84,13 +51,7 @@ async typesafeErrorsUsingThiserrorWithValue() {
/** user-defined events **/


/**
* @type {typeof __makeEvents__<{
* emptyEvent: EmptyEvent
* myDemoEvent: DemoEvent
* }>}
*/


const __typedMakeEvents__ = __makeEvents__;

export const events = __typedMakeEvents__({
Expand All @@ -105,29 +66,25 @@ export const universalConstant = 42;
/** user-defined types **/

/**
* @typedef { string } Custom
*/

* @typedef {string} Custom
*/
/**
* @typedef { string } DemoEvent
*/

* @typedef {string} DemoEvent
*/
/**
* @typedef { null } EmptyEvent
*/

* @typedef {null} EmptyEvent
*/
/**
* @typedef { { type: "IoError" } | { type: "AnotherError"; data: string } } MyError
*/

* @typedef {{ type: "IoError" } | { type: "AnotherError"; data: string }} MyError
*/
/**
* @typedef { { type: "IoError"; data: string } } MyError2
*/

* @typedef {{ type: "IoError"; data: string }} MyError2
*/
/**
* @typedef { { some_field: string } } MyStruct
*/

* @typedef {{
* some_field: string;
* }} MyStruct
*/

/** tauri-specta globals **/

Expand Down
173 changes: 39 additions & 134 deletions examples/app/src/bindings.ts
Original file line number Diff line number Diff line change
@@ -1,139 +1,44 @@
/* eslint-disable */
// This file was generated by [tauri-specta](https://github.com/specta-rs/tauri-specta). Do not edit this file manually.

/** user-defined commands **/


export const commands = {
/**
* HELLO
* WORLD
* !!!!
*/
async helloWorld(myName: string) : Promise<string> {
return await TAURI_INVOKE("hello_world", { myName });
},
async goodbyeWorld() : Promise<string> {
return await TAURI_INVOKE("goodbye_world");
},
async asyncHelloWorld(myName: string) : Promise<string> {
return await TAURI_INVOKE("async_hello_world", { myName });
},
async hasError() : Promise<Result<string, number>> {
try {
return { status: "ok", data: await TAURI_INVOKE("has_error") };
} catch (e) {
if(e instanceof Error) throw e;
else return { status: "error", error: e as any };
}
},
async someStruct() : Promise<MyStruct> {
return await TAURI_INVOKE("some_struct");
},
async generic() : Promise<void> {
await TAURI_INVOKE("generic");
},
/**
* @deprecated This is a deprecated function
*/
async deprecated() : Promise<void> {
await TAURI_INVOKE("deprecated");
},
async typesafeErrorsUsingThiserror() : Promise<Result<null, MyError>> {
try {
return { status: "ok", data: await TAURI_INVOKE("typesafe_errors_using_thiserror") };
} catch (e) {
if(e instanceof Error) throw e;
else return { status: "error", error: e as any };
}
},
async typesafeErrorsUsingThiserrorWithValue() : Promise<Result<null, MyError2>> {
try {
return { status: "ok", data: await TAURI_INVOKE("typesafe_errors_using_thiserror_with_value") };
} catch (e) {
if(e instanceof Error) throw e;
else return { status: "error", error: e as any };
}
// This file has been generated by Tauri Specta. Do not edit this file manually.

import { invoke as __TAURI_INVOKE } from '@tauri-apps/api/core';

export const commands = {
helloWorld: () => __TAURI_INVOKE<string>("hello_world", ),
goodbyeWorld: () => __TAURI_INVOKE<string>("goodbye_world", ),
asyncHelloWorld: () => __TAURI_INVOKE<string>("async_hello_world", ),
hasError: () => typedError<string, number>(__TAURI_INVOKE("has_error", )),
someStruct: () => __TAURI_INVOKE<MyStruct>("some_struct", ),
generic: () => __TAURI_INVOKE<void>("generic", ),
deprecated: () => __TAURI_INVOKE<void>("deprecated", ),
typesafeErrorsUsingThiserror: () => typedError<null, MyError>(__TAURI_INVOKE("typesafe_errors_using_thiserror", )),
typesafeErrorsUsingThiserrorWithValue: () => typedError<null, MyError2>(__TAURI_INVOKE("typesafe_errors_using_thiserror_with_value", )),
}

/* Tauri Specta runtime */
async function typedError<T, E>(result: Promise<T>): Promise<{ Ok: T } | { Error: E }> {
return await result as any; // TODO
}

/** user-defined events **/


export const events = __makeEvents__<{
emptyEvent: EmptyEvent,
myDemoEvent: DemoEvent
}>({
emptyEvent: "empty-event",
myDemoEvent: "myDemoEvent"
})

/** user-defined constants **/

export const universalConstant = 42 as const;

/** user-defined types **/

export type Custom = string
export type DemoEvent = string
export type EmptyEvent = null
export type MyError = { type: "IoError" } | { type: "AnotherError"; data: string }
export type MyError2 = { type: "IoError"; data: string }
export type MyStruct = { some_field: string }

/** tauri-specta globals **/

import {
invoke as TAURI_INVOKE,
Channel as TAURI_CHANNEL,
} from "@tauri-apps/api/core";
import * as TAURI_API_EVENT from "@tauri-apps/api/event";
import { type Webview as __Webview__ } from "@tauri-apps/api/webview";
import { type Window as __Window__ } from "@tauri-apps/api/window";

type __EventObj__<T> = {
listen: (
cb: TAURI_API_EVENT.EventCallback<T>,
) => ReturnType<typeof TAURI_API_EVENT.listen<T>>;
once: (
cb: TAURI_API_EVENT.EventCallback<T>,
) => ReturnType<typeof TAURI_API_EVENT.once<T>>;
emit: null extends T
? (payload?: T) => ReturnType<typeof TAURI_API_EVENT.emit>
: (payload: T) => ReturnType<typeof TAURI_API_EVENT.emit>;
};

export type Result<T, E> =
| { status: "ok"; data: T }
| { status: "error"; error: E };

export function __makeEvents__<T extends Record<string, any>>(
mappings: Record<keyof T, string>,
) {
const result = {} as {
[K in keyof T]: __EventObj__<T[K]> & {
(window: __Webview__ | __Window__): __EventObj__<T[K]>;
};
};

for (const key in mappings) {
const name = mappings[key];

const base: __EventObj__<any> = {
listen: (cb) => TAURI_API_EVENT.listen(name, cb),
once: (cb) => TAURI_API_EVENT.once(name, cb),
emit: (payload: any) => TAURI_API_EVENT.emit(name, payload),
};

const withWindow = (window: __Webview__ | __Window__): __EventObj__<any> => ({
listen: (cb) => window.listen(name, cb),
once: (cb) => window.once(name, cb),
emit: (payload: any) => window.emit(name, payload),
});

result[key] = Object.assign(withWindow, base);
}

return result;
function makeEvent<T>() {
return (e: T) => {}; // TODO
}


export type Custom = string;


export type DemoEvent = string;


export type EmptyEvent = null;


export type MyError = { type: "IoError" } | { type: "AnotherError"; data: string };


export type MyError2 = { type: "IoError"; data: string };


export type MyStruct = {
some_field: string,
};
Loading
Loading