From c25bad48579c92e9606443c92cc36ca7be993aa9 Mon Sep 17 00:00:00 2001 From: Aiden McClelland Date: Wed, 12 Nov 2025 22:47:01 -0700 Subject: [PATCH] better TS --- Cargo.lock | 8 +- Cargo.toml | 2 +- src/cli.rs | 21 +- src/handler/adapters.rs | 952 ++++++++++++---------------------------- src/handler/from_fn.rs | 48 -- src/handler/mod.rs | 138 ++++-- src/handler/parent.rs | 63 --- src/lib.rs | 6 +- src/ts.rs | 471 ++++++++++++++++++-- tests/test.rs | 25 +- 10 files changed, 858 insertions(+), 876 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8814f1e..5698b6d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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", diff --git a/Cargo.toml b/Cargo.toml index 94c5f29..7df5969 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" diff --git a/src/cli.rs b/src/cli.rs index 05f28f2..7e10749 100644 --- a/src/cli.rs +++ b/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 Adapter + for CallRemoteHandler +{ + type Inner = RemoteHandler; + fn as_inner(&self) -> &Self::Inner { + &self.handler + } +} #[cfg(feature = "ts")] -impl crate::handler::HandlerTS +impl HandlerTSBindings for CallRemoteHandler where - RemoteHandler: crate::handler::HandlerTS, - Extra: Send + Sync + 'static, + RemoteHandler: HandlerTSBindings, { - fn type_info(&self) -> Option { - self.handler.type_info() + fn get_ts<'a>(&'a self) -> Option> { + self.handler.get_ts() } } diff --git a/src/handler/adapters.rs b/src/handler/adapters.rs index 16f2ca8..e387880 100644 --- a/src/handler/adapters.rs +++ b/src/handler/adapters.rs @@ -8,14 +8,27 @@ use imbl_value::imbl::OrdMap; use imbl_value::Value; use serde::de::DeserializeOwned; use serde::Serialize; +#[cfg(feature = "ts")] +use visit_rs::{Static, Visit}; use yajrc::RpcError; +#[cfg(feature = "ts")] +use crate::ts::{ + HandlerTS, HandlerTSBindings, PassthroughChildrenTS, PassthroughParamsTS, PassthroughReturnTS, + TSVisitor, +}; use crate::util::{Flat, PhantomData}; use crate::{ CallRemote, CallRemoteHandler, CliBindings, DynHandler, Handler, HandlerArgs, HandlerArgsFor, - HandlerFor, HandlerTypes, LeafHandler, OrEmpty, PrintCliResult, WithContext, + HandlerFor, HandlerTypes, LeafHandler, OrEmpty, PassthroughCliBindings, PassthroughHandlerFor, + PassthroughHandlerTypes, PrintCliResult, WithContext, }; +pub trait Adapter { + type Inner; + fn as_inner(&self) -> &Self::Inner; +} + pub trait HandlerExt: HandlerFor + Sized { fn no_cli(self) -> NoCli; fn no_display(self) -> NoDisplay; @@ -44,9 +57,24 @@ pub trait HandlerExt: HandlerFor + Sized { fn with_about(self, message: M) -> WithAbout where M: IntoResettable; + #[cfg(feature = "ts")] fn no_ts(self) -> NoTS; - fn unknown_ts(self) -> UnknownTS; - fn custom_ts(self, params_ty: String, return_ty: String) -> CustomTS; + #[cfg(feature = "ts")] + fn override_params_ts(self, params_ty: Params) -> OverrideParamsTS + where + Params: Visit; + #[cfg(feature = "ts")] + fn override_return_ts(self, params_ty: Params) -> OverrideReturnTS + where + Params: Visit; + #[cfg(feature = "ts")] + fn override_params_ts_as(self) -> OverrideParamsTS> + where + Static: Visit; + #[cfg(feature = "ts")] + fn override_return_ts_as(self) -> OverrideReturnTS> + where + Static: Visit; } impl + Sized> HandlerExt for T { @@ -111,97 +139,67 @@ impl + Sized> HandlerExt NoTS { NoTS(self) } - fn unknown_ts(self) -> UnknownTS { - UnknownTS(self) + #[cfg(feature = "ts")] + fn override_params_ts(self, params_ty: Params) -> OverrideParamsTS + where + Params: Visit, + { + OverrideParamsTS { + handler: self, + params_ts: params_ty, + } } - fn custom_ts(self, params_ty: String, return_ty: String) -> CustomTS { - CustomTS { + #[cfg(feature = "ts")] + fn override_return_ts(self, return_ty: Return) -> OverrideReturnTS + where + Return: Visit, + { + OverrideReturnTS { handler: self, - params_ty, return_ty, } } + + #[cfg(feature = "ts")] + fn override_params_ts_as(self) -> OverrideParamsTS> + where + Static: Visit, + { + OverrideParamsTS { + handler: self, + params_ts: Static::::new(), + } + } + + #[cfg(feature = "ts")] + fn override_return_ts_as(self) -> OverrideReturnTS> + where + Static: Visit, + { + OverrideReturnTS { + handler: self, + return_ty: Static::::new(), + } + } } #[derive(Debug, Clone)] pub struct NoCli(pub H); - +impl Adapter for NoCli { + type Inner = H; + fn as_inner(&self) -> &Self::Inner { + &self.0 + } +} impl LeafHandler for NoCli {} - -impl HandlerTypes for NoCli { - type Params = H::Params; - type InheritedParams = H::InheritedParams; - type Ok = H::Ok; - type Err = H::Err; -} -#[cfg(feature = "ts")] -impl crate::handler::HandlerTS for NoCli -where - H: crate::handler::HandlerTS, -{ - fn type_info(&self) -> Option { - self.0.type_info() - } -} -impl HandlerFor for NoCli -where - Context: crate::Context, - H: HandlerFor, -{ - fn handle_sync( - &self, - HandlerArgs { - context, - parent_method, - method, - params, - inherited_params, - raw_params, - }: HandlerArgsFor, - ) -> Result { - self.0.handle_sync(HandlerArgs { - context, - parent_method, - method, - params, - inherited_params, - raw_params, - }) - } - async fn handle_async( - &self, - HandlerArgs { - context, - parent_method, - method, - params, - inherited_params, - raw_params, - }: HandlerArgsFor, - ) -> Result { - self.0 - .handle_async(HandlerArgs { - context, - parent_method, - method, - params, - inherited_params, - raw_params, - }) - .await - } - fn metadata(&self, method: VecDeque<&'static str>) -> OrdMap<&'static str, Value> { - self.0.metadata(method) - } - fn method_from_dots(&self, method: &str) -> Option> { - self.0.method_from_dots(method) - } -} +impl PassthroughHandlerTypes for NoCli {} +impl PassthroughHandlerFor for NoCli {} impl CliBindings for NoCli where Context: crate::Context, @@ -221,82 +219,34 @@ where unimplemented!() } } +#[cfg(feature = "ts")] +impl HandlerTSBindings for NoCli +where + H: HandlerTSBindings, +{ + fn get_ts<'a>(&'a self) -> Option> { + self.0.get_ts() + } +} #[derive(Debug, Clone)] pub struct NoDisplay(pub H); +impl Adapter for NoDisplay { + type Inner = H; + fn as_inner(&self) -> &Self::Inner { + &self.0 + } +} impl LeafHandler for NoDisplay {} - -impl HandlerTypes for NoDisplay { - type Params = H::Params; - type InheritedParams = H::InheritedParams; - type Ok = H::Ok; - type Err = H::Err; -} +impl PassthroughHandlerTypes for NoDisplay {} +impl PassthroughHandlerFor for NoDisplay {} #[cfg(feature = "ts")] -impl crate::handler::HandlerTS for NoDisplay -where - H: crate::handler::HandlerTS, -{ - fn type_info(&self) -> Option { - self.0.type_info() - } -} - -impl HandlerFor for NoDisplay -where - Context: crate::Context, - H: HandlerFor, -{ - fn handle_sync( - &self, - HandlerArgs { - context, - parent_method, - method, - params, - inherited_params, - raw_params, - }: HandlerArgsFor, - ) -> Result { - self.0.handle_sync(HandlerArgs { - context, - parent_method, - method, - params, - inherited_params, - raw_params, - }) - } - async fn handle_async( - &self, - HandlerArgs { - context, - parent_method, - method, - params, - inherited_params, - raw_params, - }: HandlerArgsFor, - ) -> Result { - self.0 - .handle_async(HandlerArgs { - context, - parent_method, - method, - params, - inherited_params, - raw_params, - }) - .await - } - fn metadata(&self, method: VecDeque<&'static str>) -> OrdMap<&'static str, Value> { - self.0.metadata(method) - } - fn method_from_dots(&self, method: &str) -> Option> { - self.0.method_from_dots(method) - } -} +impl PassthroughParamsTS for NoDisplay {} +#[cfg(feature = "ts")] +impl PassthroughReturnTS for NoDisplay {} +#[cfg(feature = "ts")] +impl PassthroughChildrenTS for NoDisplay {} impl PrintCliResult for NoDisplay where Context: crate::Context, @@ -344,83 +294,24 @@ pub struct CustomDisplay { handler: H, } +impl Adapter for CustomDisplay +where + P: Send + Sync + Clone + 'static, +{ + type Inner = H; + fn as_inner(&self) -> &Self::Inner { + &self.handler + } +} impl LeafHandler for CustomDisplay {} - -impl HandlerTypes for CustomDisplay -where - H: HandlerTypes, -{ - type Params = H::Params; - type InheritedParams = H::InheritedParams; - type Ok = H::Ok; - type Err = H::Err; -} +impl PassthroughHandlerTypes for CustomDisplay where P: Send + Sync + Clone + 'static {} +impl PassthroughHandlerFor for CustomDisplay where P: Send + Sync + Clone + 'static {} #[cfg(feature = "ts")] -impl crate::handler::HandlerTS for CustomDisplay -where - H: crate::handler::HandlerTS, - P: Send + Sync + Clone + 'static, -{ - fn type_info(&self) -> Option { - self.handler.type_info() - } -} - -impl HandlerFor for CustomDisplay -where - Context: crate::Context, - H: HandlerFor, - P: Send + Sync + Clone + 'static, -{ - fn handle_sync( - &self, - HandlerArgs { - context, - parent_method, - method, - params, - inherited_params, - raw_params, - }: HandlerArgsFor, - ) -> Result { - self.handler.handle_sync(HandlerArgs { - context, - parent_method, - method, - params, - inherited_params, - raw_params, - }) - } - async fn handle_async( - &self, - HandlerArgs { - context, - parent_method, - method, - params, - inherited_params, - raw_params, - }: HandlerArgsFor, - ) -> Result { - self.handler - .handle_async(HandlerArgs { - context, - parent_method, - method, - params, - inherited_params, - raw_params, - }) - .await - } - fn metadata(&self, method: VecDeque<&'static str>) -> OrdMap<&'static str, Value> { - self.handler.metadata(method) - } - fn method_from_dots(&self, method: &str) -> Option> { - self.handler.method_from_dots(method) - } -} +impl PassthroughParamsTS for CustomDisplay where P: Send + Sync + Clone + 'static {} +#[cfg(feature = "ts")] +impl PassthroughReturnTS for CustomDisplay where P: Send + Sync + Clone + 'static {} +#[cfg(feature = "ts")] +impl PassthroughChildrenTS for CustomDisplay where P: Send + Sync + Clone + 'static {} impl PrintCliResult for CustomDisplay where Context: crate::Context, @@ -498,6 +389,17 @@ pub struct CustomDisplayFn { handler: H, } +impl Adapter for CustomDisplayFn +where + F: Send + Sync + Clone + 'static, + Context: 'static, +{ + type Inner = H; + fn as_inner(&self) -> &Self::Inner { + &self.handler + } +} + impl LeafHandler for CustomDisplayFn {} impl Clone for CustomDisplayFn { @@ -517,82 +419,38 @@ impl Debug for CustomDisplayFn { .finish() } } -impl HandlerTypes for CustomDisplayFn +impl PassthroughHandlerTypes for CustomDisplayFn where - H: HandlerTypes, -{ - type Params = H::Params; - type InheritedParams = H::InheritedParams; - type Ok = H::Ok; - type Err = H::Err; -} -#[cfg(feature = "ts")] -impl crate::handler::HandlerTS for CustomDisplayFn -where - H: crate::handler::HandlerTS, F: Send + Sync + Clone + 'static, Context: 'static, { - fn type_info(&self) -> Option { - self.handler.type_info() - } } - -impl HandlerFor for CustomDisplayFn +impl PassthroughHandlerFor for CustomDisplayFn where - Context: crate::Context, - C: 'static, - H: HandlerFor, F: Send + Sync + Clone + 'static, + Context: 'static, +{ +} +#[cfg(feature = "ts")] +impl PassthroughParamsTS for CustomDisplayFn +where + F: Send + Sync + Clone + 'static, + Context: 'static, +{ +} +#[cfg(feature = "ts")] +impl PassthroughReturnTS for CustomDisplayFn +where + F: Send + Sync + Clone + 'static, + Context: 'static, +{ +} +#[cfg(feature = "ts")] +impl PassthroughChildrenTS for CustomDisplayFn +where + F: Send + Sync + Clone + 'static, + Context: 'static, { - fn handle_sync( - &self, - HandlerArgs { - context, - parent_method, - method, - params, - inherited_params, - raw_params, - }: HandlerArgsFor, - ) -> Result { - self.handler.handle_sync(HandlerArgs { - context, - parent_method, - method, - params, - inherited_params, - raw_params, - }) - } - async fn handle_async( - &self, - HandlerArgs { - context, - parent_method, - method, - params, - inherited_params, - raw_params, - }: HandlerArgsFor, - ) -> Result { - self.handler - .handle_async(HandlerArgs { - context, - parent_method, - method, - params, - inherited_params, - raw_params, - }) - .await - } - fn metadata(&self, method: VecDeque<&'static str>) -> OrdMap<&'static str, Value> { - self.handler.metadata(method) - } - fn method_from_dots(&self, method: &str) -> Option> { - self.handler.method_from_dots(method) - } } impl PrintCliResult for CustomDisplayFn where @@ -685,7 +543,7 @@ impl Handler where Context: crate::Context + CallRemote, RemoteContext: crate::Context, - H: HandlerFor + CliBindings + crate::handler::HandlerTS, + H: HandlerFor + CliBindings + crate::ts::HandlerTSBindings, H::Ok: Serialize + DeserializeOwned, H::Err: From, H::Params: Serialize + DeserializeOwned, @@ -713,6 +571,18 @@ pub struct InheritanceHandler { inherit: F, } +impl Adapter for InheritanceHandler +where + Params: Send + Sync + 'static, + InheritedParams: Send + Sync + 'static, + F: Send + Sync + Clone + 'static, +{ + type Inner = H; + fn as_inner(&self) -> &Self::Inner { + &self.handler + } +} + impl LeafHandler for InheritanceHandler { @@ -752,16 +622,31 @@ where } #[cfg(feature = "ts")] -impl crate::handler::HandlerTS +impl PassthroughParamsTS for InheritanceHandler where Params: Send + Sync + 'static, InheritedParams: Send + Sync + 'static, - H: crate::handler::HandlerTS, + F: Send + Sync + Clone + 'static, +{ +} +#[cfg(feature = "ts")] +impl PassthroughReturnTS + for InheritanceHandler +where + Params: Send + Sync + 'static, + InheritedParams: Send + Sync + 'static, + F: Send + Sync + Clone + 'static, +{ +} +#[cfg(feature = "ts")] +impl PassthroughChildrenTS + for InheritanceHandler +where + Params: Send + Sync + 'static, + InheritedParams: Send + Sync + 'static, + F: Send + Sync + Clone + 'static, { - fn type_info(&self) -> Option { - self.handler.type_info() - } } impl HandlerFor @@ -873,87 +758,30 @@ pub struct WithAbout { message: M, } -impl LeafHandler for WithAbout {} +impl Adapter for WithAbout +where + M: Clone + Send + Sync + 'static, +{ + type Inner = H; + fn as_inner(&self) -> &Self::Inner { + &self.handler + } +} -impl HandlerTypes for WithAbout -where - H: HandlerTypes, -{ - type Params = H::Params; - type InheritedParams = H::InheritedParams; - type Ok = H::Ok; - type Err = H::Err; -} +impl LeafHandler for WithAbout {} +impl PassthroughHandlerTypes for WithAbout where M: Clone + Send + Sync + 'static {} +impl PassthroughHandlerFor for WithAbout where M: Clone + Send + Sync + 'static {} #[cfg(feature = "ts")] -impl crate::handler::HandlerTS for WithAbout -where - H: crate::handler::HandlerTS, - M: Clone + Send + Sync + 'static, -{ - fn type_info(&self) -> Option { - self.handler.type_info() - } -} -impl HandlerFor for WithAbout -where - Context: crate::Context, - H: HandlerFor, - M: Clone + Send + Sync + 'static, -{ - fn handle_sync( - &self, - HandlerArgs { - context, - parent_method, - method, - params, - inherited_params, - raw_params, - }: HandlerArgsFor, - ) -> Result { - self.handler.handle_sync(HandlerArgs { - context, - parent_method, - method, - params, - inherited_params, - raw_params, - }) - } - async fn handle_async( - &self, - HandlerArgs { - context, - parent_method, - method, - params, - inherited_params, - raw_params, - }: HandlerArgsFor, - ) -> Result { - self.handler - .handle_async(HandlerArgs { - context, - parent_method, - method, - params, - inherited_params, - raw_params, - }) - .await - } - fn metadata(&self, method: VecDeque<&'static str>) -> OrdMap<&'static str, Value> { - self.handler.metadata(method) - } - fn method_from_dots(&self, method: &str) -> Option> { - self.handler.method_from_dots(method) - } -} +impl PassthroughParamsTS for WithAbout where M: Clone + Send + Sync + 'static {} +#[cfg(feature = "ts")] +impl PassthroughReturnTS for WithAbout where M: Clone + Send + Sync + 'static {} +#[cfg(feature = "ts")] +impl PassthroughChildrenTS for WithAbout where M: Clone + Send + Sync + 'static {} impl CliBindings for WithAbout where Context: crate::Context, H: CliBindings, - M: IntoResettable + Clone, + M: IntoResettable + Clone + Send + Sync + 'static, { fn cli_command(&self) -> clap::Command { self.handler.cli_command().about(self.message.clone()) @@ -973,309 +801,83 @@ where } } -#[derive(Debug, Clone)] -pub struct NoTS(pub H); - -impl LeafHandler for NoTS {} - -impl HandlerTypes for NoTS -where - H: HandlerTypes, -{ - type Params = H::Params; - type InheritedParams = H::InheritedParams; - type Ok = H::Ok; - type Err = H::Err; -} +#[cfg(feature = "ts")] +pub use ts::*; #[cfg(feature = "ts")] -impl crate::handler::HandlerTS for NoTS { - fn type_info(&self) -> Option { - None - } -} - -impl HandlerFor for NoTS -where - Context: crate::Context, - H: HandlerFor, -{ - fn handle_sync( - &self, - HandlerArgs { - context, - parent_method, - method, - params, - inherited_params, - raw_params, - }: HandlerArgsFor, - ) -> Result { - self.0.handle_sync(HandlerArgs { - context, - parent_method, - method, - params, - inherited_params, - raw_params, - }) - } - async fn handle_async( - &self, - HandlerArgs { - context, - parent_method, - method, - params, - inherited_params, - raw_params, - }: HandlerArgsFor, - ) -> Result { - self.0 - .handle_async(HandlerArgs { - context, - parent_method, - method, - params, - inherited_params, - raw_params, - }) - .await - } - fn metadata(&self, method: VecDeque<&'static str>) -> OrdMap<&'static str, Value> { - self.0.metadata(method) - } - fn method_from_dots(&self, method: &str) -> Option> { - self.0.method_from_dots(method) - } -} - -impl CliBindings for NoTS -where - Context: crate::Context, - H: CliBindings, -{ - fn cli_command(&self) -> clap::Command { - self.0.cli_command() - } - fn cli_parse( - &self, - arg_matches: &clap::ArgMatches, - ) -> Result<(VecDeque<&'static str>, Value), clap::Error> { - self.0.cli_parse(arg_matches) - } - fn cli_display( - &self, - handler: HandlerArgsFor, - result: Self::Ok, - ) -> Result<(), Self::Err> { - self.0.cli_display(handler, result) - } -} - -#[derive(Debug, Clone)] -pub struct UnknownTS(pub H); - -impl LeafHandler for UnknownTS {} - -impl HandlerTypes for UnknownTS -where - H: HandlerTypes, -{ - type Params = H::Params; - type InheritedParams = H::InheritedParams; - type Ok = H::Ok; - type Err = H::Err; -} - -#[cfg(feature = "ts")] -impl crate::handler::HandlerTS for UnknownTS { - fn type_info(&self) -> Option { - Some("{_PARAMS:unknown,_RETURN:unknown}".to_string()) - } -} - -impl HandlerFor for UnknownTS -where - Context: crate::Context, - H: HandlerFor, -{ - fn handle_sync( - &self, - HandlerArgs { - context, - parent_method, - method, - params, - inherited_params, - raw_params, - }: HandlerArgsFor, - ) -> Result { - self.0.handle_sync(HandlerArgs { - context, - parent_method, - method, - params, - inherited_params, - raw_params, - }) - } - async fn handle_async( - &self, - HandlerArgs { - context, - parent_method, - method, - params, - inherited_params, - raw_params, - }: HandlerArgsFor, - ) -> Result { - self.0 - .handle_async(HandlerArgs { - context, - parent_method, - method, - params, - inherited_params, - raw_params, - }) - .await - } - fn metadata(&self, method: VecDeque<&'static str>) -> OrdMap<&'static str, Value> { - self.0.metadata(method) - } - fn method_from_dots(&self, method: &str) -> Option> { - self.0.method_from_dots(method) - } -} - -impl CliBindings for UnknownTS -where - Context: crate::Context, - H: CliBindings, -{ - fn cli_command(&self) -> clap::Command { - self.0.cli_command() - } - fn cli_parse( - &self, - arg_matches: &clap::ArgMatches, - ) -> Result<(VecDeque<&'static str>, Value), clap::Error> { - self.0.cli_parse(arg_matches) - } - fn cli_display( - &self, - handler: HandlerArgsFor, - result: Self::Ok, - ) -> Result<(), Self::Err> { - self.0.cli_display(handler, result) - } -} - -#[derive(Debug, Clone)] -pub struct CustomTS { - pub handler: H, - pub params_ty: String, - pub return_ty: String, -} - -impl LeafHandler for CustomTS {} - -impl HandlerTypes for CustomTS -where - H: HandlerTypes, -{ - type Params = H::Params; - type InheritedParams = H::InheritedParams; - type Ok = H::Ok; - type Err = H::Err; -} - -#[cfg(feature = "ts")] -impl crate::handler::HandlerTS for CustomTS { - fn type_info(&self) -> Option { - Some(format!( - "{{_PARAMS:{},_RETURN:{}}}", - self.params_ty, self.return_ty - )) - } -} - -impl HandlerFor for CustomTS -where - Context: crate::Context, - H: HandlerFor, -{ - fn handle_sync( - &self, - HandlerArgs { - context, - parent_method, - method, - params, - inherited_params, - raw_params, - }: HandlerArgsFor, - ) -> Result { - self.handler.handle_sync(HandlerArgs { - context, - parent_method, - method, - params, - inherited_params, - raw_params, - }) - } - async fn handle_async( - &self, - HandlerArgs { - context, - parent_method, - method, - params, - inherited_params, - raw_params, - }: HandlerArgsFor, - ) -> Result { - self.handler - .handle_async(HandlerArgs { - context, - parent_method, - method, - params, - inherited_params, - raw_params, - }) - .await - } - fn metadata(&self, method: VecDeque<&'static str>) -> OrdMap<&'static str, Value> { - self.handler.metadata(method) - } - fn method_from_dots(&self, method: &str) -> Option> { - self.handler.method_from_dots(method) - } -} - -impl CliBindings for CustomTS -where - Context: crate::Context, - H: CliBindings, -{ - fn cli_command(&self) -> clap::Command { - self.handler.cli_command() - } - fn cli_parse( - &self, - arg_matches: &clap::ArgMatches, - ) -> Result<(VecDeque<&'static str>, Value), clap::Error> { - self.handler.cli_parse(arg_matches) - } - fn cli_display( - &self, - handler: HandlerArgsFor, - result: Self::Ok, - ) -> Result<(), Self::Err> { - self.handler.cli_display(handler, result) +mod ts { + use super::*; + use crate::ts::{HandlerTSBindings, ParamsTS, ReturnTS}; + + #[derive(Debug, Clone)] + pub struct NoTS(pub H); + impl Adapter for NoTS { + type Inner = H; + fn as_inner(&self) -> &Self::Inner { + &self.0 + } } + impl LeafHandler for NoTS {} + impl PassthroughHandlerTypes for NoTS {} + impl PassthroughHandlerFor for NoTS {} + impl PassthroughCliBindings for NoTS {} + impl HandlerTSBindings for NoTS { + fn get_ts<'a>(&'a self) -> Option> { + None + } + } + + #[derive(Clone, Debug)] + pub struct OverrideParamsTS { + pub(super) handler: H, + pub(super) params_ts: P, + } + impl Adapter for OverrideParamsTS { + type Inner = H; + fn as_inner(&self) -> &Self::Inner { + &self.handler + } + } + impl LeafHandler for OverrideParamsTS {} + impl PassthroughHandlerTypes for OverrideParamsTS {} + impl PassthroughHandlerFor for OverrideParamsTS {} + impl PassthroughCliBindings for OverrideParamsTS {} + impl ParamsTS for OverrideParamsTS + where + H: Send + Sync, + P: Visit + Send + Sync, + { + fn params_ts<'a>(&'a self) -> Box { + Box::new(move |visitor| self.params_ts.visit(visitor)) + } + } + impl PassthroughReturnTS for OverrideParamsTS where P: Clone + Send + Sync + 'static {} + impl PassthroughChildrenTS for OverrideParamsTS where P: Clone + Send + Sync + 'static {} + + #[derive(Clone, Debug)] + pub struct OverrideReturnTS { + pub(super) handler: H, + pub(super) return_ty: R, + } + impl Adapter for OverrideReturnTS { + type Inner = H; + fn as_inner(&self) -> &Self::Inner { + &self.handler + } + } + impl LeafHandler for OverrideReturnTS {} + impl PassthroughHandlerTypes for OverrideReturnTS {} + impl PassthroughHandlerFor for OverrideReturnTS {} + impl PassthroughCliBindings for OverrideReturnTS {} + impl PassthroughParamsTS for OverrideReturnTS {} + impl ReturnTS for OverrideReturnTS + where + H: Send + Sync, + R: Visit + Send + Sync, + { + fn return_ts<'a>(&'a self) -> Option> { + Some(Box::new(move |visitor| self.return_ty.visit(visitor))) + } + } + impl PassthroughChildrenTS for OverrideReturnTS {} } diff --git a/src/handler/from_fn.rs b/src/handler/from_fn.rs index af711cf..b0c320a 100644 --- a/src/handler/from_fn.rs +++ b/src/handler/from_fn.rs @@ -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 std::fmt::Debug for FromFn { } } -#[cfg(feature = "ts")] -impl crate::handler::HandlerTS for FromFn -where - Self: HandlerTypes, - visit_rs::Static<::Params>: visit_rs::Visit, - visit_rs::Static<::Ok>: visit_rs::Visit, -{ - fn type_info(&self) -> Option { - let mut visitor = TSVisitor::default(); - Some(format!( - "{{_PARAMS:{},_RETURN:{}}}", - ::Params::inline_flattened(), - ::Ok::inline_flattened(), - )) - } -} impl PrintCliResult for FromFn where Context: crate::Context, @@ -174,21 +156,6 @@ impl std::fmt::Debug for FromFnAsync { } } -#[cfg(feature = "ts")] -impl crate::handler::HandlerTS for FromFnAsync -where - Self: HandlerTypes, - ::Params: ts_rs::TS, - ::Ok: ts_rs::TS, -{ - fn type_info(&self) -> Option { - Some(format!( - "{{_PARAMS:{},_RETURN:{}}}", - ::Params::inline_flattened(), - ::Ok::inline_flattened(), - )) - } -} impl PrintCliResult for FromFnAsync where Context: crate::Context, @@ -286,21 +253,6 @@ impl std::fmt::Debug for FromFnAsyncLocal crate::handler::HandlerTS for FromFnAsyncLocal -where - Self: HandlerTypes, - ::Params: ts_rs::TS, - ::Ok: ts_rs::TS, -{ - fn type_info(&self) -> Option { - Some(format!( - "{{_PARAMS:{},_RETURN:{}}}", - ::Params::inline_flattened(), - ::Ok::inline_flattened(), - )) - } -} impl PrintCliResult for FromFnAsyncLocal where Context: crate::Context, diff --git a/src/handler/mod.rs b/src/handler/mod.rs index f904c77..a5c1321 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -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 HandleAnyArgs Option { - None - } -} - -impl HandleAnyTS for Arc { - fn type_info(&self) -> Option { - self.deref().type_info() - } -} - -pub(crate) trait HandleAnyRequires: HandleAnyTS + Send + Sync {} -impl HandleAnyRequires for T {} +pub(crate) trait HandleAnyRequires: HandlerTSBindings + Send + Sync {} +impl HandleAnyRequires for T {} #[async_trait::async_trait] pub(crate) trait HandleAny: HandleAnyRequires { @@ -149,7 +138,9 @@ pub trait PrintCliResult: HandlerTypes { } #[allow(private_interfaces)] -pub struct DynHandler(Arc>); +pub struct DynHandler( + pub(crate) Arc>, +); impl DynHandler { pub fn new(handler: H) -> Option where @@ -169,11 +160,6 @@ impl Debug for DynHandler { f.debug_struct("DynHandler").finish() } } -impl HandleAnyTS for DynHandler { - fn type_info(&self) -> Option { - self.0.type_info() - } -} #[async_trait::async_trait] impl HandleAny for DynHandler @@ -201,6 +187,12 @@ impl HandleAny self.0.cli() } } +#[cfg(feature = "ts")] +impl HandlerTSBindings for DynHandler { + fn get_ts<'a>(&'a self) -> Option> { + self.0.get_ts() + } +} #[allow(type_alias_bounds)] pub type HandlerArgsFor = @@ -227,19 +219,20 @@ pub trait HandlerTypes { type Err: Send + Sync; } +pub trait PassthroughHandlerTypes: Adapter {} +impl HandlerTypes for T +where + T: PassthroughHandlerTypes, + T::Inner: HandlerTypes, +{ + type Params = ::Params; + type InheritedParams = ::InheritedParams; + type Ok = ::Ok; + type Err = ::Err; +} + pub trait LeafHandler {} -pub trait HandlerTS { - fn type_info(&self) -> Option; -} - -#[cfg(not(feature = "ts"))] -impl HandlerTS for T { - fn type_info(&self) -> Option { - None - } -} - pub trait HandlerRequires: HandlerTypes + Clone + Send + Sync + 'static {} impl HandlerRequires for T {} @@ -293,6 +286,70 @@ pub trait HandlerFor: HandlerRequires { } } } +pub trait PassthroughHandlerFor: PassthroughHandlerTypes {} +impl HandlerFor for T +where + T: PassthroughHandlerFor + Clone + Send + Sync + 'static, + T::Inner: HandlerFor, + Context: crate::Context, +{ + fn handle_async( + &self, + handle_args: HandlerArgsFor, + ) -> impl Future> + Send { + self.as_inner().handle_async(handle_args) + } + fn handle_async_with_sync<'a>( + &'a self, + handle_args: HandlerArgsFor, + ) -> impl Future> + Send + 'a { + self.as_inner().handle_async_with_sync(handle_args) + } + fn handle_async_with_sync_blocking<'a>( + &'a self, + handle_args: HandlerArgsFor, + ) -> impl Future> + Send + 'a { + self.as_inner().handle_async_with_sync_blocking(handle_args) + } + fn handle_sync( + &self, + handle_args: HandlerArgsFor, + ) -> Result { + 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> { + self.as_inner().method_from_dots(method) + } +} + +pub trait PassthroughCliBindings: PassthroughHandlerTypes {} +impl CliBindings for T +where + T: PassthroughCliBindings, + T::Inner: CliBindings, + Context: crate::Context, +{ + const NO_CLI: bool = >::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, + result: Self::Ok, + ) -> Result<(), Self::Err> { + self.as_inner().cli_display(handle_args, result) + } +} pub trait Handler { type H: HandlerTypes; @@ -315,7 +372,7 @@ impl WithContext { impl Handler for WithContext where Context: crate::Context, - H: HandlerFor + CliBindings + HandlerTS, + H: HandlerFor + CliBindings + HandlerTSBindings, H::Ok: Serialize + DeserializeOwned, H::Params: DeserializeOwned, H::InheritedParams: OrEmpty, @@ -356,12 +413,13 @@ impl std::fmt::Debug for AnyHandler HandleAnyTS for AnyHandler +#[cfg(feature = "ts")] +impl HandlerTSBindings for AnyHandler where - H: HandlerTS, + H: HandlerTSBindings, { - fn type_info(&self) -> Option { - self.handler.type_info() + fn get_ts<'a>(&'a self) -> Option> { + self.handler.get_ts() } } @@ -369,7 +427,7 @@ where impl HandleAny for AnyHandler where Context: crate::Context, - H: HandlerFor + CliBindings + HandlerTS, + H: HandlerFor + CliBindings + HandlerTSBindings, H::Params: DeserializeOwned, H::Ok: Serialize + DeserializeOwned, H::InheritedParams: OrEmpty, @@ -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 { fn from_t(t: T) -> Self; diff --git a/src/handler/parent.rs b/src/handler/parent.rs index 3fd260a..567b78e 100644 --- a/src/handler/parent.rs +++ b/src/handler/parent.rs @@ -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 ParentHandler Option { - 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 Clone for ParentHandler { fn clone(&self) -> Self { @@ -169,40 +140,6 @@ where type Err = RpcError; } -#[cfg(feature = "ts")] -impl crate::handler::HandlerTS - for ParentHandler -where - Params: ts_rs::TS + Send + Sync + 'static, - InheritedParams: Send + Sync + 'static, -{ - fn type_info(&self) -> Option { - self.type_info_impl(&Params::inline_flattened()) - } -} -#[cfg(feature = "ts")] -impl crate::handler::HandlerTS - for CustomTS> -where - Params: Send + Sync + 'static, - InheritedParams: Send + Sync + 'static, -{ - fn type_info(&self) -> Option { - self.handler.type_info_impl(&self.params_ty) - } -} -#[cfg(feature = "ts")] -impl crate::handler::HandlerTS - for UnknownTS> -where - Params: Send + Sync + 'static, - InheritedParams: Send + Sync + 'static, -{ - fn type_info(&self) -> Option { - self.0.type_info_impl("unknown") - } -} - impl HandlerFor for ParentHandler where diff --git a/src/lib.rs b/src/lib.rs index eb3b3d4..7c9e353 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,4 +14,8 @@ mod server; pub mod ts; pub mod util; -#[cfg(feature = "ts")] \ No newline at end of file +#[cfg(not(feature = "ts"))] +pub mod ts { + pub trait HandlerTSBindings {} + impl HandlerTSBindings for T {} +} diff --git a/src/ts.rs b/src/ts.rs index 94a8359..b2917b1 100644 --- a/src/ts.rs +++ b/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 Visit for Static +where + T: TS, +{ + fn visit(&self, visitor: &mut TSVisitor) -> ::Result { + T::visit_ts(visitor); + } +} + +pub trait ParamsTS { + fn params_ts<'a>(&'a self) -> Box; +} +pub trait ReturnTS { + fn return_ts<'a>(&'a self) -> Option>; +} +pub trait ChildrenTS { + fn children_ts<'a>(&'a self) -> Option>; +} + +pub trait HandlerTSBindings { + fn get_ts<'a>(&'a self) -> Option>; +} +impl HandlerTSBindings for T { + fn get_ts<'a>(&'a self) -> Option> { + Some(HandlerTS::new(self)) + } +} +impl HandlerTSBindings for Arc { + fn get_ts<'a>(&'a self) -> Option> { + self.deref().get_ts() + } +} + +pub struct HandlerTS<'a> { + params_ts: Box, + return_ts: Option>, + children: Option>, +} +impl<'a> HandlerTS<'a> { + pub fn new(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 for HandlerTS<'a> { + fn visit(&self, visitor: &mut TSVisitor) -> ::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 ParamsTS for FromFn +where + Self: HandlerTypes, + Static<::Params>: Visit, +{ + fn params_ts(&self) -> Box { + Box::new(|visitor| Static::<::Params>::new().visit(visitor)) + } +} +impl ParamsTS for FromFnAsync +where + Self: HandlerTypes, + Static<::Params>: Visit, +{ + fn params_ts(&self) -> Box { + Box::new(|visitor| Static::<::Params>::new().visit(visitor)) + } +} +impl ParamsTS for FromFnAsyncLocal +where + Self: HandlerTypes, + Static<::Params>: Visit, +{ + fn params_ts(&self) -> Box { + Box::new(|visitor| Static::<::Params>::new().visit(visitor)) + } +} +impl ParamsTS for ParentHandler +where + Self: HandlerTypes, + Static<::Params>: Visit, +{ + fn params_ts(&self) -> Box { + Box::new(|visitor| Static::<::Params>::new().visit(visitor)) + } +} + +impl ReturnTS for FromFn +where + Self: HandlerTypes, + Static<::Ok>: Visit, +{ + fn return_ts(&self) -> Option> { + Some(Box::new(|visitor| { + Static::<::Ok>::new().visit(visitor) + })) + } +} +impl ReturnTS for FromFnAsync +where + Self: HandlerTypes, + Static<::Ok>: Visit, +{ + fn return_ts(&self) -> Option> { + Some(Box::new(|visitor| { + Static::<::Ok>::new().visit(visitor) + })) + } +} +impl ReturnTS for FromFnAsyncLocal +where + Self: HandlerTypes, + Static<::Ok>: Visit, +{ + fn return_ts(&self) -> Option> { + Some(Box::new(|visitor| { + Static::<::Ok>::new().visit(visitor) + })) + } +} +impl ReturnTS + for ParentHandler +{ + fn return_ts(&self) -> Option> { + None + } +} + +impl ChildrenTS for FromFn { + fn children_ts(&self) -> Option> { + None + } +} +impl ChildrenTS for FromFnAsync { + fn children_ts(&self) -> Option> { + None + } +} +impl ChildrenTS for FromFnAsyncLocal { + fn children_ts(&self) -> Option> { + None + } +} +impl ChildrenTS + for ParentHandler +where + Context: crate::Context, + Params: Send + Sync + 'static, + InheritedParams: Send + Sync + 'static, +{ + fn children_ts<'a>(&'a self) -> Option> { + 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 ParamsTS for T +where + T::Inner: ParamsTS, +{ + fn params_ts<'a>(&'a self) -> Box { + self.as_inner().params_ts() + } +} + +pub trait PassthroughReturnTS: Adapter {} +impl ReturnTS for T +where + T::Inner: ReturnTS, +{ + fn return_ts<'a>(&'a self) -> Option> { + self.as_inner().return_ts() + } +} + +pub trait PassthroughChildrenTS: Adapter {} +impl ChildrenTS for T +where + T::Inner: ChildrenTS, +{ + fn children_ts<'a>(&'a self) -> Option> { + 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>(&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(&mut self) + where + T: TS, + { + self.visit_ty(&Static::::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 Visit for Static -where - T: VisitFieldsStaticNamed, -{ - fn visit(&self, visitor: &mut TSVisitor) -> ::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 Visit for NamedStatic +impl<'a, T> Visit for Named<'a, Static> where + T: TS, Static: Visit, { fn visit(&self, visitor: &mut TSVisitor) -> ::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::::new().visit(visitor); + visitor.append_type::(); 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 for LiteralTS { + fn visit(&self, visitor: &mut TSVisitor) -> ::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 for Static<$ty> { - fn visit(&self, visitor: &mut TSVisitor) -> ::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(visitor: &mut TSVisitor) +where + T: VisitFieldsStaticNamed, +{ + 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(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::(); + visitor.ts.push(']'); + if K::IS_ENUMERABLE { + visitor.ts.push('?'); + } + visitor.ts.push(':'); + visitor.append_type::(); + visitor.ts.push('}'); +} + +#[macro_export] +macro_rules! impl_ts_map { + ($( + $ty:ty + $(where [$($bounds:tt)*])? + ),+ $(,)?) => { + $( + impl $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::(visitor) + } + } + )+ + }; +} + +impl_ts_map!( + std::collections::HashMap, + imbl_value::imbl::HashMap, + imbl_value::InOMap where [K: Eq + Clone, V: Clone], + BTreeMap, + OrdMap, +); + +pub fn visit_array(visitor: &mut TSVisitor) +where + T: TS, +{ + visitor.ts.push('('); + visitor.append_type::(); + visitor.ts.push_str(")[]"); +} + +#[macro_export] +macro_rules! impl_ts_array { + ($( + $ty:ty + $(where [$($bounds:tt)*])? + ),+ $(,)?) => { + $( + impl $crate::ts::TS for $ty + where + T: $crate::ts::TS, + $($($bounds)*,)? + { + fn visit_ts(visitor: &mut $crate::ts::TSVisitor) { + $crate::ts::visit_array::(visitor) + } + } + )+ + }; +} + +impl_ts_array!(Vec, Vector); + +impl TS for Box { + const DEFINE: Option<&str> = T::DEFINE; + const IS_ENUMERABLE: bool = T::IS_ENUMERABLE; + fn visit_ts(visitor: &mut TSVisitor) { + T::visit_ts(visitor); + } +} +impl TS for Arc { + const DEFINE: Option<&str> = T::DEFINE; + const IS_ENUMERABLE: bool = T::IS_ENUMERABLE; + fn visit_ts(visitor: &mut TSVisitor) { + T::visit_ts(visitor); + } +} +impl TS for Rc { + const DEFINE: Option<&str> = T::DEFINE; + const IS_ENUMERABLE: bool = T::IS_ENUMERABLE; + fn visit_ts(visitor: &mut TSVisitor) { + T::visit_ts(visitor); + } +} diff --git a/tests/test.rs b/tests/test.rs index 3c8e8bd..80557de 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -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