Skip to main content

Crate tauri_typed_ipc

Crate tauri_typed_ipc 

Source
Expand description

Trait-based, type-safe Tauri IPC, built on specta v2. Sync by default.

Define your IPC surface once as a Rust trait. procedures flattens it into a dispatch core and a descriptor the TypeScript client is rendered from; handler mounts that on tauri’s invoke pipeline. The wire is identical to a raw #[tauri::command] – same command name, same named-argument JSON, no envelope – so generated calls, hand-written invokes, and raw commands are interchangeable, and a trait can be adopted one command at a time through handler_with_fallback.

A procedure is a plain fn by default, dispatched inline on the main thread; mark one async fn and only that one is spawned on tauri’s runtime. Sync-first is the founding choice: a sync handler carries no Send bound on its own logic and skips the executor hop, where TauRPC makes every procedure async. (A sync handler still runs on the UI thread, so long-running work opts into async.)

§Quickstart

Define the surface and implement it:

use tauri_typed_ipc::procedures;

#[procedures]
trait Greeter {
    fn greet(&self, name: String) -> String;
}

struct Backend;

impl Greeter for Backend {
    fn greet(&self, name: String) -> String {
        format!("Hello, {name}!")
    }
}

// `#[procedures]` adds `into_procedures`, which type-erases the trait
// for the handler:
let procedures = Backend.into_procedures();
assert_eq!(procedures.names(), &["greet"]);

Mount every procedure on one handler. handler adapts the type-erased procedures into a tauri invoke handler; drop it into your Builder, then run as usual with .run(tauri::generate_context!()):

fn mount<R: tauri::Runtime>(builder: tauri::Builder<R>) -> tauri::Builder<R> {
    builder.invoke_handler(tauri_typed_ipc::handler(Backend.into_procedures()))
}

With the export feature, render the matching client and guard it against drift:

tauri_typed_ipc::Bindings::new()
    .register::<GreeterProcedures>()
    .export_to("../src/bindings.ts")?;
import { greeter } from "./bindings";
const hello = await greeter.greet("world"); // Promise<string>

§What’s here

  • Procedures – procedures on a trait: a sync fn dispatches inline, an async fn is spawned on tauri’s runtime.
  • Typed client – with the export feature, Bindings renders a TypeScript client from the generated ProcedureSet descriptor, and its check fails CI when the committed client drifts.
  • Events, both directions – Event on an enum emits typed payloads and generates the matching listeners.
  • Typed errors – Error: a Result<_, E> procedure rejects E on the wire, and the client types its catch against it.
  • Injection by type – an AppHandle or tauri::State parameter is resolved from the [Context] by its type, never by its name.
  • Streaming – a Channel<T> parameter streams values back to the caller, on top of the value the procedure returns.
  • Composition – Procedures::merge mounts several traits on one handler.

Structs§

Bindings
Renders a TypeScript client for one or more procedure sets: a const object per set whose methods are wire-parity invoke calls, plus the specta type definitions they reference. One file by default; Layout::Files splits them into a directory.
Channel
A typed, one-way channel a procedure streams values through.
Procedures
A type-erased procedure set: the (names, dispatch) pair that #[procedures] flattens a trait into, so routing needs no knowledge of the trait itself. merge combines several sets into one, so a whole app mounts on a single handler.
ValidationError
A payload that failed its command’s contract. Carried into DispatchError::Invalid by a validating handler and rejected at the boundary, before dispatch.
Validator
Compiled per-command argument validators, built from one or more procedure sets. Reuses the binding descriptor a #[procedures] trait already generates, so validation needs no separate schema definition and cannot drift from the client’s types.

Enums§

BindingsError
Failure rendering or checking the TypeScript client.
DispatchError
Error returned by a generated dispatch when a call cannot reach or leave the procedure. Wire-level failures only – a procedure’s own failures belong in its return type.
Layout
Allows configuring the format of the final types file
MethodCase
How a generated client casts its TypeScript method names. The wire command name and argument keys are always the Rust identifiers verbatim (wire parity); argument names in the TypeScript signature are always camelCase. Only the method name varies.
ValidatorError
Failure assembling a Validator from a procedure set: the contract’s JSON Schema could not be built or compiled. Structural, so it is raised once at register, never per call.

Traits§

ErrorSet
A wire error’s binding descriptor, generated by #[derive(tauri_typed_ipc::Error)] and implemented on the enum itself, so a procedure’s Result<_, E> return can name the error it rejects with.
EventSet
An event enum’s binding descriptor, generated by #[derive(Event)] and implemented on the enum itself.
ProcedureSet
A trait’s binding descriptor, generated by #[procedures] as the zero-sized {Trait}Procedures. Implemented on a type, not built from an instance, so a client can be rendered without standing up the procedure set.

Functions§

handler
Builds a tauri invoke handler from a procedure set.
handler_validated
Like handler, but validates each call’s arguments against a Validator before routing (the validate feature). A covered command whose payload fails its schema is rejected at the boundary – before the procedure runs – with DispatchError::Invalid; everything else routes exactly as handler does.
handler_validated_with_fallback
Like handler_with_fallback, but validates covered commands first (the validate feature). See handler_validated.
handler_with_fallback
Like handler, but commands outside the set fall through to fallback – typically tauri::generate_handler![...] – so raw commands and procedures coexist while an app migrates.

Attribute Macros§

procedures
Marks a trait as a tauri-typed-ipc procedure set.

Derive Macros§

Error
Derives a wire-serializable error for an enum: each variant serializes to { type: "<variant>", message: "<Display>" }, a discriminated union the client can branch on. Display-based, so it works with non-Serialize sources like std::io::Error.
Event
Derives a typed event channel for an enum: each variant is an event, emitted with Variant { .. }.emit(&app) under the wire name "{group}:{variant}" with the variant’s fields as the payload. The derive owns the name on the Rust side; the bindings generator owns the matching listener on the TS side (a later slice).