wip: replace ts lib

This commit is contained in:
Aiden McClelland
2025-11-12 10:57:37 -07:00
parent 2dd2832e04
commit c2bb290618
10 changed files with 143 additions and 82 deletions

65
Cargo.lock generated
View File

@@ -1198,8 +1198,8 @@ dependencies = [
"thiserror 2.0.17", "thiserror 2.0.17",
"tokio", "tokio",
"tokio-stream", "tokio-stream",
"ts-rs",
"url", "url",
"visit-rs",
"yajrc", "yajrc",
] ]
@@ -1495,15 +1495,6 @@ dependencies = [
"windows-sys 0.61.2", "windows-sys 0.61.2",
] ]
[[package]]
name = "termcolor"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755"
dependencies = [
"winapi-util",
]
[[package]] [[package]]
name = "thiserror" name = "thiserror"
version = "1.0.69" version = "1.0.69"
@@ -1704,28 +1695,6 @@ version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
[[package]]
name = "ts-rs"
version = "9.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b44017f9f875786e543595076374b9ef7d13465a518dd93d6ccdbf5b432dde8c"
dependencies = [
"thiserror 1.0.69",
"ts-rs-macros",
]
[[package]]
name = "ts-rs-macros"
version = "9.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c88cc88fd23b5a04528f3a8436024f20010a16ec18eb23c164b1242f65860130"
dependencies = [
"proc-macro2",
"quote",
"syn",
"termcolor",
]
[[package]] [[package]]
name = "unicode-ident" name = "unicode-ident"
version = "1.0.22" version = "1.0.22"
@@ -1774,6 +1743,29 @@ version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]]
name = "visit-rs"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ea0fdb05dd98029f7e226ccd11bd7184ad68566365547930bdde9ae79b5ce3d"
dependencies = [
"async-stream",
"futures",
"serde",
"visit-rs-derive",
]
[[package]]
name = "visit-rs-derive"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8dde3216fefaf10421d9509f88bc5a480b5fb3fe48779ca4f148e75896d3f5ad"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "want" name = "want"
version = "0.3.1" version = "0.3.1"
@@ -1866,15 +1858,6 @@ dependencies = [
"wasm-bindgen", "wasm-bindgen",
] ]
[[package]]
name = "winapi-util"
version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
dependencies = [
"windows-sys 0.61.2",
]
[[package]] [[package]]
name = "windows-link" name = "windows-link"
version = "0.1.3" version = "0.1.3"

View File

@@ -1,31 +1,32 @@
[package] [package]
authors = ["Aiden McClelland <me@drbonez.dev>"] authors = ["Aiden McClelland <me@drbonez.dev>"]
edition = "2018"
name = "rpc-toolkit"
version = "0.3.2"
description = "A toolkit for creating JSON-RPC 2.0 servers with automatic cli bindings" description = "A toolkit for creating JSON-RPC 2.0 servers with automatic cli bindings"
license = "MIT"
documentation = "https://docs.rs/rpc-toolkit" documentation = "https://docs.rs/rpc-toolkit"
keywords = ["json", "rpc", "cli"] edition = "2018"
keywords = ["cli", "json", "rpc"]
license = "MIT"
name = "rpc-toolkit"
repository = "https://github.com/Start9Labs/rpc-toolkit" repository = "https://github.com/Start9Labs/rpc-toolkit"
version = "0.3.2"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[features] [features]
cbor = ["serde_cbor"] cbor = ["serde_cbor"]
default = ["cbor", "ts-rs"] default = ["cbor", "ts"]
ts = ["visit-rs"]
[dependencies] [dependencies]
axum = "0.8"
async-stream = "0.3" async-stream = "0.3"
async-trait = "0.1" async-trait = "0.1"
axum = "0.8"
clap = { version = "4", features = ["derive"] } clap = { version = "4", features = ["derive"] }
futures = "0.3" futures = "0.3"
http = "1" http = "1"
http-body-util = "0.1" http-body-util = "0.1"
# hyper = { version = "1", features = ["server", "http1", "http2", "client"] } # hyper = { version = "1", features = ["server", "http1", "http2", "client"] }
itertools = "0.14"
imbl-value = "0.4.3" imbl-value = "0.4.3"
itertools = "0.14"
lazy_format = "2" lazy_format = "2"
lazy_static = "1.4" lazy_static = "1.4"
openssl = { version = "0.10", features = ["vendored"] } openssl = { version = "0.10", features = ["vendored"] }
@@ -37,6 +38,6 @@ serde_json = "1.0"
thiserror = "2.0" 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"] }
ts-rs = { version = "9.0.1", optional = true }
url = "2" url = "2"
visit-rs = { version = "0.1.5", optional = true }
yajrc = "0.1" yajrc = "0.1"

