Skip to content

Commit

Permalink
thiserror typesafe error handling example
Browse files Browse the repository at this point in the history
  • Loading branch information
oscartbeaumont committed May 9, 2024
1 parent 60fed8b commit 772a43c
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 17 deletions.
9 changes: 5 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions examples/app/src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
Expand Down
41 changes: 30 additions & 11 deletions examples/app/src-tauri/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use serde::{Deserialize, Serialize};
use specta::{Type, TypeCollection};
use tauri_specta::*;
use thiserror::Error;

/// HELLO
/// WORLD
Expand Down Expand Up @@ -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);

Expand All @@ -63,18 +87,17 @@ 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,
goodbye_world,
has_error,
nested::some_struct,
generic::<tauri::Wry>,
deprecated
deprecated,
typesafe_errors_using_thiserror
])
.events(tauri_specta::collect_events![DemoEvent, EmptyEvent])
.types(TypeCollection::default().register::<Custom>())
Expand All @@ -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)
Expand Down
13 changes: 11 additions & 2 deletions examples/app/src/bindings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,21 @@ 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 };
}
}
}

Expand All @@ -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 **/
Expand Down

0 comments on commit 772a43c

Please sign in to comment.