| Crates.io | hsnet-rpc-macro |
| lib.rs | hsnet-rpc-macro |
| version | 0.1.0 |
| created_at | 2025-10-14 08:47:20.573648+00 |
| updated_at | 2025-10-14 08:47:20.573648+00 |
| description | Procedural macros for hsnet-rpc framework |
| homepage | |
| repository | |
| max_upload_size | |
| id | 1881889 |
| size | 20,685 |
Proc macro 实现,为 hsnet-rpc 提供 #[rpc(server)] 宏。
消除重复的 handler 注册代码。
// 需要写两遍:trait 定义 + 手动注册
trait MyService {
async fn start(&self, config: Config) -> Result<String, RpcError>;
async fn stop(&self, force: bool) -> Result<String, RpcError>;
}
// 手动注册(容易出错)
let server = RpcServer::new()
.handle("start", {
let service = service.clone();
move |config: Config| {
let service = service.clone();
async move { service.start(config).await }
}
})
.handle("stop", {
let service = service.clone();
move |force: bool| {
let service = service.clone();
async move { service.stop(force).await }
}
});
问题:
"start")容易拼写错误#[rpc(server)]
trait MyService {
#[method(name = "start")]
async fn start(&self, config: Config) -> Result<String, RpcError>;
#[method(name = "stop")]
async fn stop(&self, force: bool) -> Result<String, RpcError>;
}
// 自动生成注册代码
let server = service.into_rpc();
use hsnet_rpc::{rpc, RpcError};
use async_trait::async_trait;
#[async_trait]
#[rpc(server)]
pub trait ControlRpc {
#[method(name = "start")]
async fn start(&self, config: Config) -> Result<String, RpcError>;
#[method(name = "stop")]
async fn stop(&self, force: bool) -> Result<String, RpcError>;
// 无参数方法
#[method(name = "status")]
async fn status(&self) -> Result<String, RpcError>;
}
pub struct ControlService { /* ... */ }
#[async_trait]
impl ControlRpc for ControlService {
async fn start(&self, config: Config) -> Result<String, RpcError> {
// 业务逻辑
Ok("started".to_string())
}
async fn stop(&self, force: bool) -> Result<String, RpcError> {
Ok("stopped".to_string())
}
async fn status(&self) -> Result<String, RpcError> {
Ok("running".to_string())
}
}
into_rpc() 方法use hsnet_rpc::IntoRpcServer; // 宏生成的 trait
let service = Arc::new(ControlService::new());
let server = service.into_rpc(); // 自动注册所有方法
server.run_unix("/tmp/control.sock").await?;
输入:
#[rpc(server)]
pub trait ControlRpc {
#[method(name = "start")]
async fn start(&self, config: Config) -> Result<String, RpcError>;
#[method(name = "stop")]
async fn stop(&self, force: bool) -> Result<String, RpcError>;
}
展开为:
// 1. 保留原始 trait 定义(去掉 #[rpc] 和 #[method] 属性)
pub trait ControlRpc {
async fn start(&self, config: Config) -> Result<String, RpcError>;
async fn stop(&self, force: bool) -> Result<String, RpcError>;
}
// 2. 生成扩展 trait
pub trait IntoRpcServer {
fn into_rpc(self: std::sync::Arc<Self>) -> hsnet_rpc::RpcServer;
}
// 3. 为所有实现了 ControlRpc 的类型实现 IntoRpcServer
impl<T: ControlRpc + Send + Sync + 'static> IntoRpcServer for T {
fn into_rpc(self: std::sync::Arc<Self>) -> hsnet_rpc::RpcServer {
let service = self;
let mut server = hsnet_rpc::RpcServer::new();
// 注册 "start" 方法(单参数)
server = server.handle("start", {
let service = service.clone();
move |config: Config| {
let service = service.clone();
async move { service.start(config).await }
}
});
// 注册 "stop" 方法(单参数)
server = server.handle("stop", {
let service = service.clone();
move |force: bool| {
let service = service.clone();
async move { service.stop(force).await }
}
});
server
}
}
#[cfg])#[rpc(server)]
pub trait ControlRpc {
// 所有平台都有
#[method(name = "start")]
async fn start(&self, config: Config) -> Result<String, RpcError>;
// 仅 Windows 平台
#[cfg(windows)]
#[method(name = "restart")]
async fn restart(&self) -> Result<String, RpcError>;
// 仅 Unix 平台
#[cfg(unix)]
#[method(name = "reload_config")]
async fn reload_config(&self) -> Result<String, RpcError>;
}
宏会自动处理 #[cfg] 属性,生成条件编译的注册代码:
impl<T: ControlRpc + Send + Sync + 'static> IntoRpcServer for T {
fn into_rpc(self: Arc<Self>) -> RpcServer {
let mut server = RpcServer::new();
server = server.handle("start", /* ... */);
#[cfg(windows)]
{
server = server.handle("restart", /* ... */);
}
#[cfg(unix)]
{
server = server.handle("reload_config", /* ... */);
}
server
}
}
#[method(name = "status")]
async fn status(&self) -> Result<String, RpcError>;
生成:
server = server.handle("status", {
let service = service.clone();
move |_: ()| {
let service = service.clone();
async move { service.status().await }
}
});
客户端调用:
let status: String = client.call("status", ()).await?;
#[method(name = "start")]
async fn start(&self, config: Config) -> Result<String, RpcError>;
生成:
server = server.handle("start", {
let service = service.clone();
move |config: Config| {
let service = service.clone();
async move { service.start(config).await }
}
});
// ✅ 支持
#[method(name = "method1")]
async fn method1(&self) -> Result<String, RpcError>;
#[method(name = "method2")]
async fn method2(&self, param: Config) -> Result<String, RpcError>;
// ❌ 不支持(会生成编译错误)
#[method(name = "method3")]
async fn method3(&self, p1: String, p2: u32) -> Result<String, RpcError>;
原因: 保持简单性。多参数可以用结构体包装:
#[derive(Serialize, Deserialize)]
struct Method3Params {
p1: String,
p2: u32,
}
#[method(name = "method3")]
async fn method3(&self, params: Method3Params) -> Result<String, RpcError>;
// ✅ 正确
#[method(name = "start")]
async fn start(&self, config: Config) -> Result<String, RpcError>;
// ❌ 错误(编译失败)
#[method(name = "start")]
async fn start(&self, config: &Config) -> Result<String, RpcError>;
原因: 生成的闭包跨任务边界,需要 'static 生命周期。
解决方案:
bool, u32)String、Vec、Arc 等拥有所有权的类型Result<T, RpcError>// ✅ 正确
#[method(name = "start")]
async fn start(&self, config: Config) -> Result<String, RpcError>;
// ❌ 错误(类型不匹配)
#[method(name = "start")]
async fn start(&self, config: Config) -> String;
原因: RPC 调用需要统一的错误处理。
#[async_trait] 配合使用use async_trait::async_trait;
#[async_trait] // 必须
#[rpc(server)]
pub trait ControlRpc {
// async fn 在 trait 中需要 async_trait
#[method(name = "start")]
async fn start(&self, config: Config) -> Result<String, RpcError>;
}
原因: Rust trait 中的 async fn 需要 async_trait 宏支持。
解析 trait 定义
syn::parse_macro_input 解析 TokenStream提取 RPC 方法
#[method] 属性的方法#[method(name = "xxx")] 提取方法名&self)#[cfg] 属性用于条件编译生成 handler 注册代码
move |_: ()| { ... }move |param: T| { ... }生成最终代码
IntoRpcServer traitimpl<T: OriginalTrait> IntoRpcServer for T// 解析方法名
fn parse_method_name(attr: &Attribute) -> String {
if let Meta::List(meta_list) = &attr.meta {
let tokens_str = meta_list.tokens.to_string();
// 简单解析 name = "xxx"
if let Some(start) = tokens_str.find('"') {
if let Some(end) = tokens_str[start + 1..].find('"') {
return tokens_str[start + 1..start + 1 + end].to_string();
}
}
}
String::new()
}
// 生成 handler 注册代码
if method.params.is_empty() {
// 无参数
quote! {
server = server.handle(#rpc_name, {
let service = service.clone();
move |_: ()| {
let service = service.clone();
async move { service.#method_name().await }
}
});
}
} else if method.params.len() == 1 {
// 单参数
let param_name = &method.params[0].0;
let param_type = &method.params[0].1;
quote! {
server = server.handle(#rpc_name, {
let service = service.clone();
move |#param_name: #param_type| {
let service = service.clone();
async move { service.#method_name(#param_name).await }
}
});
}
}
使用 cargo expand 查看宏展开后的代码:
# 安装 cargo-expand
cargo install cargo-expand
# 展开特定文件的宏
cargo expand --package client --lib ctrl_ipc::service
# 或者展开整个 crate
cargo expand --package client
参考 client/src/ctrl_ipc/service.rs,这是生产环境中的完整示例:
#[async_trait]
#[rpc(server)]
pub trait ControlRpc {
// 通用方法
#[method(name = "wg_start")]
async fn wg_start(&self, config: WgConfig) -> Result<String, RpcError>;
#[method(name = "wg_stop")]
async fn wg_stop(&self, force: bool) -> Result<String, RpcError>;
// Windows 专用方法
#[cfg(windows)]
#[method(name = "restart")]
async fn restart(&self) -> Result<String, RpcError>;
#[cfg(windows)]
#[method(name = "cmd_svc")]
async fn cmd_svc(&self, msg: IpcCommand) -> Result<String, RpcError>;
// ... 共 18 个方法
}
// 实现
pub struct ControlService { /* ... */ }
#[async_trait]
impl ControlRpc for ControlService {
async fn wg_start(&self, config: WgConfig) -> Result<String, RpcError> {
// 业务逻辑
}
// ...
}
// 使用
let service = ControlService::new(...);
let server = service.into_rpc();
server.run_pipe(r"\\.\pipe\hsnetv2_ctrl_ipc").await?;
MIT