From 772a43c57682f9db7d55ffc950b587dcfc5f52b6 Mon Sep 17 00:00:00 2001 From: Oscar Beaumont Date: Thu, 9 May 2024 10:15:01 +0800 Subject: [PATCH] thiserror typesafe error handling example --- Cargo.lock | 9 ++++--- examples/app/src-tauri/Cargo.toml | 1 + examples/app/src-tauri/src/main.rs | 41 ++++++++++++++++++++++-------- examples/app/src/bindings.ts | 13 ++++++++-- 4 files changed, 47 insertions(+), 17 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ebf701a..ca5960f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3167,6 +3167,7 @@ dependencies = [ "tauri-build", "tauri-plugin-os", "tauri-specta", + "thiserror", ] [[package]] @@ -3254,18 +3255,18 @@ checksum = "8eaa81235c7058867fa8c0e7314f33dcce9c215f535d1913822a2b3f5e289f3c" [[package]] name = "thiserror" -version = "1.0.49" +version = "1.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1177e8c6d7ede7afde3585fd2513e611227efd6481bd78d2e82ba1ce16557ed4" +checksum = "579e9083ca58dd9dcf91a9923bb9054071b9ebbd800b342194c9feb0ee89fc18" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.49" +version = "1.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10712f02019e9288794769fba95cd6847df9874d49d871d062172f9dd41bc4cc" +checksum = "e2470041c06ec3ac1ab38d0356a6119054dedaea53e12fbefc0de730a1c08524" dependencies = [ "proc-macro2", "quote", diff --git a/examples/app/src-tauri/Cargo.toml b/examples/app/src-tauri/Cargo.toml index f0f8761..f18e6f2 100644 --- a/examples/app/src-tauri/Cargo.toml +++ b/examples/app/src-tauri/Cargo.toml @@ -19,6 +19,7 @@ serde = { version = "1.0", features = ["derive"] } tauri = { version = "2.0.0-beta", features = [] } tauri-specta = { path = "../../../", features = ["typescript", "javascript"] } tauri-plugin-os = "2.0.0-beta.3" +thiserror = "1.0.60" [features] default = ["custom-protocol"] diff --git a/examples/app/src-tauri/src/main.rs b/examples/app/src-tauri/src/main.rs index 72671ec..9f475a6 100644 --- a/examples/app/src-tauri/src/main.rs +++ b/examples/app/src-tauri/src/main.rs @@ -6,6 +6,7 @@ use serde::{Deserialize, Serialize}; use specta::{Type, TypeCollection}; use tauri_specta::*; +use thiserror::Error; /// HELLO /// WORLD @@ -54,6 +55,29 @@ mod nested { } } +#[derive(Error, Debug, Serialize, Type)] +pub enum MyError { + // On the frontend this variant will be "IoError" with no data. + #[error("io error: {0}")] + IoError( + #[serde(skip)] // io::Error is not `Serialize` or `Type` + #[from] + std::io::Error, + ), + // On the frontend this variant will be "AnotherError" with string data. + #[error("some other error: {0}")] + AnotherError(String), +} + +#[tauri::command] +#[specta::specta] +fn typesafe_errors_using_thiserror() -> Result<(), MyError> { + Err(MyError::IoError(std::io::Error::new( + std::io::ErrorKind::Other, + "oh no!", + ))) +} + #[derive(Serialize, Deserialize, Debug, Clone, specta::Type, tauri_specta::Event)] pub struct DemoEvent(String); @@ -63,10 +87,8 @@ pub struct EmptyEvent; #[derive(Type)] pub struct Custom(String); -// We recommend re-using the builder via a macro rather than function as the builder's -// generics can be tricky to deal with -macro_rules! specta_builder { - () => {{ +fn main() { + let (invoke_handler, register_events) = { let builder = ts::builder() .commands(tauri_specta::collect_commands![ hello_world, @@ -74,7 +96,8 @@ macro_rules! specta_builder { has_error, nested::some_struct, generic::, - deprecated + deprecated, + typesafe_errors_using_thiserror ]) .events(tauri_specta::collect_events![DemoEvent, EmptyEvent]) .types(TypeCollection::default().register::()) @@ -83,12 +106,8 @@ macro_rules! specta_builder { #[cfg(debug_assertions)] let builder = builder.path("../src/bindings.ts"); - builder - }}; -} - -fn main() { - let (invoke_handler, register_events) = specta_builder!().build().unwrap(); + builder.build().unwrap() + }; tauri::Builder::default() .invoke_handler(invoke_handler) diff --git a/examples/app/src/bindings.ts b/examples/app/src/bindings.ts index 99aed59..045fbe6 100644 --- a/examples/app/src/bindings.ts +++ b/examples/app/src/bindings.ts @@ -24,13 +24,21 @@ async someStruct() : Promise { return await TAURI_INVOKE("some_struct"); }, async generic() : Promise { - +await TAURI_INVOKE("generic"); }, /** * @deprecated This is a deprecated function */ async deprecated() : Promise { - +await TAURI_INVOKE("deprecated"); +}, +async typesafeErrorsUsingThiserror() : Promise> { +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 }; +} } } @@ -47,6 +55,7 @@ emptyEvent: "empty-event" export type Custom = string export type DemoEvent = string export type EmptyEvent = null +export type MyError = "IoError" | { AnotherError: string } export type MyStruct = { some_field: string } /** tauri-specta globals **/