diff --git a/Cargo.lock b/Cargo.lock index 5698b6d..03a8706 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1745,9 +1745,9 @@ checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "visit-rs" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbd4baf031bf580bc7ba411f4625a304909c975bf3d3e8d9c43ed88d23be1b8e" +checksum = "f89c27f644c3e4ce302bc7bce5144e295405d569784e5f4921271404e600cecf" dependencies = [ "async-stream", "futures", @@ -1757,9 +1757,9 @@ dependencies = [ [[package]] name = "visit-rs-derive" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2336cc680cfb205620939f0fa62348a4b6da37d1e727670f3d7d77aebc50b611" +checksum = "2a3bfb04fd13da4fc8df24709b7a0949667f43c63691d9fecddf1d3be8af5099" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 7df5969..ff2d11a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ authors = ["Aiden McClelland "] description = "A toolkit for creating JSON-RPC 2.0 servers with automatic cli bindings" documentation = "https://docs.rs/rpc-toolkit" -edition = "2018" +edition = "2024" keywords = ["cli", "json", "rpc"] license = "MIT" name = "rpc-toolkit" @@ -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.8", optional = true } +visit-rs = { version = "0.1.9", optional = true } yajrc = "0.1" diff --git a/src/server/mod.rs b/src/server/mod.rs index 3da0c4c..597f544 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -50,11 +50,11 @@ impl Server { &self, method: &str, params: Value, - ) -> impl Future> + Send + 'static { + ) -> impl Future> + Send + 'static + use { let (make_ctx, root_handler, method) = ( self.make_ctx.clone(), self.root_handler.clone(), - self.root_handler.method_from_dots(method), + self.root_handler.method_from_dots(method.as_ref()), ); async move { @@ -74,14 +74,11 @@ impl Server { &self, RpcRequest { id, method, params }: RpcRequest, ) -> impl Future + Send + 'static { - let handle = (|| Ok::<_, RpcError>(self.handle_command(method.as_str(), params)))(); + let handle = self.handle_command(method.as_str(), params); async move { RpcResponse { id, - result: match handle { - Ok(handle) => handle.await, - Err(e) => Err(e), - }, + result: handle.await, } } } diff --git a/src/ts.rs b/src/ts.rs index b2917b1..4340b89 100644 --- a/src/ts.rs +++ b/src/ts.rs @@ -6,7 +6,11 @@ use std::sync::Arc; use imbl_value::imbl::{OrdMap, Vector}; use imbl_value::InternedString; -use visit_rs::{Named, Static, Visit, VisitFieldsStaticNamed, Visitor}; +use visit_rs::{ + Named, Static, StructInfo, StructInfoData, Variant, Visit, VisitFieldsStatic, + VisitFieldsStaticNamed, VisitVariantFieldsStatic, VisitVariantFieldsStaticNamed, + VisitVariantsStatic, Visitor, +}; use crate::{Adapter, FromFn, FromFnAsync, FromFnAsyncLocal, HandlerTypes, ParentHandler}; @@ -315,6 +319,96 @@ where } } +#[derive(Default)] +pub struct SerdeTag { + pub tag: Option, + pub contents: Option, +} +impl SerdeTag { + pub fn apply_meta(this: &mut Option, meta: &syn::Meta) { + if meta.path().is_ident("untagged") { + *this = None; + } else if meta.path().is_ident("tag") { + if let Some(tag) = meta.require_name_value().ok() { + if let syn::Expr::Lit(syn::ExprLit { + lit: syn::Lit::Str(tag), + .. + }) = &tag.value + { + this.get_or_insert_default().tag = Some(tag.clone()); + } + } + } else if meta.path().is_ident("contents") { + if let Some(tag) = meta.require_name_value().ok() { + if let syn::Expr::Lit(syn::ExprLit { + lit: syn::Lit::Str(tag), + .. + }) = &tag.value + { + this.get_or_insert_default().contents = Some(tag.clone()); + } + } + } + } + pub fn from_metas(metas: &[syn::Meta]) -> Option { + let mut res = Some(SerdeTag::default()); + for meta in metas.into_iter().filter_map(|m| m.require_list().ok()) { + if meta.path.is_ident("serde") { + syn::parse2::(meta.tokens.clone()) + .ok() + .as_ref() + .map(|meta| Self::apply_meta(&mut res, meta)); + } + } + for meta in metas.into_iter().filter_map(|m| m.require_list().ok()) { + if meta.path.is_ident("visit") { + if let Some(meta) = syn::parse2::(meta.tokens.clone()) + .ok() + .as_ref() + .and_then(|m| m.require_list().ok()) + { + if meta.path.is_ident("ts") { + syn::parse2::(meta.tokens.clone()) + .ok() + .as_ref() + .map(|meta| Self::apply_meta(&mut res, meta)); + } + } + } + } + None + } +} + +impl<'a, T> Visit for Variant<'a, Static> +where + T: TS, + T: VisitVariantFieldsStaticNamed + VisitVariantFieldsStatic, +{ + fn visit(&self, visitor: &mut TSVisitor) -> ::Result { + // TODO: handle serde tagging + let tag = Some(SerdeTag { + tag: None, + contents: None, + }); + + if T::DATA.variant_count > 1 { + visitor.ts.push('|'); + } + visit_struct_impl( + &self.info, + |name, visitor| { + if name { + T::visit_variant_fields_static_named(&self.info, visitor).for_each(drop); + } else { + T::visit_variant_fields_static(&self.info, visitor).for_each(drop); + } + }, + visitor, + ); + } +} + pub struct LiteralTS(Cow<'static, str>); impl Visit for LiteralTS { fn visit(&self, visitor: &mut TSVisitor) -> ::Result { @@ -347,21 +441,43 @@ impl_ts!(u64,u128,i64,i128 => "bigint"); impl_ts!(Unknown,imbl_value::Value,serde_json::Value,serde_cbor::Value => "unknown"); impl_ts!(Never => "never"); +fn visit_struct_impl( + info: &StructInfoData, + fields: impl FnOnce(bool, &mut TSVisitor), + visitor: &mut TSVisitor, +) { + if !info.named_fields && info.field_count == 1 { + fields(false, visitor) + } else { + if info.named_fields { + visitor.ts.push_str("{"); + } else { + visitor.ts.push_str("["); + } + fields(true, visitor); + if info.named_fields { + visitor.ts.push_str("}"); + } else { + visitor.ts.push_str("]"); + } + } +} + pub fn visit_struct(visitor: &mut TSVisitor) where - T: VisitFieldsStaticNamed, + T: VisitFieldsStaticNamed + VisitFieldsStatic, { - 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("]"); - } + visit_struct_impl( + &T::DATA, + |named, visitor| { + if named { + T::visit_fields_static_named(visitor).for_each(drop); + } else { + T::visit_fields_static(visitor).for_each(drop); + } + }, + visitor, + ); } #[macro_export] @@ -376,6 +492,17 @@ macro_rules! impl_ts_struct { }; } +pub fn visit_enum(visitor: &mut TSVisitor) +where + T: VisitVariantsStatic, +{ + if T::DATA.variant_count == 0 { + visitor.append_type::() + } else { + T::visit_variants_static(visitor).for_each(drop) + } +} + pub fn visit_map(visitor: &mut TSVisitor) where K: TS,