18次发布
0.3.0-alpha.2 | 2024年3月31日 |
---|---|
0.3.0-alpha.1 | 2024年1月13日 |
0.2.7 | 2024年7月11日 |
0.2.6 | 2024年4月10日 |
0.1.1 | 2023年6月4日 |
#11 in #frontend
123 monthly downloads
28KB
291 lines
TauRPC
This package is a Tauri extension to give you a fully-typed IPC layer for Tauri commands and events.
The TS types corresponding to your pre-defined Rust backend API are generated on runtime, after which they can be used to call the backend from your TypeScript frontend framework of choice. This crate provides typesafe bidirectional IPC communication between the Rust backend and TypeScript frontend. Specta is used under the hood for the type-generation. The trait-based API structure was inspired by tarpc.
使用🔧
首先,将以下crates添加到你的 Cargo.toml
# src-tauri/Cargo.toml
[dependencies]
// Using tauri 2.0.0-beta.12
taurpc = "=0.3.0-alpha.2"
specta = { version = "1.0.5", features = ["export"] }
tokio = { version = "1", features = ["full"] }
然后,声明并实现你的IPC方法和解析器。如果你想使用你的API来为Tauri的事件,你不需要实现解析器,请转到 调用前端
// src-tauri/src/main.rs
#[taurpc::procedures]
trait Api {
async fn hello_world();
}
#[derive(Clone)]
struct ApiImpl;
#[taurpc::resolvers]
impl Api for ApiImpl {
async fn hello_world(self) {
println!("Hello world");
}
}
#[tokio::main]
async fn main() {
tauri::Builder::default()
.plugin(tauri_plugin_shell::init())
.invoke_handler(taurpc::create_ipc_handler(ApiImpl.into_handler()))
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
The #[taurpc::procedures]
trait will generate everything necessary for handling calls and the type-generation. Now, you should run pnpm tauri dev
to generate and export the TS types. The types will by default be exported to bindings.ts
in the root of your project, but you can specify an export path by doing this #[taurpc::procedures(export_to = "../src/types.ts")]
.
然后在前端安装taurpc包。
pnpm install taurpc@next
现在在前端,你可以导入生成的类型,如果你在进程上指定了 export_to
属性,你应该从那里导入。有了这些类型,就生成了一个类型安全的代理,你可以使用它来调用命令和监听事件。
import { createTauRPCProxy } from '../bindings.ts'
const taurpc = await createTauRPCProxy()
await taurpc.hello_world()
在启动应用程序后,将生成taurpc的类型,运行 pnpm tauri dev
。如果类型没有被LSP拾取,你可能需要重新启动typescript以重新加载类型。
你可以在这里找到一个完整的示例(使用Svelte)。
使用结构体
如果您想在过程的输入/输出中使用结构体,您应该始终添加 #[taurpc::ipc_struct]
来确保生成相应的 ts 类型。这将派生 serde 的 Serialize
和 Deserialize
,以及 Clone
和 specta::Type
。
#[taurpc::ipc_struct]
// #[derive(serde::Serialize, serde::Deserialize, specta::Type, Clone)]
struct User {
user_id: u32,
first_name: String,
last_name: String,
}
#[taurpc::procedures]
trait Api {
async fn get_user() -> User;
}
访问管理状态
为了在过程之间共享一些状态,您可以在 API 实现结构体上添加字段。如果状态需要可变的,您需要使用一个支持内部可变的容器,例如 Mutex。
您可以使用 window
和 app_handle
参数,就像使用 Tauri 的命令一样。 Tauri 文档
// src-tauri/src/main.rs
use std::sync::Arc;
use tokio::sync::Mutex;
use tauri::{Manager, Runtime, State, Window};
type MyState = Arc<Mutex<String>>;
#[taurpc::procedures]
trait Api {
async fn with_state();
async fn with_window<R: Runtime>(window: Window<R>);
}
#[derive(Clone)]
struct ApiImpl {
state: MyState
};
#[taurpc::resolvers]
impl Api for ApiImpl {
async fn with_state(self) {
// ...
// let state = self.state.lock().await;
// ...
}
async fn with_window<R: Runtime>(self, window: Window<R>) {
// ...
}
}
#[tokio::main]
async fn main() {
tauri::Builder::default()
.invoke_handler(taurpc::create_ipc_handler(
ApiImpl {
state: Arc::new(Mutex::new("state".to_string())),
}
.into_handler(),
))
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
自定义错误处理
您可以通过返回一个 Result<T, E>
来返回一个错误,如果过程失败。这将拒绝前端上的承诺并抛出一个错误。如果您正在使用 Rust 的 std 库中的错误类型,它们可能不会实现 serde::Serialize
,这是在过程中返回任何内容所必需的。在简单场景中,您可以使用 map_err
将这些错误转换为 String
。对于更复杂的场景,您可以创建自己的错误类型,该类型实现了 serde::Serialize
。您可以在 这里 找到使用 thiserror 的示例。您也可以在 Tauri 指南 中找到更多相关信息。
过程的额外选项
在您的过程特质内部,您可以为定义的方法添加属性。这可以用来忽略或重命名一个方法。重命名将改变前端上过程的名称。
#[taurpc::procedures]
trait Api {
// #[taurpc(skip)]
#[taurpc(alias = "_hello_world_")]
async fn hello_world();
}
路由
您可以在单个过程特质内部定义所有命令和事件,但这会很快变得杂乱。通过使用 Router
结构体,您可以创建嵌套的命令和事件,您可以使用代理 TypeScript 客户端来调用它们。
通过使用 #[taurpc::procedures(path = "")]
上的 path
属性设置过程特质的路径,然后您可以创建一个空的路由器并使用 merge
方法向路由器添加处理程序。您只能有一个未指定路径的特质,这将是根。最后,您不需要使用 taurpc::create_ipc_handler()
,只需在路由器上调用 into_handler()
即可。
// Root procedures
#[taurpc::procedures]
trait Api {
async fn hello_world();
}
#[derive(Clone)]
struct ApiImpl;
#[taurpc::resolvers]
impl Api for ApiImpl {
async fn hello_world(self) {
println!("Hello world");
}
}
// Nested procedures, you can also do this (path = "api.events.users")
#[taurpc::procedures(path = "events")]
trait Events {
#[taurpc(event)]
async fn event();
}
#[derive(Clone)]
struct EventsImpl;
#[taurpc::resolvers]
impl Events for EventsImpl {}
#[tokio::main]
async fn main() {
let router = Router::new()
.merge(ApiImpl.into_handler())
.merge(EventsImpl.into_handler());
tauri::Builder::default()
.invoke_handler(router.into_handler())
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
现在在前端上您可以使用代理客户端。
// Call `hello_world` on the root layer
await taurpc.hello_world()
// Listen for `event` on the `events` layer
const unlisten = taurpc.events.event.on(() => {
console.log('Hello World!')
})
调用前端
从Rust后端触发TypeScript前端的事件,提供完整的类型化体验。宏#[taurpc::procedures]
还会生成一个结构体,您可以用来触发事件,这意味着您可以用定义过程的方式定义事件类型。
首先声明API结构,默认情况下,事件触发结构体会被识别为TauRpc{trait_ident}EventTrigger
。如果您想更改此设置,可以添加一个属性来实现,#[taurpc::procedures(event_trigger = ApiEventTrigger)]
。更多详细信息请参阅示例。
您应该在事件上添加#[taurpc(event)]
属性。如果您这样做,您将不需要实现相应的解析器。
// src-tauri/src/main.rs
#[taurpc::procedures(event_trigger = ApiEventTrigger)]
trait Api {
#[taurpc(event)]
async fn hello_world();
}
#[derive(Clone)]
struct ApiImpl;
#[taurpc::resolvers]
impl Api for ApiImpl {}
#[tokio::main]
async fn main() {
tauri::Builder::default()
.invoke_handler(taurpc::create_ipc_handler(ApiImpl.into_handler()))
.setup(|app| {
let trigger = ApiEventTrigger::new(app.handle());
trigger.hello_world()?;
Ok(())
})
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
然后,在客户端,您可以监听类型的事件
const unlisten = taurpc.hello_world.on(() => {
console.log('Hello World!')
})
// Run this inside a cleanup function, for example in React and onDestroy in Svelte
unlisten()
向特定窗口发送事件
默认情况下,事件会发送到所有窗口。如果您想通过标签发送事件到特定窗口,可以执行以下操作
use taurpc::Windows;
trigger.send_to(Windows::One("main".to_string())).hello_world()?;
// Options:
// - Windows::All (default)
// - Windows::One(String)
// - Windows::N(Vec<String>)
功能
- 基本输入
- 结构体输入
- 共享状态
- 使用Tauri的托管状态?
- 重命名方法
- 嵌套路由
- 合并路由器
- 自定义错误处理
- 类型输出
- 异步方法 - async traits👀
- 允许同步方法
- 调用前端
- 重命名事件触发结构体
- 向特定窗口发送事件
- React/Svelte处理程序
依赖
~18–59MB
~1M SLoC