anyhow-tauri

Crates.ioanyhow-tauri
lib.rsanyhow-tauri
version1.0.0
sourcesrc
created_at2024-05-30 22:55:32.890365
updated_at2024-05-30 22:55:32.890365
descriptionA crate that lets you use anyhow as a command result with the tauri framework.
homepagehttps://github.com/TDiblik/anyhow-tauri
repositoryhttps://github.com/TDiblik/anyhow-tauri
max_upload_size
id1257330
size9,131
Tomáš Diblík (TDiblik)

documentation

README

Introduction

A crate that makes it easy to use the anyhow crate with the tauri framework as a command result.

How to use

Define a function that uses anyhow:

fn function_that_throws() -> anyhow::Result<()> {
    anyhow::bail!("Simulating a possible throw")
}

fn function_that_succeeds() -> anyhow::Result<String> {
    Ok("this function succeeds".to_string())
}

Then proceed to use the anyhow_tauri::TAResult<T> as a return type of your command. And that's it! Now, you can handle your errors as you would outside tauri!

#[tauri::command]
fn test() -> anyhow_tauri::TAResult<String> {
    Ok("No error thrown.".into())
}

#[tauri::command]
fn test_anyhow_success() -> anyhow_tauri::TAResult<String> {
    function_that_succeeds().into_ta_result()

    // could also be written as
    // Ok(function_that_succeeds()?)
}

#[tauri::command]
fn test_throw() -> anyhow_tauri::TAResult<String> {
    function_that_throws()?;

    Ok("this should never trigger".to_owned())
}

#[tauri::command]
fn test_pure_err_conversion() -> anyhow_tauri::TAResult<String> {
    let _some_empty_err = anyhow::anyhow!("some err").into_ta_empty_result();
    anyhow::anyhow!("Showcase of the .into_ta_result()").into_ta_result()
}

#[tauri::command]
fn test_bail() -> anyhow_tauri::TAResult<String> {
    anyhow_tauri::bail!("Showcase of the .bail!()")
}

#[tauri::command]
fn test_ensure() -> anyhow_tauri::TAResult<String> {
    anyhow_tauri::ensure!(1 == 2); // this should throw

    Ok("this should never trigger".to_owned())
}

Notice that you can casually use the ? operator. I've had to create a wrapper for the bail!() and ensure!() macros, since I was unable to implement proper traits for them (somebody can submit a PR tho). The TA before each type means TauriAnyhow because I don't want to use type names that could collide with other type names in your codebase. The whole crate is around +-100 lines of code. If you don't want to depend on another package, you should be able to copy-paste it to your codebase without any problems (I don't expect this crate to change that much).

Caveats

  • By default, sending errors to the client is disabled in production builds. To enable them, turn on the feature show_errs_in_release.
  • You have to use the anyhow_tauri::TAResult<T> as a return type of your commands. Could be a problem for some people.
  • Might lack support for some anyhow features, I didn't find any while dogfooding tho!

Credit

Initially, when I asked about using anyhow with tauri in a Tauri Working Group Office Hours call, a Discord user (I cannot find them for the love of god 😅) suggested I use the following code:

#[derive(Debug)]
pub struct CommandError(pub anyhow::Error);

impl std::error::Error for CommandError {}

impl Serialize for CommandError {
    fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        serializer.serialize_str(&format!("{:#}", self.0))
    }
}

impl From<anyhow::Error> for CommandError {
    fn from(error: anyhow::Error) -> Self {
        Self(error)
    }
}

pub type Result<T> = std::result::Result<T, CommandError>;

I've taken this code, further extended it, and added some additional features so that it's "smoother" for anyhow to work with tauri. Soooo... credit to that mysterious Discord user :/

Commit count: 7

cargo fmt