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]]
|
[[package]]
|
||||||
name = "visit-rs"
|
name = "visit-rs"
|
||||||
version = "0.1.5"
|
version = "0.1.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1ea0fdb05dd98029f7e226ccd11bd7184ad68566365547930bdde9ae79b5ce3d"
|
checksum = "fbd4baf031bf580bc7ba411f4625a304909c975bf3d3e8d9c43ed88d23be1b8e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-stream",
|
"async-stream",
|
||||||
"futures",
|
"futures",
|
||||||
@@ -1757,9 +1757,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "visit-rs-derive"
|
name = "visit-rs-derive"
|
||||||
version = "0.1.3"
|
version = "0.1.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8dde3216fefaf10421d9509f88bc5a480b5fb3fe48779ca4f148e75896d3f5ad"
|
checksum = "2336cc680cfb205620939f0fa62348a4b6da37d1e727670f3d7d77aebc50b611"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
|||||||
@@ -39,5 +39,5 @@ thiserror = "2.0"
|
|||||||
tokio = { version = "1", features = ["full"] }
|
tokio = { version = "1", features = ["full"] }
|
||||||
tokio-stream = { version = "0.1", features = ["io-util", "net"] }
|
tokio-stream = { version = "0.1", features = ["io-util", "net"] }
|
||||||
url = "2"
|
url = "2"
|
||||||
visit-rs = { version = "0.1.5", optional = true }
|
visit-rs = { version = "0.1.8", optional = true }
|
||||||
yajrc = "0.1"
|
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 url::Url;
|
||||||
use yajrc::{Id, RpcError};
|
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::util::{internal_error, invalid_params, parse_error, without, Flat, PhantomData};
|
||||||
use crate::{
|
use crate::{
|
||||||
AnyHandler, CliBindings, CliBindingsAny, Empty, HandleAny, HandleAnyArgs, HandlerArgs,
|
Adapter, AnyHandler, CliBindings, CliBindingsAny, Empty, HandleAny, HandleAnyArgs, HandlerArgs,
|
||||||
HandlerArgsFor, HandlerFor, HandlerTypes, Name, ParentHandler, PrintCliResult,
|
HandlerArgsFor, HandlerFor, HandlerTypes, Name, ParentHandler, PrintCliResult,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -209,15 +211,22 @@ where
|
|||||||
type Err = RemoteHandler::Err;
|
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")]
|
#[cfg(feature = "ts")]
|
||||||
impl<Context, RemoteContext, RemoteHandler, Extra> crate::handler::HandlerTS
|
impl<Context, RemoteContext, RemoteHandler, Extra> HandlerTSBindings
|
||||||
for CallRemoteHandler<Context, RemoteContext, RemoteHandler, Extra>
|
for CallRemoteHandler<Context, RemoteContext, RemoteHandler, Extra>
|
||||||
where
|
where
|
||||||
RemoteHandler: crate::handler::HandlerTS,
|
RemoteHandler: HandlerTSBindings,
|
||||||
Extra: Send + Sync + 'static,
|
|
||||||
{
|
{
|
||||||
fn type_info(&self) -> Option<String> {
|
fn get_ts<'a>(&'a self) -> Option<crate::ts::HandlerTS<'a>> {
|
||||||
self.handler.type_info()
|
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::de::DeserializeOwned;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
#[cfg(feature = "ts")]
|
|
||||||
use crate::ts::TSVisitor;
|
|
||||||
use crate::util::PhantomData;
|
use crate::util::PhantomData;
|
||||||
use crate::{
|
use crate::{
|
||||||
CliBindings, Empty, HandlerArgs, HandlerArgsFor, HandlerFor, HandlerTypes, LeafHandler,
|
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>
|
impl<Context, F, T, E, Args> PrintCliResult<Context> for FromFn<F, T, E, Args>
|
||||||
where
|
where
|
||||||
Context: crate::Context,
|
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>
|
impl<Context, F, Fut, T, E, Args> PrintCliResult<Context> for FromFnAsync<F, Fut, T, E, Args>
|
||||||
where
|
where
|
||||||
Context: crate::Context,
|
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>
|
impl<Context, F, Fut, T, E, Args> PrintCliResult<Context> for FromFnAsyncLocal<F, Fut, T, E, Args>
|
||||||
where
|
where
|
||||||
Context: crate::Context,
|
Context: crate::Context,
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ use serde::de::DeserializeOwned;
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use yajrc::RpcError;
|
use yajrc::RpcError;
|
||||||
|
|
||||||
|
use crate::impl_ts_struct;
|
||||||
|
use crate::ts::HandlerTSBindings;
|
||||||
use crate::util::{internal_error, invalid_params, Flat};
|
use crate::util::{internal_error, invalid_params, Flat};
|
||||||
|
|
||||||
pub mod adapters;
|
pub mod adapters;
|
||||||
@@ -55,21 +57,8 @@ impl<Context: crate::Context, Inherited: Send + Sync> HandleAnyArgs<Context, Inh
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) trait HandleAnyTS {
|
pub(crate) trait HandleAnyRequires: HandlerTSBindings + Send + Sync {}
|
||||||
#[allow(dead_code)]
|
impl<T: HandlerTSBindings + Send + Sync> HandleAnyRequires for T {}
|
||||||
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 {}
|
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
pub(crate) trait HandleAny<Context>: HandleAnyRequires {
|
pub(crate) trait HandleAny<Context>: HandleAnyRequires {
|
||||||
@@ -149,7 +138,9 @@ pub trait PrintCliResult<Context: crate::Context>: HandlerTypes {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[allow(private_interfaces)]
|
#[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> {
|
impl<Context: crate::Context, Inherited> DynHandler<Context, Inherited> {
|
||||||
pub fn new<C, H>(handler: H) -> Option<Self>
|
pub fn new<C, H>(handler: H) -> Option<Self>
|
||||||
where
|
where
|
||||||
@@ -169,11 +160,6 @@ impl<Context, Inherited> Debug for DynHandler<Context, Inherited> {
|
|||||||
f.debug_struct("DynHandler").finish()
|
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]
|
#[async_trait::async_trait]
|
||||||
impl<Context: crate::Context, Inherited: Send> HandleAny<Context>
|
impl<Context: crate::Context, Inherited: Send> HandleAny<Context>
|
||||||
for DynHandler<Context, Inherited>
|
for DynHandler<Context, Inherited>
|
||||||
@@ -201,6 +187,12 @@ impl<Context: crate::Context, Inherited: Send> HandleAny<Context>
|
|||||||
self.0.cli()
|
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)]
|
#[allow(type_alias_bounds)]
|
||||||
pub type HandlerArgsFor<Context: crate::Context, H: HandlerTypes + ?Sized> =
|
pub type HandlerArgsFor<Context: crate::Context, H: HandlerTypes + ?Sized> =
|
||||||
@@ -227,19 +219,20 @@ pub trait HandlerTypes {
|
|||||||
type Err: Send + Sync;
|
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 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 {}
|
pub trait HandlerRequires: HandlerTypes + Clone + Send + Sync + 'static {}
|
||||||
impl<T: HandlerTypes + Clone + Send + Sync + 'static> HandlerRequires for T {}
|
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> {
|
pub trait Handler<Inherited> {
|
||||||
type H: HandlerTypes;
|
type H: HandlerTypes;
|
||||||
@@ -315,7 +372,7 @@ impl<Context, H> WithContext<Context, H> {
|
|||||||
impl<Context, Inherited, H> Handler<Inherited> for WithContext<Context, H>
|
impl<Context, Inherited, H> Handler<Inherited> for WithContext<Context, H>
|
||||||
where
|
where
|
||||||
Context: crate::Context,
|
Context: crate::Context,
|
||||||
H: HandlerFor<Context> + CliBindings<Context> + HandlerTS,
|
H: HandlerFor<Context> + CliBindings<Context> + HandlerTSBindings,
|
||||||
H::Ok: Serialize + DeserializeOwned,
|
H::Ok: Serialize + DeserializeOwned,
|
||||||
H::Params: DeserializeOwned,
|
H::Params: DeserializeOwned,
|
||||||
H::InheritedParams: OrEmpty<Inherited>,
|
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
|
where
|
||||||
H: HandlerTS,
|
H: HandlerTSBindings,
|
||||||
{
|
{
|
||||||
fn type_info(&self) -> Option<String> {
|
fn get_ts<'a>(&'a self) -> Option<crate::ts::HandlerTS<'a>> {
|
||||||
self.handler.type_info()
|
self.handler.get_ts()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -369,7 +427,7 @@ where
|
|||||||
impl<Context, Inherited, H> HandleAny<Context> for AnyHandler<Context, Inherited, H>
|
impl<Context, Inherited, H> HandleAny<Context> for AnyHandler<Context, Inherited, H>
|
||||||
where
|
where
|
||||||
Context: crate::Context,
|
Context: crate::Context,
|
||||||
H: HandlerFor<Context> + CliBindings<Context> + HandlerTS,
|
H: HandlerFor<Context> + CliBindings<Context> + HandlerTSBindings,
|
||||||
H::Params: DeserializeOwned,
|
H::Params: DeserializeOwned,
|
||||||
H::Ok: Serialize + DeserializeOwned,
|
H::Ok: Serialize + DeserializeOwned,
|
||||||
H::InheritedParams: OrEmpty<Inherited>,
|
H::InheritedParams: OrEmpty<Inherited>,
|
||||||
@@ -450,9 +508,9 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Deserialize, Serialize, Parser)]
|
#[derive(Debug, Clone, Copy, Deserialize, Serialize, Parser)]
|
||||||
#[cfg_attr(feature = "ts", derive(ts_rs::TS))]
|
#[cfg_attr(feature = "ts", derive(visit_rs::VisitFields))]
|
||||||
#[cfg_attr(feature = "ts", ts(type = "{}"))]
|
|
||||||
pub struct Empty {}
|
pub struct Empty {}
|
||||||
|
impl_ts_struct!(Empty);
|
||||||
|
|
||||||
pub trait OrEmpty<T> {
|
pub trait OrEmpty<T> {
|
||||||
fn from_t(t: T) -> Self;
|
fn from_t(t: T) -> Self;
|
||||||
|
|||||||
@@ -7,15 +7,11 @@ use imbl_value::Value;
|
|||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use yajrc::RpcError;
|
use yajrc::RpcError;
|
||||||
|
|
||||||
#[cfg(feature = "ts")]
|
|
||||||
use crate::handler::HandleAnyTS;
|
|
||||||
use crate::util::{combine, Flat, PhantomData};
|
use crate::util::{combine, Flat, PhantomData};
|
||||||
use crate::{
|
use crate::{
|
||||||
CliBindings, DynHandler, Empty, HandleAny, HandleAnyArgs, Handler, HandlerArgs, HandlerArgsFor,
|
CliBindings, DynHandler, Empty, HandleAny, HandleAnyArgs, Handler, HandlerArgs, HandlerArgsFor,
|
||||||
HandlerFor, HandlerRequires, HandlerTypes, WithContext,
|
HandlerFor, HandlerRequires, HandlerTypes, WithContext,
|
||||||
};
|
};
|
||||||
#[cfg(feature = "ts")]
|
|
||||||
use crate::{CustomTS, UnknownTS};
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
pub(crate) struct Name(pub(crate) &'static str);
|
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.metadata.insert(key, value);
|
||||||
self
|
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> {
|
impl<Context, Params, InheritedParams> Clone for ParentHandler<Context, Params, InheritedParams> {
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
@@ -169,40 +140,6 @@ where
|
|||||||
type Err = RpcError;
|
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>
|
impl<Context, Params, InheritedParams> HandlerFor<Context>
|
||||||
for ParentHandler<Context, Params, InheritedParams>
|
for ParentHandler<Context, Params, InheritedParams>
|
||||||
where
|
where
|
||||||
|
|||||||
@@ -14,4 +14,8 @@ mod server;
|
|||||||
pub mod ts;
|
pub mod ts;
|
||||||
pub mod util;
|
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 {
|
pub fn type_helpers() -> &'static str {
|
||||||
include_str!("./type-helpers.ts")
|
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 struct TSVisitor {
|
||||||
|
pub definitions: BTreeMap<&'static str, String>,
|
||||||
pub ts: 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 {
|
impl Visitor for TSVisitor {
|
||||||
type Result = ();
|
type Result = ();
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Visit<TSVisitor> for Static<T>
|
impl<'a, T> Visit<TSVisitor> for Named<'a, 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>
|
|
||||||
where
|
where
|
||||||
|
T: TS,
|
||||||
Static<T>: Visit<TSVisitor>,
|
Static<T>: Visit<TSVisitor>,
|
||||||
{
|
{
|
||||||
fn visit(&self, visitor: &mut TSVisitor) -> <TSVisitor as Visitor>::Result {
|
fn visit(&self, visitor: &mut TSVisitor) -> <TSVisitor as Visitor>::Result {
|
||||||
|
use std::fmt::Write;
|
||||||
|
|
||||||
if let Some(name) = self.name {
|
if let Some(name) = self.name {
|
||||||
if name.chars().all(|c| c.is_alphanumeric() || c == '_')
|
if name.chars().all(|c| c.is_alphanumeric() || c == '_')
|
||||||
&& name.chars().next().map_or(false, |c| c.is_alphabetic())
|
&& name.chars().next().map_or(false, |c| c.is_alphabetic())
|
||||||
{
|
{
|
||||||
visitor.ts.push_str(name);
|
visitor.ts.push_str(name);
|
||||||
} else {
|
} else {
|
||||||
write!(
|
write!(&mut visitor.ts, "{}", serde_json::to_string(&name).unwrap()).unwrap();
|
||||||
&mut visitor.ts,
|
|
||||||
"[{}]",
|
|
||||||
serde_json::to_string(&name).unwrap()
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
visitor.ts.push_str(":");
|
visitor.ts.push_str(":");
|
||||||
}
|
}
|
||||||
Static::<T>::new().visit(visitor);
|
visitor.append_type::<T>();
|
||||||
if self.name.is_some() {
|
if self.name.is_some() {
|
||||||
visitor.ts.push(";");
|
visitor.ts.push(';');
|
||||||
} else {
|
} 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 {
|
macro_rules! impl_ts {
|
||||||
($($ty:ty),+ => $ts:expr) => {
|
($($ty:ty),+ => $ts:expr) => {
|
||||||
$(
|
$(
|
||||||
impl Visit<TSVisitor> for Static<$ty> {
|
impl $crate::ts::TS for $ty {
|
||||||
fn visit(&self, visitor: &mut TSVisitor) -> <TSVisitor as Visitor>::Result {
|
fn visit_ts(visitor: &mut $crate::ts::TSVisitor) {
|
||||||
visitor.ts.push_str($ts);
|
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!(usize,u8,u16,u32,isize,i8,i16,i32,f32,f64 => "number");
|
||||||
impl_ts!(u64,u128,i64,i128 => "bigint");
|
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 clap::Parser;
|
||||||
|
use rpc_toolkit::ts::HandlerTSBindings;
|
||||||
use rpc_toolkit::{
|
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 serde::{Deserialize, Serialize};
|
||||||
use yajrc::RpcError;
|
use yajrc::RpcError;
|
||||||
@@ -11,10 +14,12 @@ struct TestContext;
|
|||||||
impl Context for TestContext {}
|
impl Context for TestContext {}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Parser)]
|
#[derive(Debug, Deserialize, Serialize, Parser)]
|
||||||
#[cfg_attr(feature = "ts", derive(ts_rs::TS))]
|
#[cfg_attr(feature = "ts", derive(visit_rs::VisitFields))]
|
||||||
struct Thing1Params {
|
struct Thing1Params {
|
||||||
thing: String,
|
thing: String,
|
||||||
}
|
}
|
||||||
|
#[cfg(feature = "ts")]
|
||||||
|
impl_ts_struct!(Thing1Params);
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Parser)]
|
#[derive(Debug, Deserialize, Serialize, Parser)]
|
||||||
struct NoTSParams {
|
struct NoTSParams {
|
||||||
@@ -30,11 +35,13 @@ fn no_ts_handler(_ctx: TestContext, params: NoTSParams) -> Result<String, RpcErr
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Parser)]
|
#[derive(Debug, Deserialize, Serialize, Parser)]
|
||||||
#[cfg_attr(feature = "ts", derive(ts_rs::TS))]
|
#[cfg_attr(feature = "ts", derive(visit_rs::VisitFields))]
|
||||||
struct GroupParams {
|
struct GroupParams {
|
||||||
#[arg(short, long)]
|
#[arg(short, long)]
|
||||||
verbose: bool,
|
verbose: bool,
|
||||||
}
|
}
|
||||||
|
#[cfg(feature = "ts")]
|
||||||
|
impl_ts_struct!(GroupParams);
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_basic_server() {
|
async fn test_basic_server() {
|
||||||
@@ -53,7 +60,17 @@ async fn test_basic_server() {
|
|||||||
.subcommand("no-ts", from_fn(no_ts_handler).no_ts()),
|
.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);
|
let server = Server::new(|| async { Ok(TestContext) }, root_handler);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user