Support converting multiple methods into handlers.
Closed this issue · 2 comments
andeya commented
@chrislearn The following is my implementation. If you think it's good, you can consider integrating it into the official package:
use salvo::oapi::extract::*;
use salvo::prelude::*;
use salvo_macro_ext::craft;
#[derive(Clone)]
pub struct Service {
state: i64,
}
#[craft]
impl Service {
fn new(state: i64) -> Self {
Self { state }
}
/// doc line 1
/// doc line 2
#[salvo_macro_ext::craft(handler)]
fn add1(&self, left: QueryParam<i64, true>, right: QueryParam<i64, true>) -> String {
(self.state + *left + *right).to_string()
}
#[craft(handler)]
pub(crate) fn add2(
self: ::std::sync::Arc<Self>,
left: QueryParam<i64, true>,
right: QueryParam<i64, true>,
) -> String {
(self.state + *left + *right).to_string()
}
#[craft(handler)]
pub fn add3(left: QueryParam<i64, true>, right: QueryParam<i64, true>) -> String {
(*left + *right).to_string()
}
}
Code after macro expansion:
#[derive(Clone)]
impl Service {
fn new(state: i64) -> Self {
Self { state }
}
/// doc line 1
/// doc line 2
fn add1(&self) -> impl Handler {
pub struct handle(::std::sync::Arc<Service>);
impl ::std::ops::Deref for handle {
type Target = Service;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[handler]
impl handle {
/// doc line 1
/// doc line 2
async fn handle(
&self,
left: QueryParam<i64, true>,
right: QueryParam<i64, true>
) -> String {
(self.state + *left + *right).to_string()
}
}
handle(::std::sync::Arc::new(self.clone()))
}
fn add2(self: &::std::sync::Arc<Self>) -> impl Handler {
pub struct handle(::std::sync::Arc<Service>);
impl ::std::ops::Deref for handle {
type Target = Service;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[handler]
impl handle {
async fn handle(
&self,
left: QueryParam<i64, true>,
right: QueryParam<i64, true>
) -> String {
(self.state + *left + *right).to_string()
}
}
handle(self.clone())
}
fn add3() -> impl Handler {
#[handler]
async fn add3(left: QueryParam<i64, true>, right: QueryParam<i64, true>) -> String {
(*left + *right).to_string()
}
add3
}
}
Sure, you can also replace #[craft(handler)]
with #[craft(endpoint(...))]
.
NOTE: If the receiver of a method is &self
, you need to implement the Clone
trait for the type.
Repository: https://github.com/andeya/salvo_macro_ext
Example detail: https://github.com/andeya/salvo_macro_ext/blob/main/examples/add.rs
immno commented
我们是这样去实现的,涉及协同开发的时候:
pub trait RouterContext {
fn server(&self) -> Option<Router> { None }
fn server_with_anonymous(&self) -> Option<Router> { None }
fn openapi(&self) -> Option<Router> { None }
fn poseidon(&self) -> Option<Router> { None }
}
有个统一注册中心,统一管理:
fn router_context_register() -> Vec<Box<dyn RouterContext>> {
vec![
Box::new(AssetDataApi),
Box::new(CategoryApi),
Box::new(HealthCheckApi),
]
}
pub async fn create_service(context: Arc<ApplicationContext>) -> AppResult<Service> {
let routers = router_context_register();
let (mut sr, so) = merge_router(&routers, &context, |c| c.server());
let (mut ar, ao) = merge_router(&routers, &context, |c| c.server_with_anonymous());
let (or, oo) = merge_router(&routers, &context, |c| c.openapi());
let (po, _) = merge_router(&routers, &context, |c| c.poseidon());
// .......
Ok(Service::new(all_routers)
.hoop(ContextInject { context })
.hoop(TraceLogger)
.catcher(SalvoErrorCatcher::catcher()))
}
最后是api层:
pub struct AssetDataApi;
#[endpoint(tags("xxx"), parameters(("id", description = "ID")))]
async fn identity(id: PathParam<i64>, depot: &mut Depot) -> AppResult<ResponseResult<'static, Option<String>>> {
let ctx = obtain_context(depot)?;
let store_identity = AssetDataService::identity(ctx, id.into_inner()).await?;
Ok(ResponseResult::ok(store_identity))
}
impl RouterContext for AssetDataApi {
fn server(&self) -> Option<Router> {
let router = Router::with_path("asset-data")
.push(Router::with_path("sample").get(sample))
.push(Router::with_path("batch/sample").post(batch_sample))
.push(Router::with_path("statistical-chart").get(statistical_chart))
.push(Router::with_path("batch-recent").post(batch_recent))
.push(Router::with_path("page").get(page))
.push(Router::with_path("repair").post(repair_asset_data))
.push(Router::with_path("statistics").get(statistics))
.push(Router::with_path("<id>").push(Router::with_path("identity").get(identity)));
Some(router)
}
}
应该也能满足团队协作的情况吧
immno commented
当然可以设计一个全局的状态,通过宏的方式将所有路由全部加进去,而不是像现在需要手动添加。