View File

@@ -209,7 +209,7 @@ where
type Err = RemoteHandler::Err; type Err = RemoteHandler::Err;
} }
#[cfg(feature = "ts-rs")] #[cfg(feature = "ts")]
impl<Context, RemoteContext, RemoteHandler, Extra> crate::handler::HandlerTS impl<Context, RemoteContext, RemoteHandler, Extra> crate::handler::HandlerTS
for CallRemoteHandler<Context, RemoteContext, RemoteHandler, Extra> for CallRemoteHandler<Context, RemoteContext, RemoteHandler, Extra>
where where

View File

@@ -139,7 +139,7 @@ impl<H: HandlerTypes> HandlerTypes for NoCli<H> {
type Ok = H::Ok; type Ok = H::Ok;
type Err = H::Err; type Err = H::Err;
} }
#[cfg(feature = "ts-rs")] #[cfg(feature = "ts")]
impl<H> crate::handler::HandlerTS for NoCli<H> impl<H> crate::handler::HandlerTS for NoCli<H>
where where
H: crate::handler::HandlerTS, H: crate::handler::HandlerTS,
@@ -233,7 +233,7 @@ impl<H: HandlerTypes> HandlerTypes for NoDisplay<H> {
type Ok = H::Ok; type Ok = H::Ok;
type Err = H::Err; type Err = H::Err;
} }
#[cfg(feature = "ts-rs")] #[cfg(feature = "ts")]
impl<H> crate::handler::HandlerTS for NoDisplay<H> impl<H> crate::handler::HandlerTS for NoDisplay<H>
where where
H: crate::handler::HandlerTS, H: crate::handler::HandlerTS,
@@ -355,7 +355,7 @@ where
type Ok = H::Ok; type Ok = H::Ok;
type Err = H::Err; type Err = H::Err;
} }
#[cfg(feature = "ts-rs")] #[cfg(feature = "ts")]
impl<P, H> crate::handler::HandlerTS for CustomDisplay<P, H> impl<P, H> crate::handler::HandlerTS for CustomDisplay<P, H>
where where
H: crate::handler::HandlerTS, H: crate::handler::HandlerTS,
@@ -526,7 +526,7 @@ where
type Ok = H::Ok; type Ok = H::Ok;
type Err = H::Err; type Err = H::Err;
} }
#[cfg(feature = "ts-rs")] #[cfg(feature = "ts")]
impl<F, H, Context> crate::handler::HandlerTS for CustomDisplayFn<F, H, Context> impl<F, H, Context> crate::handler::HandlerTS for CustomDisplayFn<F, H, Context>
where where
H: crate::handler::HandlerTS, H: crate::handler::HandlerTS,
@@ -751,7 +751,7 @@ where
type Err = H::Err; type Err = H::Err;
} }
#[cfg(feature = "ts-rs")] #[cfg(feature = "ts")]
impl<Params, InheritedParams, H, F> crate::handler::HandlerTS impl<Params, InheritedParams, H, F> crate::handler::HandlerTS
for InheritanceHandler<Params, InheritedParams, H, F> for InheritanceHandler<Params, InheritedParams, H, F>
where where
@@ -884,7 +884,7 @@ where
type Ok = H::Ok; type Ok = H::Ok;
type Err = H::Err; type Err = H::Err;
} }
#[cfg(feature = "ts-rs")] #[cfg(feature = "ts")]
impl<M, H> crate::handler::HandlerTS for WithAbout<M, H> impl<M, H> crate::handler::HandlerTS for WithAbout<M, H>
where where
H: crate::handler::HandlerTS, H: crate::handler::HandlerTS,
@@ -988,7 +988,7 @@ where
type Err = H::Err; type Err = H::Err;
} }
#[cfg(feature = "ts-rs")] #[cfg(feature = "ts")]
impl<H> crate::handler::HandlerTS for NoTS<H> { impl<H> crate::handler::HandlerTS for NoTS<H> {
fn type_info(&self) -> Option<String> { fn type_info(&self) -> Option<String> {
None None
@@ -1088,7 +1088,7 @@ where
type Err = H::Err; type Err = H::Err;
} }
#[cfg(feature = "ts-rs")] #[cfg(feature = "ts")]
impl<H: LeafHandler> crate::handler::HandlerTS for UnknownTS<H> { impl<H: LeafHandler> crate::handler::HandlerTS for UnknownTS<H> {
fn type_info(&self) -> Option<String> { fn type_info(&self) -> Option<String> {
Some("{_PARAMS:unknown,_RETURN:unknown}".to_string()) Some("{_PARAMS:unknown,_RETURN:unknown}".to_string())
@@ -1192,7 +1192,7 @@ where
type Err = H::Err; type Err = H::Err;
} }
#[cfg(feature = "ts-rs")] #[cfg(feature = "ts")]
impl<H: LeafHandler> crate::handler::HandlerTS for CustomTS<H> { impl<H: LeafHandler> crate::handler::HandlerTS for CustomTS<H> {
fn type_info(&self) -> Option<String> { fn type_info(&self) -> Option<String> {
Some(format!( Some(format!(

View File

@@ -7,9 +7,9 @@ use imbl_value::imbl::OrdMap;
use imbl_value::Value; use imbl_value::Value;
use serde::de::DeserializeOwned; use serde::de::DeserializeOwned;
use serde::Serialize; use serde::Serialize;
#[cfg(feature = "ts-rs")]
use ts_rs::TS;
#[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,14 +48,15 @@ impl<F, T, E, Args> std::fmt::Debug for FromFn<F, T, E, Args> {
} }
} }
#[cfg(feature = "ts-rs")] #[cfg(feature = "ts")]
impl<F, T, E, Args> crate::handler::HandlerTS for FromFn<F, T, E, Args> impl<F, T, E, Args> crate::handler::HandlerTS for FromFn<F, T, E, Args>
where where
Self: HandlerTypes, Self: HandlerTypes,
<Self as HandlerTypes>::Params: ts_rs::TS, visit_rs::Static<<Self as HandlerTypes>::Params>: visit_rs::Visit<TSVisitor>,
<Self as HandlerTypes>::Ok: ts_rs::TS, visit_rs::Static<<Self as HandlerTypes>::Ok>: visit_rs::Visit<TSVisitor>,
{ {
fn type_info(&self) -> Option<String> { fn type_info(&self) -> Option<String> {
let mut visitor = TSVisitor::default();
Some(format!( Some(format!(
"{{_PARAMS:{},_RETURN:{}}}", "{{_PARAMS:{},_RETURN:{}}}",
<Self as HandlerTypes>::Params::inline_flattened(), <Self as HandlerTypes>::Params::inline_flattened(),
@@ -173,7 +174,7 @@ impl<F, Fut, T, E, Args> std::fmt::Debug for FromFnAsync<F, Fut, T, E, Args> {
} }
} }
#[cfg(feature = "ts-rs")] #[cfg(feature = "ts")]
impl<F, Fut, T, E, Args> crate::handler::HandlerTS for FromFnAsync<F, Fut, T, E, Args> impl<F, Fut, T, E, Args> crate::handler::HandlerTS for FromFnAsync<F, Fut, T, E, Args>
where where
Self: HandlerTypes, Self: HandlerTypes,
@@ -285,7 +286,7 @@ impl<F, Fut, T, E, Args> std::fmt::Debug for FromFnAsyncLocal<F, Fut, T, E, Args
} }
} }
#[cfg(feature = "ts-rs")] #[cfg(feature = "ts")]
impl<F, Fut, T, E, Args> crate::handler::HandlerTS for FromFnAsyncLocal<F, Fut, T, E, Args> impl<F, Fut, T, E, Args> crate::handler::HandlerTS for FromFnAsyncLocal<F, Fut, T, E, Args>
where where
Self: HandlerTypes, Self: HandlerTypes,

View File

@@ -233,7 +233,7 @@ pub trait HandlerTS {
fn type_info(&self) -> Option<String>; fn type_info(&self) -> Option<String>;
} }
#[cfg(not(feature = "ts-rs"))] #[cfg(not(feature = "ts"))]
impl<T: HandlerTypes> HandlerTS for T { impl<T: HandlerTypes> HandlerTS for T {
fn type_info(&self) -> Option<String> { fn type_info(&self) -> Option<String> {
None None
@@ -450,8 +450,8 @@ where
} }
#[derive(Debug, Clone, Copy, Deserialize, Serialize, Parser)] #[derive(Debug, Clone, Copy, Deserialize, Serialize, Parser)]
#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))] #[cfg_attr(feature = "ts", derive(ts_rs::TS))]
#[cfg_attr(feature = "ts-rs", ts(type = "{}"))] #[cfg_attr(feature = "ts", ts(type = "{}"))]
pub struct Empty {} pub struct Empty {}
pub trait OrEmpty<T> { pub trait OrEmpty<T> {

View File

@@ -7,14 +7,14 @@ use imbl_value::Value;
use serde::Serialize; use serde::Serialize;
use yajrc::RpcError; use yajrc::RpcError;
#[cfg(feature = "ts-rs")] #[cfg(feature = "ts")]
use crate::handler::HandleAnyTS; 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-rs")] #[cfg(feature = "ts")]
use crate::{CustomTS, UnknownTS}; use crate::{CustomTS, UnknownTS};
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
@@ -88,7 +88,7 @@ impl<Context, Params, InheritedParams> ParentHandler<Context, Params, InheritedP
self.metadata.insert(key, value); self.metadata.insert(key, value);
self self
} }
#[cfg(feature = "ts-rs")] #[cfg(feature = "ts")]
fn type_info_impl(&self, params_ty: &str) -> Option<String> { fn type_info_impl(&self, params_ty: &str) -> Option<String> {
use std::fmt::Write; use std::fmt::Write;
let mut res = "{".to_owned(); let mut res = "{".to_owned();
@@ -169,7 +169,7 @@ where
type Err = RpcError; type Err = RpcError;
} }
#[cfg(feature = "ts-rs")] #[cfg(feature = "ts")]
impl<Context, Params, InheritedParams> crate::handler::HandlerTS impl<Context, Params, InheritedParams> crate::handler::HandlerTS
for ParentHandler<Context, Params, InheritedParams> for ParentHandler<Context, Params, InheritedParams>
where where
@@ -180,7 +180,7 @@ where
self.type_info_impl(&Params::inline_flattened()) self.type_info_impl(&Params::inline_flattened())
} }
} }
#[cfg(feature = "ts-rs")] #[cfg(feature = "ts")]
impl<Context, Params, InheritedParams> crate::handler::HandlerTS impl<Context, Params, InheritedParams> crate::handler::HandlerTS
for CustomTS<ParentHandler<Context, Params, InheritedParams>> for CustomTS<ParentHandler<Context, Params, InheritedParams>>
where where
@@ -191,7 +191,7 @@ where
self.handler.type_info_impl(&self.params_ty) self.handler.type_info_impl(&self.params_ty)
} }
} }
#[cfg(feature = "ts-rs")] #[cfg(feature = "ts")]
impl<Context, Params, InheritedParams> crate::handler::HandlerTS impl<Context, Params, InheritedParams> crate::handler::HandlerTS
for UnknownTS<ParentHandler<Context, Params, InheritedParams>> for UnknownTS<ParentHandler<Context, Params, InheritedParams>>
where where

View File

@@ -10,9 +10,8 @@ pub mod command_helpers;
mod context; mod context;
mod handler; mod handler;
mod server; mod server;
#[cfg(feature = "ts")]
pub mod ts;
pub mod util; pub mod util;
#[cfg(feature = "ts-rs")] #[cfg(feature = "ts")]
pub fn type_helpers() -> &'static str {
include_str!("./type-helpers.ts")
}

77
src/ts.rs Normal file
View File

@@ -0,0 +1,77 @@
use visit_rs::{NamedStatic, Static, Visit, VisitFieldsStaticNamed, Visitor};
pub fn type_helpers() -> &'static str {
include_str!("./type-helpers.ts")
}
#[derive(Default)]
pub struct TSVisitor {
pub ts: String,
}
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>
where
Static<T>: Visit<TSVisitor>,
{
fn visit(&self, visitor: &mut TSVisitor) -> <TSVisitor as Visitor>::Result {
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();
}
visitor.ts.push_str(":");
}
Static::<T>::new().visit(visitor);
if self.name.is_some() {
visitor.ts.push(";");
} else {
visitor.ts.push(",");
}
}
}
macro_rules! impl_ts {
($($ty:ty),+ => $ts:expr) => {
$(
impl Visit<TSVisitor> for Static<$ty> {
fn visit(&self, visitor: &mut TSVisitor) -> <TSVisitor as Visitor>::Result {
visitor.ts.push_str($ts);
}
}
)+
};
}
impl_ts!(String => "string");
impl_ts!(usize,u8,u16,u32,isize,i8,i16,i32,f32,f64 => "number");
impl_ts!(u64,u128,i64,i128 => "bigint");

View File

@@ -11,7 +11,7 @@ struct TestContext;
impl Context for TestContext {} impl Context for TestContext {}
#[derive(Debug, Deserialize, Serialize, Parser)] #[derive(Debug, Deserialize, Serialize, Parser)]
#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))] #[cfg_attr(feature = "ts", derive(ts_rs::TS))]
struct Thing1Params { struct Thing1Params {
thing: String, thing: String,
} }
@@ -30,7 +30,7 @@ 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-rs", derive(ts_rs::TS))] #[cfg_attr(feature = "ts", derive(ts_rs::TS))]
struct GroupParams { struct GroupParams {
#[arg(short, long)] #[arg(short, long)]
verbose: bool, verbose: bool,