mirror of
https://github.com/Start9Labs/rpc-toolkit.git
synced 2026-03-26 02:11:56 +00:00
better TS
This commit is contained in:
8
Cargo.lock
generated
8
Cargo.lock
generated
@@ -1745,9 +1745,9 @@ checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||
|
||||
[[package]]
|
||||
name = "visit-rs"
|
||||
version = "0.1.5"
|
||||
version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ea0fdb05dd98029f7e226ccd11bd7184ad68566365547930bdde9ae79b5ce3d"
|
||||
checksum = "fbd4baf031bf580bc7ba411f4625a304909c975bf3d3e8d9c43ed88d23be1b8e"
|
||||
dependencies = [
|
||||
"async-stream",
|
||||
"futures",
|
||||
@@ -1757,9 +1757,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "visit-rs-derive"
|
||||
version = "0.1.3"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8dde3216fefaf10421d9509f88bc5a480b5fb3fe48779ca4f148e75896d3f5ad"
|
||||
checksum = "2336cc680cfb205620939f0fa62348a4b6da37d1e727670f3d7d77aebc50b611"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
||||
@@ -39,5 +39,5 @@ thiserror = "2.0"
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
tokio-stream = { version = "0.1", features = ["io-util", "net"] }
|
||||
url = "2"
|
||||
visit-rs = { version = "0.1.5", optional = true }
|
||||
visit-rs = { version = "0.1.8", optional = true }
|
||||
yajrc = "0.1"
|
||||
|
||||
21
src/cli.rs
21
src/cli.rs
@@ -13,9 +13,11 @@ use tokio::io::{AsyncBufReadExt, AsyncRead, AsyncWrite, AsyncWriteExt, BufReader
|
||||
use url::Url;
|
||||
use yajrc::{Id, RpcError};
|
||||
|
||||
#[cfg(feature = "ts")]
|
||||
use crate::ts::HandlerTSBindings;
|
||||
use crate::util::{internal_error, invalid_params, parse_error, without, Flat, PhantomData};
|
||||
use crate::{
|
||||
AnyHandler, CliBindings, CliBindingsAny, Empty, HandleAny, HandleAnyArgs, HandlerArgs,
|
||||
Adapter, AnyHandler, CliBindings, CliBindingsAny, Empty, HandleAny, HandleAnyArgs, HandlerArgs,
|
||||
HandlerArgsFor, HandlerFor, HandlerTypes, Name, ParentHandler, PrintCliResult,
|
||||
};
|
||||
|
||||
@@ -209,15 +211,22 @@ where
|
||||
type Err = RemoteHandler::Err;
|
||||
}
|
||||
|
||||
impl<Context, RemoteContext, RemoteHandler, Extra> Adapter
|
||||
for CallRemoteHandler<Context, RemoteContext, RemoteHandler, Extra>
|
||||
{
|
||||
type Inner = RemoteHandler;
|
||||
fn as_inner(&self) -> &Self::Inner {
|
||||
&self.handler
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "ts")]
|
||||
impl<Context, RemoteContext, RemoteHandler, Extra> crate::handler::HandlerTS
|
||||
impl<Context, RemoteContext, RemoteHandler, Extra> HandlerTSBindings
|
||||
for CallRemoteHandler<Context, RemoteContext, RemoteHandler, Extra>
|
||||
where
|
||||
RemoteHandler: crate::handler::HandlerTS,
|
||||
Extra: Send + Sync + 'static,
|
||||
RemoteHandler: HandlerTSBindings,
|
||||
{
|
||||
fn type_info(&self) -> Option<String> {
|
||||
self.handler.type_info()
|
||||
fn get_ts<'a>(&'a self) -> Option<crate::ts::HandlerTS<'a>> {
|
||||
self.handler.get_ts()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -8,8 +8,6 @@ use imbl_value::Value;
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde::Serialize;
|
||||
|
||||
#[cfg(feature = "ts")]
|
||||
use crate::ts::TSVisitor;
|
||||
use crate::util::PhantomData;
|
||||
use crate::{
|
||||
CliBindings, Empty, HandlerArgs, HandlerArgsFor, HandlerFor, HandlerTypes, LeafHandler,
|
||||
@@ -48,22 +46,6 @@ impl<F, T, E, Args> std::fmt::Debug for FromFn<F, T, E, Args> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "ts")]
|
||||
impl<F, T, E, Args> crate::handler::HandlerTS for FromFn<F, T, E, Args>
|
||||
where
|
||||
Self: HandlerTypes,
|
||||
visit_rs::Static<<Self as HandlerTypes>::Params>: visit_rs::Visit<TSVisitor>,
|
||||
visit_rs::Static<<Self as HandlerTypes>::Ok>: visit_rs::Visit<TSVisitor>,
|
||||
{
|
||||
fn type_info(&self) -> Option<String> {
|
||||
let mut visitor = TSVisitor::default();
|
||||
Some(format!(
|
||||
"{{_PARAMS:{},_RETURN:{}}}",
|
||||
<Self as HandlerTypes>::Params::inline_flattened(),
|
||||
<Self as HandlerTypes>::Ok::inline_flattened(),
|
||||
))
|
||||
}
|
||||
}
|
||||
impl<Context, F, T, E, Args> PrintCliResult<Context> for FromFn<F, T, E, Args>
|
||||
where
|
||||
Context: crate::Context,
|
||||
@@ -174,21 +156,6 @@ impl<F, Fut, T, E, Args> std::fmt::Debug for FromFnAsync<F, Fut, T, E, Args> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "ts")]
|
||||
impl<F, Fut, T, E, Args> crate::handler::HandlerTS for FromFnAsync<F, Fut, T, E, Args>
|
||||
where
|
||||
Self: HandlerTypes,
|
||||
<Self as HandlerTypes>::Params: ts_rs::TS,
|
||||
<Self as HandlerTypes>::Ok: ts_rs::TS,
|
||||
{
|
||||
fn type_info(&self) -> Option<String> {
|
||||
Some(format!(
|
||||
"{{_PARAMS:{},_RETURN:{}}}",
|
||||
<Self as HandlerTypes>::Params::inline_flattened(),
|
||||
<Self as HandlerTypes>::Ok::inline_flattened(),
|
||||
))
|
||||
}
|
||||
}
|
||||
impl<Context, F, Fut, T, E, Args> PrintCliResult<Context> for FromFnAsync<F, Fut, T, E, Args>
|
||||
where
|
||||
Context: crate::Context,
|
||||
@@ -286,21 +253,6 @@ impl<F, Fut, T, E, Args> std::fmt::Debug for FromFnAsyncLocal<F, Fut, T, E, Args
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "ts")]
|
||||
impl<F, Fut, T, E, Args> crate::handler::HandlerTS for FromFnAsyncLocal<F, Fut, T, E, Args>
|
||||
where
|
||||
Self: HandlerTypes,
|
||||
<Self as HandlerTypes>::Params: ts_rs::TS,
|
||||
<Self as HandlerTypes>::Ok: ts_rs::TS,
|
||||
{
|
||||
fn type_info(&self) -> Option<String> {
|
||||
Some(format!(
|
||||
"{{_PARAMS:{},_RETURN:{}}}",
|
||||
<Self as HandlerTypes>::Params::inline_flattened(),
|
||||
<Self as HandlerTypes>::Ok::inline_flattened(),
|
||||
))
|
||||
}
|
||||
}
|
||||
impl<Context, F, Fut, T, E, Args> PrintCliResult<Context> for FromFnAsyncLocal<F, Fut, T, E, Args>
|
||||
where
|
||||
Context: crate::Context,
|
||||
|
||||
@@ -13,6 +13,8 @@ use serde::de::DeserializeOwned;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use yajrc::RpcError;
|
||||
|
||||
use crate::impl_ts_struct;
|
||||
use crate::ts::HandlerTSBindings;
|
||||
use crate::util::{internal_error, invalid_params, Flat};
|
||||
|
||||
pub mod adapters;
|
||||
@@ -55,21 +57,8 @@ impl<Context: crate::Context, Inherited: Send + Sync> HandleAnyArgs<Context, Inh
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) trait HandleAnyTS {
|
||||
#[allow(dead_code)]
|
||||
fn type_info(&self) -> Option<String> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: HandleAnyTS> HandleAnyTS for Arc<T> {
|
||||
fn type_info(&self) -> Option<String> {
|
||||
self.deref().type_info()
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) trait HandleAnyRequires: HandleAnyTS + Send + Sync {}
|
||||
impl<T: HandleAnyTS + Send + Sync> HandleAnyRequires for T {}
|
||||
pub(crate) trait HandleAnyRequires: HandlerTSBindings + Send + Sync {}
|
||||
impl<T: HandlerTSBindings + Send + Sync> HandleAnyRequires for T {}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
pub(crate) trait HandleAny<Context>: HandleAnyRequires {
|
||||
@@ -149,7 +138,9 @@ pub trait PrintCliResult<Context: crate::Context>: HandlerTypes {
|
||||
}
|
||||
|
||||
#[allow(private_interfaces)]
|
||||
pub struct DynHandler<Context, Inherited>(Arc<dyn HandleAny<Context, Inherited = Inherited>>);
|
||||
pub struct DynHandler<Context, Inherited>(
|
||||
pub(crate) Arc<dyn HandleAny<Context, Inherited = Inherited>>,
|
||||
);
|
||||
impl<Context: crate::Context, Inherited> DynHandler<Context, Inherited> {
|
||||
pub fn new<C, H>(handler: H) -> Option<Self>
|
||||
where
|
||||
@@ -169,11 +160,6 @@ impl<Context, Inherited> Debug for DynHandler<Context, Inherited> {
|
||||
f.debug_struct("DynHandler").finish()
|
||||
}
|
||||
}
|
||||
impl<Context, Inherited> HandleAnyTS for DynHandler<Context, Inherited> {
|
||||
fn type_info(&self) -> Option<String> {
|
||||
self.0.type_info()
|
||||
}
|
||||
}
|
||||
#[async_trait::async_trait]
|
||||
impl<Context: crate::Context, Inherited: Send> HandleAny<Context>
|
||||
for DynHandler<Context, Inherited>
|
||||
@@ -201,6 +187,12 @@ impl<Context: crate::Context, Inherited: Send> HandleAny<Context>
|
||||
self.0.cli()
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "ts")]
|
||||
impl<Context, Inherited> HandlerTSBindings for DynHandler<Context, Inherited> {
|
||||
fn get_ts<'a>(&'a self) -> Option<crate::ts::HandlerTS<'a>> {
|
||||
self.0.get_ts()
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(type_alias_bounds)]
|
||||
pub type HandlerArgsFor<Context: crate::Context, H: HandlerTypes + ?Sized> =
|
||||
@@ -227,19 +219,20 @@ pub trait HandlerTypes {
|
||||
type Err: Send + Sync;
|
||||
}
|
||||
|
||||
pub trait PassthroughHandlerTypes: Adapter {}
|
||||
impl<T> HandlerTypes for T
|
||||
where
|
||||
T: PassthroughHandlerTypes,
|
||||
T::Inner: HandlerTypes,
|
||||
{
|
||||
type Params = <T::Inner as HandlerTypes>::Params;
|
||||
type InheritedParams = <T::Inner as HandlerTypes>::InheritedParams;
|
||||
type Ok = <T::Inner as HandlerTypes>::Ok;
|
||||
type Err = <T::Inner as HandlerTypes>::Err;
|
||||
}
|
||||
|
||||
pub trait LeafHandler {}
|
||||
|
||||
pub trait HandlerTS {
|
||||
fn type_info(&self) -> Option<String>;
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "ts"))]
|
||||
impl<T: HandlerTypes> HandlerTS for T {
|
||||
fn type_info(&self) -> Option<String> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub trait HandlerRequires: HandlerTypes + Clone + Send + Sync + 'static {}
|
||||
impl<T: HandlerTypes + Clone + Send + Sync + 'static> HandlerRequires for T {}
|
||||
|
||||
@@ -293,6 +286,70 @@ pub trait HandlerFor<Context: crate::Context>: HandlerRequires {
|
||||
}
|
||||
}
|
||||
}
|
||||
pub trait PassthroughHandlerFor: PassthroughHandlerTypes {}
|
||||
impl<T, Context> HandlerFor<Context> for T
|
||||
where
|
||||
T: PassthroughHandlerFor + Clone + Send + Sync + 'static,
|
||||
T::Inner: HandlerFor<Context>,
|
||||
Context: crate::Context,
|
||||
{
|
||||
fn handle_async(
|
||||
&self,
|
||||
handle_args: HandlerArgsFor<Context, Self>,
|
||||
) -> impl Future<Output = Result<Self::Ok, Self::Err>> + Send {
|
||||
self.as_inner().handle_async(handle_args)
|
||||
}
|
||||
fn handle_async_with_sync<'a>(
|
||||
&'a self,
|
||||
handle_args: HandlerArgsFor<Context, Self>,
|
||||
) -> impl Future<Output = Result<Self::Ok, Self::Err>> + Send + 'a {
|
||||
self.as_inner().handle_async_with_sync(handle_args)
|
||||
}
|
||||
fn handle_async_with_sync_blocking<'a>(
|
||||
&'a self,
|
||||
handle_args: HandlerArgsFor<Context, Self>,
|
||||
) -> impl Future<Output = Result<Self::Ok, Self::Err>> + Send + 'a {
|
||||
self.as_inner().handle_async_with_sync_blocking(handle_args)
|
||||
}
|
||||
fn handle_sync(
|
||||
&self,
|
||||
handle_args: HandlerArgsFor<Context, Self>,
|
||||
) -> Result<Self::Ok, Self::Err> {
|
||||
self.as_inner().handle_sync(handle_args)
|
||||
}
|
||||
fn metadata(&self, method: VecDeque<&'static str>) -> OrdMap<&'static str, Value> {
|
||||
self.as_inner().metadata(method)
|
||||
}
|
||||
fn method_from_dots(&self, method: &str) -> Option<VecDeque<&'static str>> {
|
||||
self.as_inner().method_from_dots(method)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait PassthroughCliBindings: PassthroughHandlerTypes {}
|
||||
impl<T, Context> CliBindings<Context> for T
|
||||
where
|
||||
T: PassthroughCliBindings,
|
||||
T::Inner: CliBindings<Context>,
|
||||
Context: crate::Context,
|
||||
{
|
||||
const NO_CLI: bool = <T::Inner as CliBindings<Context>>::NO_CLI;
|
||||
fn cli_command(&self) -> Command {
|
||||
self.as_inner().cli_command()
|
||||
}
|
||||
fn cli_parse(
|
||||
&self,
|
||||
matches: &ArgMatches,
|
||||
) -> Result<(VecDeque<&'static str>, Value), clap::Error> {
|
||||
self.as_inner().cli_parse(matches)
|
||||
}
|
||||
fn cli_display(
|
||||
&self,
|
||||
handle_args: HandlerArgsFor<Context, Self>,
|
||||
result: Self::Ok,
|
||||
) -> Result<(), Self::Err> {
|
||||
self.as_inner().cli_display(handle_args, result)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Handler<Inherited> {
|
||||
type H: HandlerTypes;
|
||||
@@ -315,7 +372,7 @@ impl<Context, H> WithContext<Context, H> {
|
||||
impl<Context, Inherited, H> Handler<Inherited> for WithContext<Context, H>
|
||||
where
|
||||
Context: crate::Context,
|
||||
H: HandlerFor<Context> + CliBindings<Context> + HandlerTS,
|
||||
H: HandlerFor<Context> + CliBindings<Context> + HandlerTSBindings,
|
||||
H::Ok: Serialize + DeserializeOwned,
|
||||
H::Params: DeserializeOwned,
|
||||
H::InheritedParams: OrEmpty<Inherited>,
|
||||
@@ -356,12 +413,13 @@ impl<Context, Inherited, H: std::fmt::Debug> std::fmt::Debug for AnyHandler<Cont
|
||||
}
|
||||
}
|
||||
|
||||
impl<Context, Inherited, H> HandleAnyTS for AnyHandler<Context, Inherited, H>
|
||||
#[cfg(feature = "ts")]
|
||||
impl<Context, Inherited, H> HandlerTSBindings for AnyHandler<Context, Inherited, H>
|
||||
where
|
||||
H: HandlerTS,
|
||||
H: HandlerTSBindings,
|
||||
{
|
||||
fn type_info(&self) -> Option<String> {
|
||||
self.handler.type_info()
|
||||
fn get_ts<'a>(&'a self) -> Option<crate::ts::HandlerTS<'a>> {
|
||||
self.handler.get_ts()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -369,7 +427,7 @@ where
|
||||
impl<Context, Inherited, H> HandleAny<Context> for AnyHandler<Context, Inherited, H>
|
||||
where
|
||||
Context: crate::Context,
|
||||
H: HandlerFor<Context> + CliBindings<Context> + HandlerTS,
|
||||
H: HandlerFor<Context> + CliBindings<Context> + HandlerTSBindings,
|
||||
H::Params: DeserializeOwned,
|
||||
H::Ok: Serialize + DeserializeOwned,
|
||||
H::InheritedParams: OrEmpty<Inherited>,
|
||||
@@ -450,9 +508,9 @@ where
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Deserialize, Serialize, Parser)]
|
||||
#[cfg_attr(feature = "ts", derive(ts_rs::TS))]
|
||||
#[cfg_attr(feature = "ts", ts(type = "{}"))]
|
||||
#[cfg_attr(feature = "ts", derive(visit_rs::VisitFields))]
|
||||
pub struct Empty {}
|
||||
impl_ts_struct!(Empty);
|
||||
|
||||
pub trait OrEmpty<T> {
|
||||
fn from_t(t: T) -> Self;
|
||||
|
||||
@@ -7,15 +7,11 @@ use imbl_value::Value;
|
||||
use serde::Serialize;
|
||||
use yajrc::RpcError;
|
||||
|
||||
#[cfg(feature = "ts")]
|
||||
use crate::handler::HandleAnyTS;
|
||||
use crate::util::{combine, Flat, PhantomData};
|
||||
use crate::{
|
||||
CliBindings, DynHandler, Empty, HandleAny, HandleAnyArgs, Handler, HandlerArgs, HandlerArgsFor,
|
||||
HandlerFor, HandlerRequires, HandlerTypes, WithContext,
|
||||
};
|
||||
#[cfg(feature = "ts")]
|
||||
use crate::{CustomTS, UnknownTS};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub(crate) struct Name(pub(crate) &'static str);
|
||||
@@ -88,31 +84,6 @@ impl<Context, Params, InheritedParams> ParentHandler<Context, Params, InheritedP
|
||||
self.metadata.insert(key, value);
|
||||
self
|
||||
}
|
||||
#[cfg(feature = "ts")]
|
||||
fn type_info_impl(&self, params_ty: &str) -> Option<String> {
|
||||
use std::fmt::Write;
|
||||
let mut res = "{".to_owned();
|
||||
res.push_str("_CHILDREN:{");
|
||||
for (name, handler) in &self.subcommands.1 {
|
||||
let Some(ty) = handler.type_info() else {
|
||||
continue;
|
||||
};
|
||||
write!(
|
||||
&mut res,
|
||||
"{}:{};",
|
||||
serde_json::to_string(&name.0).unwrap(),
|
||||
ty,
|
||||
)
|
||||
.ok()?;
|
||||
}
|
||||
res.push_str("};}");
|
||||
if let Some(ty) = self.subcommands.0.as_ref().and_then(|h| h.type_info()) {
|
||||
write!(&mut res, "&{}", ty).ok()?;
|
||||
} else {
|
||||
write!(&mut res, "&{{_PARAMS:{}}}", params_ty).ok()?;
|
||||
}
|
||||
Some(res)
|
||||
}
|
||||
}
|
||||
impl<Context, Params, InheritedParams> Clone for ParentHandler<Context, Params, InheritedParams> {
|
||||
fn clone(&self) -> Self {
|
||||
@@ -169,40 +140,6 @@ where
|
||||
type Err = RpcError;
|
||||
}
|
||||
|
||||
#[cfg(feature = "ts")]
|
||||
impl<Context, Params, InheritedParams> crate::handler::HandlerTS
|
||||
for ParentHandler<Context, Params, InheritedParams>
|
||||
where
|
||||
Params: ts_rs::TS + Send + Sync + 'static,
|
||||
InheritedParams: Send + Sync + 'static,
|
||||
{
|
||||
fn type_info(&self) -> Option<String> {
|
||||
self.type_info_impl(&Params::inline_flattened())
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "ts")]
|
||||
impl<Context, Params, InheritedParams> crate::handler::HandlerTS
|
||||
for CustomTS<ParentHandler<Context, Params, InheritedParams>>
|
||||
where
|
||||
Params: Send + Sync + 'static,
|
||||
InheritedParams: Send + Sync + 'static,
|
||||
{
|
||||
fn type_info(&self) -> Option<String> {
|
||||
self.handler.type_info_impl(&self.params_ty)
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "ts")]
|
||||
impl<Context, Params, InheritedParams> crate::handler::HandlerTS
|
||||
for UnknownTS<ParentHandler<Context, Params, InheritedParams>>
|
||||
where
|
||||
Params: Send + Sync + 'static,
|
||||
InheritedParams: Send + Sync + 'static,
|
||||
{
|
||||
fn type_info(&self) -> Option<String> {
|
||||
self.0.type_info_impl("unknown")
|
||||
}
|
||||
}
|
||||
|
||||
impl<Context, Params, InheritedParams> HandlerFor<Context>
|
||||
for ParentHandler<Context, Params, InheritedParams>
|
||||
where
|
||||
|
||||
@@ -14,4 +14,8 @@ mod server;
|
||||
pub mod ts;
|
||||
pub mod util;
|
||||
|
||||
#[cfg(feature = "ts")]
|
||||
#[cfg(not(feature = "ts"))]
|
||||
pub mod ts {
|
||||
pub trait HandlerTSBindings {}
|
||||
impl<T> HandlerTSBindings for T {}
|
||||
}
|
||||
|
||||
471
src/ts.rs
471
src/ts.rs
@@ -1,70 +1,338 @@
|
||||
use visit_rs::{NamedStatic, Static, Visit, VisitFieldsStaticNamed, Visitor};
|
||||
use std::borrow::Cow;
|
||||
use std::collections::BTreeMap;
|
||||
use std::ops::Deref;
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
|
||||
use imbl_value::imbl::{OrdMap, Vector};
|
||||
use imbl_value::InternedString;
|
||||
use visit_rs::{Named, Static, Visit, VisitFieldsStaticNamed, Visitor};
|
||||
|
||||
use crate::{Adapter, FromFn, FromFnAsync, FromFnAsyncLocal, HandlerTypes, ParentHandler};
|
||||
|
||||
pub fn type_helpers() -> &'static str {
|
||||
include_str!("./type-helpers.ts")
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub trait TS {
|
||||
const DEFINE: Option<&str> = None;
|
||||
const IS_ENUMERABLE: bool = false;
|
||||
fn visit_ts(visitor: &mut TSVisitor);
|
||||
}
|
||||
|
||||
impl<T> Visit<TSVisitor> for Static<T>
|
||||
where
|
||||
T: TS,
|
||||
{
|
||||
fn visit(&self, visitor: &mut TSVisitor) -> <TSVisitor as Visitor>::Result {
|
||||
T::visit_ts(visitor);
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ParamsTS {
|
||||
fn params_ts<'a>(&'a self) -> Box<dyn Fn(&mut TSVisitor) + Send + Sync + 'a>;
|
||||
}
|
||||
pub trait ReturnTS {
|
||||
fn return_ts<'a>(&'a self) -> Option<Box<dyn Fn(&mut TSVisitor) + Send + Sync + 'a>>;
|
||||
}
|
||||
pub trait ChildrenTS {
|
||||
fn children_ts<'a>(&'a self) -> Option<Box<dyn Fn(&mut TSVisitor) + Send + Sync + 'a>>;
|
||||
}
|
||||
|
||||
pub trait HandlerTSBindings {
|
||||
fn get_ts<'a>(&'a self) -> Option<HandlerTS<'a>>;
|
||||
}
|
||||
impl<T: ParamsTS + ReturnTS + ChildrenTS> HandlerTSBindings for T {
|
||||
fn get_ts<'a>(&'a self) -> Option<HandlerTS<'a>> {
|
||||
Some(HandlerTS::new(self))
|
||||
}
|
||||
}
|
||||
impl<T: HandlerTSBindings> HandlerTSBindings for Arc<T> {
|
||||
fn get_ts<'a>(&'a self) -> Option<HandlerTS<'a>> {
|
||||
self.deref().get_ts()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct HandlerTS<'a> {
|
||||
params_ts: Box<dyn Fn(&mut TSVisitor) + Send + Sync + 'a>,
|
||||
return_ts: Option<Box<dyn Fn(&mut TSVisitor) + Send + Sync + 'a>>,
|
||||
children: Option<Box<dyn Fn(&mut TSVisitor) + Send + Sync + 'a>>,
|
||||
}
|
||||
impl<'a> HandlerTS<'a> {
|
||||
pub fn new<H>(handler: &'a H) -> Self
|
||||
where
|
||||
H: ParamsTS + ReturnTS + ChildrenTS,
|
||||
{
|
||||
Self {
|
||||
params_ts: handler.params_ts(),
|
||||
return_ts: handler.return_ts(),
|
||||
children: handler.children_ts(),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<'a> Visit<TSVisitor> for HandlerTS<'a> {
|
||||
fn visit(&self, visitor: &mut TSVisitor) -> <TSVisitor as Visitor>::Result {
|
||||
visitor.ts.push_str("{_PARAMS:");
|
||||
(self.params_ts)(visitor);
|
||||
if let Some(return_ty) = &self.return_ts {
|
||||
visitor.ts.push_str(";_RETURN:");
|
||||
return_ty(visitor);
|
||||
}
|
||||
if let Some(children) = &self.children {
|
||||
visitor.ts.push_str(";_CHILDREN:");
|
||||
children(visitor);
|
||||
}
|
||||
visitor.ts.push_str("}");
|
||||
}
|
||||
}
|
||||
|
||||
impl<F, T, E, Args> ParamsTS for FromFn<F, T, E, Args>
|
||||
where
|
||||
Self: HandlerTypes,
|
||||
Static<<Self as HandlerTypes>::Params>: Visit<TSVisitor>,
|
||||
{
|
||||
fn params_ts(&self) -> Box<dyn Fn(&mut TSVisitor) + Send + Sync> {
|
||||
Box::new(|visitor| Static::<<Self as HandlerTypes>::Params>::new().visit(visitor))
|
||||
}
|
||||
}
|
||||
impl<F, Fut, T, E, Args> ParamsTS for FromFnAsync<F, Fut, T, E, Args>
|
||||
where
|
||||
Self: HandlerTypes,
|
||||
Static<<Self as HandlerTypes>::Params>: Visit<TSVisitor>,
|
||||
{
|
||||
fn params_ts(&self) -> Box<dyn Fn(&mut TSVisitor) + Send + Sync> {
|
||||
Box::new(|visitor| Static::<<Self as HandlerTypes>::Params>::new().visit(visitor))
|
||||
}
|
||||
}
|
||||
impl<F, Fut, T, E, Args> ParamsTS for FromFnAsyncLocal<F, Fut, T, E, Args>
|
||||
where
|
||||
Self: HandlerTypes,
|
||||
Static<<Self as HandlerTypes>::Params>: Visit<TSVisitor>,
|
||||
{
|
||||
fn params_ts(&self) -> Box<dyn Fn(&mut TSVisitor) + Send + Sync> {
|
||||
Box::new(|visitor| Static::<<Self as HandlerTypes>::Params>::new().visit(visitor))
|
||||
}
|
||||
}
|
||||
impl<Context, Params, InheritedParams> ParamsTS for ParentHandler<Context, Params, InheritedParams>
|
||||
where
|
||||
Self: HandlerTypes,
|
||||
Static<<Self as HandlerTypes>::Params>: Visit<TSVisitor>,
|
||||
{
|
||||
fn params_ts(&self) -> Box<dyn Fn(&mut TSVisitor) + Send + Sync> {
|
||||
Box::new(|visitor| Static::<<Self as HandlerTypes>::Params>::new().visit(visitor))
|
||||
}
|
||||
}
|
||||
|
||||
impl<F, T, E, Args> ReturnTS for FromFn<F, T, E, Args>
|
||||
where
|
||||
Self: HandlerTypes,
|
||||
Static<<Self as HandlerTypes>::Ok>: Visit<TSVisitor>,
|
||||
{
|
||||
fn return_ts(&self) -> Option<Box<dyn Fn(&mut TSVisitor) + Send + Sync>> {
|
||||
Some(Box::new(|visitor| {
|
||||
Static::<<Self as HandlerTypes>::Ok>::new().visit(visitor)
|
||||
}))
|
||||
}
|
||||
}
|
||||
impl<F, Fut, T, E, Args> ReturnTS for FromFnAsync<F, Fut, T, E, Args>
|
||||
where
|
||||
Self: HandlerTypes,
|
||||
Static<<Self as HandlerTypes>::Ok>: Visit<TSVisitor>,
|
||||
{
|
||||
fn return_ts(&self) -> Option<Box<dyn Fn(&mut TSVisitor) + Send + Sync>> {
|
||||
Some(Box::new(|visitor| {
|
||||
Static::<<Self as HandlerTypes>::Ok>::new().visit(visitor)
|
||||
}))
|
||||
}
|
||||
}
|
||||
impl<F, Fut, T, E, Args> ReturnTS for FromFnAsyncLocal<F, Fut, T, E, Args>
|
||||
where
|
||||
Self: HandlerTypes,
|
||||
Static<<Self as HandlerTypes>::Ok>: Visit<TSVisitor>,
|
||||
{
|
||||
fn return_ts(&self) -> Option<Box<dyn Fn(&mut TSVisitor) + Send + Sync>> {
|
||||
Some(Box::new(|visitor| {
|
||||
Static::<<Self as HandlerTypes>::Ok>::new().visit(visitor)
|
||||
}))
|
||||
}
|
||||
}
|
||||
impl<Context, Params, InheritedParams> ReturnTS
|
||||
for ParentHandler<Context, Params, InheritedParams>
|
||||
{
|
||||
fn return_ts(&self) -> Option<Box<dyn Fn(&mut TSVisitor) + Send + Sync>> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl<F, T, E, Args> ChildrenTS for FromFn<F, T, E, Args> {
|
||||
fn children_ts(&self) -> Option<Box<dyn Fn(&mut TSVisitor) + Send + Sync>> {
|
||||
None
|
||||
}
|
||||
}
|
||||
impl<F, Fut, T, E, Args> ChildrenTS for FromFnAsync<F, Fut, T, E, Args> {
|
||||
fn children_ts(&self) -> Option<Box<dyn Fn(&mut TSVisitor) + Send + Sync>> {
|
||||
None
|
||||
}
|
||||
}
|
||||
impl<F, Fut, T, E, Args> ChildrenTS for FromFnAsyncLocal<F, Fut, T, E, Args> {
|
||||
fn children_ts(&self) -> Option<Box<dyn Fn(&mut TSVisitor) + Send + Sync>> {
|
||||
None
|
||||
}
|
||||
}
|
||||
impl<Context, Params, InheritedParams> ChildrenTS
|
||||
for ParentHandler<Context, Params, InheritedParams>
|
||||
where
|
||||
Context: crate::Context,
|
||||
Params: Send + Sync + 'static,
|
||||
InheritedParams: Send + Sync + 'static,
|
||||
{
|
||||
fn children_ts<'a>(&'a self) -> Option<Box<dyn Fn(&mut TSVisitor) + Send + Sync + 'a>> {
|
||||
use std::fmt::Write;
|
||||
|
||||
Some(Box::new(move |visitor| {
|
||||
visitor.ts.push('{');
|
||||
for (name, handler) in &self.subcommands.1 {
|
||||
write!(
|
||||
&mut visitor.ts,
|
||||
"{}:",
|
||||
serde_json::to_string(&name.0).unwrap()
|
||||
)
|
||||
.ok();
|
||||
// Call get_ts on the handler
|
||||
if let Some(ts) = handler.0.get_ts() {
|
||||
ts.visit(visitor);
|
||||
} else {
|
||||
visitor.ts.push_str("unknown");
|
||||
}
|
||||
visitor.ts.push(';');
|
||||
}
|
||||
visitor.ts.push('}');
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
pub trait PassthroughParamsTS: Adapter {}
|
||||
impl<T: PassthroughParamsTS> ParamsTS for T
|
||||
where
|
||||
T::Inner: ParamsTS,
|
||||
{
|
||||
fn params_ts<'a>(&'a self) -> Box<dyn Fn(&mut TSVisitor) + Send + Sync + 'a> {
|
||||
self.as_inner().params_ts()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait PassthroughReturnTS: Adapter {}
|
||||
impl<T: PassthroughReturnTS> ReturnTS for T
|
||||
where
|
||||
T::Inner: ReturnTS,
|
||||
{
|
||||
fn return_ts<'a>(&'a self) -> Option<Box<dyn Fn(&mut TSVisitor) + Send + Sync + 'a>> {
|
||||
self.as_inner().return_ts()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait PassthroughChildrenTS: Adapter {}
|
||||
impl<T: PassthroughChildrenTS> ChildrenTS for T
|
||||
where
|
||||
T::Inner: ChildrenTS,
|
||||
{
|
||||
fn children_ts<'a>(&'a self) -> Option<Box<dyn Fn(&mut TSVisitor) + Send + Sync + 'a>> {
|
||||
self.as_inner().children_ts()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone)]
|
||||
pub struct TSVisitor {
|
||||
pub definitions: BTreeMap<&'static str, String>,
|
||||
pub ts: String,
|
||||
}
|
||||
impl TSVisitor {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
pub fn visit_ty<V: Visit<Self>>(&mut self, value: &V, define: Option<&'static str>) {
|
||||
if let Some(name) = define {
|
||||
self.ts.push_str(name);
|
||||
let mut defn = Self::new();
|
||||
value.visit(&mut defn);
|
||||
self.load_definition(name, defn);
|
||||
} else {
|
||||
value.visit(self);
|
||||
}
|
||||
}
|
||||
pub fn append_type<T>(&mut self)
|
||||
where
|
||||
T: TS,
|
||||
{
|
||||
self.visit_ty(&Static::<T>::new(), T::DEFINE);
|
||||
}
|
||||
pub fn insert_definition(&mut self, name: &'static str, definition: String) {
|
||||
if let Some(def) = self.definitions.get(&name) {
|
||||
assert_eq!(def, &definition, "Conflicting definitions for {name}");
|
||||
}
|
||||
debug_assert!(!definition.is_empty());
|
||||
self.definitions.insert(name, definition);
|
||||
}
|
||||
pub fn load_definition(
|
||||
&mut self,
|
||||
name: &'static str,
|
||||
TSVisitor { definitions, ts }: TSVisitor,
|
||||
) {
|
||||
for (name, definition) in definitions {
|
||||
self.insert_definition(name, definition);
|
||||
}
|
||||
self.insert_definition(name, ts);
|
||||
}
|
||||
}
|
||||
impl Visitor for TSVisitor {
|
||||
type Result = ();
|
||||
}
|
||||
|
||||
impl<T> Visit<TSVisitor> for Static<T>
|
||||
where
|
||||
T: VisitFieldsStaticNamed<TSVisitor>,
|
||||
{
|
||||
fn visit(&self, visitor: &mut TSVisitor) -> <TSVisitor as Visitor>::Result {
|
||||
if Self::IS_NAMED {
|
||||
visitor.ts.push_str("{");
|
||||
} else {
|
||||
visitor.ts.push_str("[");
|
||||
}
|
||||
self.visit_fields_static_named(visitor).collect::<()>();
|
||||
if Self::IS_NAMED {
|
||||
visitor.ts.push_str("}");
|
||||
} else {
|
||||
visitor.ts.push_str("]");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Visit<TSVisitor> for NamedStatic<T>
|
||||
impl<'a, T> Visit<TSVisitor> for Named<'a, Static<T>>
|
||||
where
|
||||
T: TS,
|
||||
Static<T>: Visit<TSVisitor>,
|
||||
{
|
||||
fn visit(&self, visitor: &mut TSVisitor) -> <TSVisitor as Visitor>::Result {
|
||||
use std::fmt::Write;
|
||||
|
||||
if let Some(name) = self.name {
|
||||
if name.chars().all(|c| c.is_alphanumeric() || c == '_')
|
||||
&& name.chars().next().map_or(false, |c| c.is_alphabetic())
|
||||
{
|
||||
visitor.ts.push_str(name);
|
||||
} else {
|
||||
write!(
|
||||
&mut visitor.ts,
|
||||
"[{}]",
|
||||
serde_json::to_string(&name).unwrap()
|
||||
)
|
||||
.unwrap();
|
||||
write!(&mut visitor.ts, "{}", serde_json::to_string(&name).unwrap()).unwrap();
|
||||
}
|
||||
visitor.ts.push_str(":");
|
||||
}
|
||||
Static::<T>::new().visit(visitor);
|
||||
visitor.append_type::<T>();
|
||||
if self.name.is_some() {
|
||||
visitor.ts.push(";");
|
||||
visitor.ts.push(';');
|
||||
} else {
|
||||
visitor.ts.push(",");
|
||||
visitor.ts.push(',');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct LiteralTS(Cow<'static, str>);
|
||||
impl Visit<TSVisitor> for LiteralTS {
|
||||
fn visit(&self, visitor: &mut TSVisitor) -> <TSVisitor as Visitor>::Result {
|
||||
visitor.ts.push_str(&*self.0);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct Unknown;
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub enum Never {}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! impl_ts {
|
||||
($($ty:ty),+ => $ts:expr) => {
|
||||
$(
|
||||
impl Visit<TSVisitor> for Static<$ty> {
|
||||
fn visit(&self, visitor: &mut TSVisitor) -> <TSVisitor as Visitor>::Result {
|
||||
impl $crate::ts::TS for $ty {
|
||||
fn visit_ts(visitor: &mut $crate::ts::TSVisitor) {
|
||||
visitor.ts.push_str($ts);
|
||||
}
|
||||
}
|
||||
@@ -72,6 +340,141 @@ macro_rules! impl_ts {
|
||||
};
|
||||
}
|
||||
|
||||
impl_ts!(String => "string");
|
||||
impl_ts!(bool => "boolean");
|
||||
impl_ts!(String,str,InternedString => "string");
|
||||
impl_ts!(usize,u8,u16,u32,isize,i8,i16,i32,f32,f64 => "number");
|
||||
impl_ts!(u64,u128,i64,i128 => "bigint");
|
||||
impl_ts!(Unknown,imbl_value::Value,serde_json::Value,serde_cbor::Value => "unknown");
|
||||
impl_ts!(Never => "never");
|
||||
|
||||
pub fn visit_struct<T>(visitor: &mut TSVisitor)
|
||||
where
|
||||
T: VisitFieldsStaticNamed<TSVisitor>,
|
||||
{
|
||||
if T::NAMED_FIELDS {
|
||||
visitor.ts.push_str("{");
|
||||
} else {
|
||||
visitor.ts.push_str("[");
|
||||
}
|
||||
T::visit_fields_static_named(visitor).for_each(drop);
|
||||
if T::NAMED_FIELDS {
|
||||
visitor.ts.push_str("}");
|
||||
} else {
|
||||
visitor.ts.push_str("]");
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! impl_ts_struct {
|
||||
($ty:ty $({ define: $name:expr })?) => {
|
||||
impl $crate::ts::TS for $ty {
|
||||
$(const DEFINE: Option<&str> = Some($name);)?
|
||||
fn visit_ts(visitor: &mut $crate::ts::TSVisitor) {
|
||||
$crate::ts::visit_struct::<$ty>(visitor)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn visit_map<K, V>(visitor: &mut TSVisitor)
|
||||
where
|
||||
K: TS,
|
||||
V: TS,
|
||||
{
|
||||
visitor.ts.push_str("{[key");
|
||||
if K::IS_ENUMERABLE {
|
||||
visitor.ts.push_str(" in ");
|
||||
} else {
|
||||
visitor.ts.push(':');
|
||||
}
|
||||
visitor.append_type::<K>();
|
||||
visitor.ts.push(']');
|
||||
if K::IS_ENUMERABLE {
|
||||
visitor.ts.push('?');
|
||||
}
|
||||
visitor.ts.push(':');
|
||||
visitor.append_type::<V>();
|
||||
visitor.ts.push('}');
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! impl_ts_map {
|
||||
($(
|
||||
$ty:ty
|
||||
$(where [$($bounds:tt)*])?
|
||||
),+ $(,)?) => {
|
||||
$(
|
||||
impl<K, V> $crate::ts::TS for $ty
|
||||
where
|
||||
K: $crate::ts::TS,
|
||||
V: $crate::ts::TS,
|
||||
$($($bounds)*,)?
|
||||
{
|
||||
fn visit_ts(visitor: &mut $crate::ts::TSVisitor) {
|
||||
$crate::ts::visit_map::<K, V>(visitor)
|
||||
}
|
||||
}
|
||||
)+
|
||||
};
|
||||
}
|
||||
|
||||
impl_ts_map!(
|
||||
std::collections::HashMap<K,V>,
|
||||
imbl_value::imbl::HashMap<K,V>,
|
||||
imbl_value::InOMap<K,V> where [K: Eq + Clone, V: Clone],
|
||||
BTreeMap<K,V>,
|
||||
OrdMap<K,V>,
|
||||
);
|
||||
|
||||
pub fn visit_array<T>(visitor: &mut TSVisitor)
|
||||
where
|
||||
T: TS,
|
||||
{
|
||||
visitor.ts.push('(');
|
||||
visitor.append_type::<T>();
|
||||
visitor.ts.push_str(")[]");
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! impl_ts_array {
|
||||
($(
|
||||
$ty:ty
|
||||
$(where [$($bounds:tt)*])?
|
||||
),+ $(,)?) => {
|
||||
$(
|
||||
impl<T> $crate::ts::TS for $ty
|
||||
where
|
||||
T: $crate::ts::TS,
|
||||
$($($bounds)*,)?
|
||||
{
|
||||
fn visit_ts(visitor: &mut $crate::ts::TSVisitor) {
|
||||
$crate::ts::visit_array::<T>(visitor)
|
||||
}
|
||||
}
|
||||
)+
|
||||
};
|
||||
}
|
||||
|
||||
impl_ts_array!(Vec<T>, Vector<T>);
|
||||
|
||||
impl<T: TS> TS for Box<T> {
|
||||
const DEFINE: Option<&str> = T::DEFINE;
|
||||
const IS_ENUMERABLE: bool = T::IS_ENUMERABLE;
|
||||
fn visit_ts(visitor: &mut TSVisitor) {
|
||||
T::visit_ts(visitor);
|
||||
}
|
||||
}
|
||||
impl<T: TS> TS for Arc<T> {
|
||||
const DEFINE: Option<&str> = T::DEFINE;
|
||||
const IS_ENUMERABLE: bool = T::IS_ENUMERABLE;
|
||||
fn visit_ts(visitor: &mut TSVisitor) {
|
||||
T::visit_ts(visitor);
|
||||
}
|
||||
}
|
||||
impl<T: TS> TS for Rc<T> {
|
||||
const DEFINE: Option<&str> = T::DEFINE;
|
||||
const IS_ENUMERABLE: bool = T::IS_ENUMERABLE;
|
||||
fn visit_ts(visitor: &mut TSVisitor) {
|
||||
T::visit_ts(visitor);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
#![recursion_limit = "512"]
|
||||
|
||||
use clap::Parser;
|
||||
use rpc_toolkit::ts::HandlerTSBindings;
|
||||
use rpc_toolkit::{
|
||||
from_fn, from_fn_async, Context, Empty, HandlerExt, HandlerTS, ParentHandler, Server,
|
||||
from_fn, from_fn_async, impl_ts_struct, Context, Empty, HandlerExt, ParentHandler, Server,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use yajrc::RpcError;
|
||||
@@ -11,10 +14,12 @@ struct TestContext;
|
||||
impl Context for TestContext {}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, Parser)]
|
||||
#[cfg_attr(feature = "ts", derive(ts_rs::TS))]
|
||||
#[cfg_attr(feature = "ts", derive(visit_rs::VisitFields))]
|
||||
struct Thing1Params {
|
||||
thing: String,
|
||||
}
|
||||
#[cfg(feature = "ts")]
|
||||
impl_ts_struct!(Thing1Params);
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, Parser)]
|
||||
struct NoTSParams {
|
||||
@@ -30,11 +35,13 @@ fn no_ts_handler(_ctx: TestContext, params: NoTSParams) -> Result<String, RpcErr
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, Parser)]
|
||||
#[cfg_attr(feature = "ts", derive(ts_rs::TS))]
|
||||
#[cfg_attr(feature = "ts", derive(visit_rs::VisitFields))]
|
||||
struct GroupParams {
|
||||
#[arg(short, long)]
|
||||
verbose: bool,
|
||||
}
|
||||
#[cfg(feature = "ts")]
|
||||
impl_ts_struct!(GroupParams);
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_basic_server() {
|
||||
@@ -53,7 +60,17 @@ async fn test_basic_server() {
|
||||
.subcommand("no-ts", from_fn(no_ts_handler).no_ts()),
|
||||
);
|
||||
|
||||
println!("{}", root_handler.type_info().unwrap_or_default());
|
||||
println!(
|
||||
"{:?}",
|
||||
root_handler.get_ts().map(|t| {
|
||||
use rpc_toolkit::ts::TSVisitor;
|
||||
use visit_rs::Visit;
|
||||
|
||||
let mut ts = TSVisitor::new();
|
||||
t.visit(&mut ts);
|
||||
ts
|
||||
})
|
||||
);
|
||||
|
||||
let server = Server::new(|| async { Ok(TestContext) }, root_handler);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user