#![cfg_attr(any(docs, docsrs), feature(doc_cfg))]
pub mod combinator;
mod json_rpc;
mod post_json;
pub mod server {
#[cfg(feature = "json-rpc-server")]
pub use crate::json_rpc::server as json_rpc;
#[cfg(feature = "post-json-axum")]
pub use crate::post_json::server as post_json;
}
#[cfg(feature = "client")]
pub mod client {
pub use crate::json_rpc::client as json_rpc;
pub use crate::post_json::client as post_json;
}
pub mod generate;
#[doc(hidden)]
pub mod internal {
pub use paste::paste;
}
#[cfg(test)]
mod test;
use core::future::Future;
use core::pin::Pin;
pub trait IsApi {
type Methods;
const API_NAME: &str;
const API_VERSION: &str;
}
pub trait HasMethod<M>: IsApi {
type Res;
const METHOD_NAME: &str;
const METHOD_DOCS: Option<&str>;
}
pub trait ImplsMethod<API: HasMethod<M>, M> {
fn call_api(&self, _req: M) -> impl Future<Output = API::Res> + Send;
}
#[derive(Debug, Clone, Copy)]
#[repr(transparent)]
pub struct BoxedImpl<B>(pub B);
use core::ops::Deref;
impl<API, M, B> ImplsMethod<API, M> for BoxedImpl<B>
where
API: IsApi + HasMethod<M> + 'static,
M: Send,
B: Deref + Sync,
B::Target: ImplsMethodBoxed<API, M>,
{
async fn call_api(&self, req: M) -> API::Res {
self.0.call_api_box(req).await
}
}
impl<API, M, B> ImplsMethod<API, M> for std::sync::Arc<B>
where
API: IsApi + HasMethod<M>,
M: Send,
B: ImplsMethod<API, M> + Send + Sync + ?Sized,
{
async fn call_api(&self, req: M) -> API::Res {
self.as_ref().call_api(req).await
}
}
impl<API, M, B> ImplsMethod<API, M> for Box<B>
where
API: IsApi + HasMethod<M>,
M: Send,
B: ImplsMethod<API, M> + Send + Sync + ?Sized,
{
async fn call_api(&self, req: M) -> API::Res {
self.as_ref().call_api(req).await
}
}
#[allow(clippy::type_complexity)]
pub trait ImplsMethodBoxed<API: HasMethod<M>, M>: Sync {
#[must_use]
fn call_api_box<'s, 'a>(&'s self, _req: M) -> Pin<Box<dyn Future<Output = API::Res> + Send + 'a>>
where
's: 'a,
Self: 's,
API: 'static,
M: 'a;
}
impl<API, M, B> ImplsMethodBoxed<API, M> for B
where
B: ImplsMethod<API, M> + Sync,
API: HasMethod<M>,
{
fn call_api_box<'s, 'a>(&'s self, req: M) -> Pin<Box<dyn Future<Output = API::Res> + Send + 'a>>
where
's: 'a,
Self: 'a,
API: 'a,
M: 'a,
{
Box::pin(self.call_api(req))
}
}
pub trait CallApi {
fn call_api_x<API, Req>(&self, req: Req) -> impl Future<Output = API::Res> + Send
where
API: HasMethod<Req>,
Self: ImplsMethod<API, Req>;
}
impl<E> CallApi for E {
fn call_api_x<API, Req>(&self, req: Req) -> impl Future<Output = API::Res> + Send
where
API: HasMethod<Req>,
Self: ImplsMethod<API, Req>,
{
self.call_api(req)
}
}
#[macro_export]
#[doc(hidden)]
macro_rules! impl_method {
{$api:ty, $method:expr, $req:ty, $res:ty, docs = $docs:expr} => {
impl $crate::HasMethod<$req> for $api {
type Res = $res;
const METHOD_NAME: &str = $method;
const METHOD_DOCS: Option<&str> = $docs;
}
};
{$api:ty, $method:expr, $req:ty, $res:ty, () } => {
$crate::impl_method!{$api, $method, $req, $res, docs = None}
};
{$api:ty, $method:expr, $req:ty, $res:ty, ( $($doc:expr),+ ) } => {
$crate::impl_method!{$api, $method, $req, $res, docs = Some(concat!($($doc, "\n"),*).trim_ascii())}
};
}
#[macro_export]
#[doc(hidden)]
macro_rules! build_hlist {
() => { () };
($type:ty $(, $rest:ty)*) => { ($type, $crate::build_hlist!($($rest),*)) };
}
#[macro_export]
macro_rules! define_api {
{ $vis:vis $api:ident $(, version = $version:expr)? $(, name = $name:expr)? => {
$( $( #[doc = $doc:expr] )* $method:literal, $req:ty => $res:ty; )+
}} => {
$crate::impl_is_api!{$vis $api, ($($req),+) $(, version = $version)? $(, name = $name)?}
$( $crate::impl_method!{ $api, $method, $req, $res, ($($doc),*) } )+
};
{ $vis:vis $api:ident $(, name = $name:expr)? $(, version = $version:expr)? => {
$( $( #[doc = $doc:expr] )* $method:literal, $req:ty => $res:ty; )+
}} => {
$crate::impl_is_api!{$vis $api, ($($req),+) $(, version = $version)? $(, name = $name)?}
$( $crate::impl_method!{ $api, $method, $req, $res, ($($doc),*) } )+
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! impl_is_api {
{ $vis:vis $api:ident, ($($req:ty),+), version = $version:expr, name = $name:expr } => {
$crate::impl_is_api!{$vis $api, ($($req),+), $version, $name}
};
{ $vis:vis $api:ident, ($($req:ty),+), version = $version:expr } => {
$crate::impl_is_api!{$vis $api, ($($req),+), $version, stringify!($api)}
};
{ $vis:vis $api:ident, ($($req:ty),+), name = $name:expr } => {
$crate::impl_is_api!{$vis $api, ($($req),+), "0.0.0", $name}
};
{ $vis:vis $api:ident, ($($req:ty),+) } => {
$crate::impl_is_api!{$vis $api, ($($req),+), "0.0.0", stringify!($api)}
};
{$vis:vis $api:ident, ($($req:ty),+), $version:expr, $name:expr} => {
impl $crate::IsApi for $api {
type Methods = $crate::build_hlist!($($req),+);
const API_NAME: &str = $name;
const API_VERSION: &str = $version;
}
$crate::internal::paste! {
#[allow(unused_macros)]
macro_rules! [<per_ $api _method>] {
($m:path) => { $m!{$($req),*} }
}
#[doc = concat!("Trait alias for `ImplsMethod<", stringify!($api), ", M>` for all of the methods.")]
#[allow(dead_code)]
$vis trait [<Impls $api>]: $($crate::ImplsMethod<$api, $req> +)* {}
impl<B: $($crate::ImplsMethod<$api, $req> +)*> [<Impls $api>] for B {}
#[doc = concat!("Trait alias for `ImplsMethodBoxed<", stringify!($api), ", M>` for all of the methods.")]
#[allow(dead_code)]
$vis trait [<Impls $api Boxed>]: $($crate::ImplsMethodBoxed<$api, $req> +)* {}
impl<B: $($crate::ImplsMethodBoxed<$api, $req> +)*> [<Impls $api Boxed>] for B {}
}
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! mk_handler {
($api:ty, $envt:ty => { $($func:ident : $req:ty ,)+ } ) => (
$(
impl $crate::ImplsMethod<$api, $req> for $envt {
async fn call_api(&self, req: $req) -> <$api as $crate::HasMethod<$req>>::Res
{
self.$func(req).await
}
}
)+
)
}