1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
//! Trait-based, type-safe Tauri IPC, built on [specta](https://specta.dev)
//! v2. Sync by default.
//!
//! Define your IPC surface once as a Rust trait. [`macro@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 `invoke`s, 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!())`:
//!
//! ```no_run
//! # 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}!")
//! # }
//! # }
//! 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:
//!
//! ```no_run
//! # use tauri_typed_ipc::procedures;
//! # #[procedures]
//! # trait Greeter {
//! # fn greet(&self, name: String) -> String;
//! # }
//! tauri_typed_ipc::Bindings::new()
//! .register::<GreeterProcedures>()
//! .export_to("../src/bindings.ts")?;
//! # Ok::<(), Box<dyn std::error::Error>>(())
//! ```
//!
//! ```typescript
//! import { greeter } from "./bindings";
//! const hello = await greeter.greet("world"); // Promise<string>
//! ```
//!
//! # What's here
//!
//! - **Procedures** -- [`macro@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 -- [`macro@Event`] on an enum emits typed
//! payloads and generates the matching listeners.
//! - **Typed errors** -- [`macro@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>`](Channel) parameter streams values
//! back to the caller, on top of the value the procedure returns.
//! - **Composition** -- [`Procedures::merge`] mounts several traits on one
//! handler.
pub use ;
pub use ;
pub use ;
pub use ;
// Descriptor payloads carried from the derives to the bindings generator. Named
// in generated code (`ProcedureSet::procedures` etc.), never built by hand.
pub use ;
pub use Channel;
pub use Context;
pub use ;
// The dispatch result types: returned by generated `dispatch` and consumed by
// `handler`. Plumbing, not part of the hand-written surface.
pub use ;
pub use ;
/// 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.
// Referenced by macro-generated code. Not public API.