diff --git a/.gitignore b/.gitignore index 2f7896d..948e252 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ target/ +rpc.sock \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index 333618a..839835f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,2 +1,42 @@ -[workspace] -members = ["rpc-toolkit", "rpc-toolkit-macro", "rpc-toolkit-macro-internals"] +[package] +authors = ["Aiden McClelland "] +edition = "2018" +name = "rpc-toolkit" +version = "0.2.3" +description = "A toolkit for creating JSON-RPC 2.0 servers with automatic cli bindings" +license = "MIT" +documentation = "https://docs.rs/rpc-toolkit" +keywords = ["json", "rpc", "cli"] +repository = "https://github.com/Start9Labs/rpc-toolkit" + + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[features] +cbor = ["serde_cbor"] +default = [] + +[dependencies] +axum = "0.7.3" +async-stream = "0.3" +async-trait = "0.1" +clap = { version = "4", features = ["derive"] } +futures = "0.3" +http = "1" +http-body-util = "0.1" +# hyper = { version = "1", features = ["server", "http1", "http2", "client"] } +itertools = "0.12" +imbl-value = { git = "https://github.com/Start9Labs/imbl-value.git" } +lazy_format = "2" +lazy_static = "1.4" +openssl = { version = "0.10", features = ["vendored"] } +pin-project = "1" +reqwest = { version = "0.11" } +rpc-toolkit-macro = { version = "0.2.2", path = "../rpc-toolkit-macro" } +serde = { version = "1.0", features = ["derive"] } +serde_cbor = { version = "0.11", optional = true } +serde_json = "1.0" +thiserror = "1.0" +tokio = { version = "1", features = ["full"] } +tokio-stream = { version = "0.1", features = ["io-util", "net"] } +url = "2" +yajrc = "0.1" diff --git a/rpc-toolkit-macro-internals/.gitignore b/rpc-toolkit-macro-internals/.gitignore deleted file mode 100644 index 96ef6c0..0000000 --- a/rpc-toolkit-macro-internals/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/target -Cargo.lock diff --git a/rpc-toolkit-macro-internals/Cargo.toml b/rpc-toolkit-macro-internals/Cargo.toml deleted file mode 100644 index dde611b..0000000 --- a/rpc-toolkit-macro-internals/Cargo.toml +++ /dev/null @@ -1,14 +0,0 @@ -[package] -authors = ["Aiden McClelland "] -edition = "2018" -name = "rpc-toolkit-macro-internals" -version = "0.2.2" -description = "internals for macros for rpc-toolkit" -license = "MIT" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html -[dependencies] -proc-macro2 = "1.0" -quote = "1.0" -syn = { version = "1.0", features = ["full", "fold"] } -itertools = "0.12" diff --git a/rpc-toolkit-macro-internals/src/command/build.rs b/rpc-toolkit-macro-internals/src/command/build.rs deleted file mode 100644 index 8b3190f..0000000 --- a/rpc-toolkit-macro-internals/src/command/build.rs +++ /dev/null @@ -1,1511 +0,0 @@ -use std::collections::HashSet; - -use itertools::MultiUnzip; -use proc_macro2::*; -use quote::*; -use syn::fold::Fold; -use syn::punctuated::Punctuated; -use syn::spanned::Spanned; -use syn::token::{Add, Comma, Where}; - -use super::parse::*; -use super::*; - -// fn ctx_trait(ctx_ty: Option, opt: &mut Options) -> TokenStream { -// let mut bounds: Punctuated = Punctuated::new(); -// bounds.push(macro_try!(parse2(quote! { ::rpc_toolkit::Context }))); -// let mut rpc_bounds = bounds.clone(); -// let mut cli_bounds = bounds; - -// let (use_cli, use_rpc) = match &opt.common().exec_ctx { -// ExecutionContext::CliOnly(_) => (Some(None), false), -// ExecutionContext::RpcOnly(_) | ExecutionContext::Standard => (None, true), -// ExecutionContext::Local(_) => (Some(None), true), -// ExecutionContext::CustomCli { context, .. } => (Some(Some(context.clone())), true), -// }; - -// if let Options::Parent(ParentOptions { -// subcommands, -// self_impl, -// .. -// }) = opt -// { -// if let Some(ctx_ty) = ctx_ty { -// cli_bounds.push(macro_try!(parse2(quote! { Into<#ctx_ty> }))); -// cli_bounds.push(macro_try!(parse2(quote! { Clone }))); -// rpc_bounds.push(macro_try!(parse2(quote! { Into<#ctx_ty> }))); -// rpc_bounds.push(macro_try!(parse2(quote! { Clone }))); -// } -// if let Some(SelfImplInfo { context, .. }) = self_impl { -// if let Some(cli_ty) = use_cli.as_ref() { -// if let Some(cli_ty) = cli_ty { -// cli_bounds.push(macro_try!(parse2(quote! { Into<#cli_ty> }))); -// } else { -// cli_bounds.push(macro_try!(parse2(quote! { Into<#context> }))); -// } -// } -// if use_rpc { -// rpc_bounds.push(macro_try!(parse2(quote! { Into<#context> }))); -// } -// } -// for subcmd in subcommands { -// let mut path = subcmd.clone(); -// std::mem::take(&mut path.segments.last_mut().unwrap().arguments); -// cli_bounds.push(macro_try!(parse2(quote! { #path::CommandContextCli }))); -// rpc_bounds.push(macro_try!(parse2(quote! { #path::CommandContextRpc }))); -// } -// } else { -// if let Some(cli_ty) = use_cli.as_ref() { -// if let Some(cli_ty) = cli_ty { -// cli_bounds.push(macro_try!(parse2(quote! { Into<#cli_ty> }))); -// } else if let Some(ctx_ty) = &ctx_ty { -// cli_bounds.push(macro_try!(parse2(quote! { Into<#ctx_ty> }))); -// } -// } -// if use_rpc { -// if let Some(ctx_ty) = &ctx_ty { -// rpc_bounds.push(macro_try!(parse2(quote! { Into<#ctx_ty> }))); -// } -// } -// } - -// let res = quote! { -// pub trait CommandContextCli: #cli_bounds {} -// impl CommandContextCli for T where T: #cli_bounds {} - -// pub trait CommandContextRpc: #rpc_bounds {} -// impl CommandContextRpc for T where T: #rpc_bounds {} -// }; -// res -// } - -// fn metadata(full_options: &Options) -> TokenStream { -// let options = match full_options { -// Options::Leaf(a) => a, -// Options::Parent(ParentOptions { common, .. }) => common, -// }; -// let fallthrough = |ty: &str| { -// let getter_name = Ident::new(&format!("get_{}", ty), Span::call_site()); -// match &*full_options { -// Options::Parent(ParentOptions { subcommands, .. }) => { -// let subcmd_handler = subcommands.iter().map(|subcmd| { -// let mut subcmd = subcmd.clone(); -// subcmd.segments.last_mut().unwrap().arguments = PathArguments::None; -// quote_spanned!{ subcmd.span() => -// [#subcmd::NAME, rest] => if let Some(val) = #subcmd::Metadata.#getter_name(rest, key) { -// return Some(val); -// }, -// } -// }); -// quote! { -// if !command.is_empty() { -// match command.splitn(2, ".").chain(std::iter::repeat("")).take(2).collect::>().as_slice() { -// #( -// #subcmd_handler -// )* -// _ => () -// } -// } -// } -// } -// _ => quote! {}, -// } -// }; -// fn impl_getter>( -// ty: &str, -// metadata: I, -// fallthrough: TokenStream, -// ) -> TokenStream { -// let getter_name = Ident::new(&format!("get_{}", ty), Span::call_site()); -// let ty: Type = syn::parse_str(ty).unwrap(); -// quote! { -// fn #getter_name(&self, command: &str, key: &str) -> Option<#ty> { -// #fallthrough -// match key { -// #(#metadata)* -// _ => None, -// } -// } -// } -// } -// let bool_metadata = options -// .metadata -// .iter() -// .filter(|(_, lit)| matches!(lit, Lit::Bool(_))) -// .map(|(name, value)| { -// let name = LitStr::new(&name.to_string(), name.span()); -// quote! { -// #name => Some(#value), -// } -// }); -// let number_metadata = |ty: &str| { -// let ty: Type = syn::parse_str(ty).unwrap(); -// options -// .metadata -// .iter() -// .filter(|(_, lit)| matches!(lit, Lit::Int(_) | Lit::Float(_) | Lit::Byte(_))) -// .map(move |(name, value)| { -// let name = LitStr::new(&name.to_string(), name.span()); -// quote! { -// #name => Some(#value as #ty), -// } -// }) -// }; -// let char_metadata = options -// .metadata -// .iter() -// .filter(|(_, lit)| matches!(lit, Lit::Char(_))) -// .map(|(name, value)| { -// let name = LitStr::new(&name.to_string(), name.span()); -// quote! { -// #name => Some(#value), -// } -// }); -// let str_metadata = options -// .metadata -// .iter() -// .filter(|(_, lit)| matches!(lit, Lit::Str(_))) -// .map(|(name, value)| { -// let name = LitStr::new(&name.to_string(), name.span()); -// quote! { -// #name => Some(#value), -// } -// }); -// let bstr_metadata = options -// .metadata -// .iter() -// .filter(|(_, lit)| matches!(lit, Lit::ByteStr(_))) -// .map(|(name, value)| { -// let name = LitStr::new(&name.to_string(), name.span()); -// quote! { -// #name => Some(#value), -// } -// }); - -// let bool_getter = impl_getter("bool", bool_metadata, fallthrough("bool")); -// let u8_getter = impl_getter("u8", number_metadata("u8"), fallthrough("u8")); -// let u16_getter = impl_getter("u16", number_metadata("u16"), fallthrough("u16")); -// let u32_getter = impl_getter("u32", number_metadata("u32"), fallthrough("u32")); -// let u64_getter = impl_getter("u64", number_metadata("u64"), fallthrough("u64")); -// let usize_getter = impl_getter("usize", number_metadata("usize"), fallthrough("usize")); -// let i8_getter = impl_getter("i8", number_metadata("i8"), fallthrough("i8")); -// let i16_getter = impl_getter("i16", number_metadata("i16"), fallthrough("i16")); -// let i32_getter = impl_getter("i32", number_metadata("i32"), fallthrough("i32")); -// let i64_getter = impl_getter("i64", number_metadata("i64"), fallthrough("i64")); -// let isize_getter = impl_getter("isize", number_metadata("isize"), fallthrough("isize")); -// let f32_getter = impl_getter("f32", number_metadata("f32"), fallthrough("f32")); -// let f64_getter = impl_getter("f64", number_metadata("f64"), fallthrough("f64")); -// let char_getter = impl_getter("char", char_metadata, fallthrough("char")); -// let str_fallthrough = fallthrough("str"); -// let str_getter = quote! { -// fn get_str(&self, command: &str, key: &str) -> Option<&'static str> { -// #str_fallthrough -// match key { -// #(#str_metadata)* -// _ => None, -// } -// } -// }; -// let bstr_fallthrough = fallthrough("bstr"); -// let bstr_getter = quote! { -// fn get_bstr(&self, command: &str, key: &str) -> Option<&'static [u8]> { -// #bstr_fallthrough -// match key { -// #(#bstr_metadata)* -// _ => None, -// } -// } -// }; - -// let res = quote! { -// #[derive(Clone, Copy, Default)] -// pub struct Metadata; - -// #[allow(overflowing_literals)] -// impl ::rpc_toolkit::Metadata for Metadata { -// #bool_getter -// #u8_getter -// #u16_getter -// #u32_getter -// #u64_getter -// #usize_getter -// #i8_getter -// #i16_getter -// #i32_getter -// #i64_getter -// #isize_getter -// #f32_getter -// #f64_getter -// #char_getter -// #str_getter -// #bstr_getter -// } -// }; -// // panic!("{}", res); -// res -// } - -// fn build_app(name: LitStr, opt: &mut Options, params: &mut [ParamType]) -> TokenStream { -// let about = opt.common().about.clone().into_iter(); -// let (subcommand, subcommand_required) = if let Options::Parent(opt) = opt { -// ( -// opt.subcommands -// .iter() -// .map(|subcmd| { -// let mut path = subcmd.clone(); -// path.segments.last_mut().unwrap().arguments = PathArguments::None; -// path -// }) -// .collect(), -// opt.self_impl.is_none(), -// ) -// } else { -// (Vec::new(), false) -// }; -// let arg = params -// .iter_mut() -// .filter_map(|param| { -// if let ParamType::Arg(arg) = param { -// if arg.stdin.is_some() { -// return None; -// } -// let name = arg.name.clone().unwrap(); -// let name_str = arg.rename.clone().unwrap_or_else(|| LitStr::new(&name.to_string(), name.span())); -// let help = arg.help.clone().into_iter(); -// let short = arg.short.clone().into_iter(); -// let long = arg.long.clone().into_iter(); -// let mut modifications = TokenStream::default(); -// let ty_span = arg.ty.span(); -// if let Type::Path(p) = &mut arg.ty { -// if p.path.is_ident("bool") -// && arg.parse.is_none() -// && (arg.short.is_some() || arg.long.is_some()) -// { -// arg.check_is_present = true; -// modifications.extend(quote_spanned! { ty_span => -// arg = arg.takes_value(false); -// }); -// } else if arg.count.is_some() { -// modifications.extend(quote_spanned! { ty_span => -// arg = arg.takes_value(false); -// arg = arg.multiple(true); -// }); -// } else { -// modifications.extend(quote_spanned! { ty_span => -// arg = arg.takes_value(true); -// }); -// if let Some(_) = &arg.default { -// modifications.extend(quote_spanned! { ty_span => -// arg = arg.required(false); -// }); -// } else if p.path.segments.last().unwrap().ident == "Option" { -// arg.optional = true; -// modifications.extend(quote_spanned! { ty_span => -// arg = arg.required(false); -// }); -// } else if arg.multiple.is_some() { -// modifications.extend(quote_spanned! { ty_span => -// arg = arg.multiple(true); -// }); -// } else { -// modifications.extend(quote_spanned! { ty_span => -// arg = arg.required(true); -// }); -// } -// } -// }; -// Some(quote! { -// { -// let mut arg = ::rpc_toolkit::command_helpers::prelude::Arg::with_name(#name_str); -// #( -// arg = arg.help(#help); -// )* -// #( -// arg = arg.short(#short); -// )* -// #( -// arg = arg.long(#long); -// )* -// #modifications - -// arg -// } -// }) -// } else { -// None -// } -// }) -// .collect::>(); -// let required = LitBool::new(subcommand_required, Span::call_site()); -// let alias = &opt.common().aliases; -// quote! { -// pub fn build_app() -> ::rpc_toolkit::command_helpers::prelude::App<'static> { -// let mut app = ::rpc_toolkit::command_helpers::prelude::App::new(#name); -// #( -// app = app.about(#about); -// )* -// #( -// app = app.alias(#alias); -// )* -// #( -// app = app.arg(#arg); -// )* -// #( -// app = app.subcommand(#subcommand::build_app()); -// )* -// if #required { -// app = app.setting(::rpc_toolkit::command_helpers::prelude::AppSettings::SubcommandRequired); -// } -// app -// } -// } -// } - -struct GenericFilter<'a> { - src: &'a Generics, - lifetimes: HashSet, - types: HashSet, -} -impl<'a> GenericFilter<'a> { - fn new(src: &'a Generics) -> Self { - GenericFilter { - src, - lifetimes: HashSet::new(), - types: HashSet::new(), - } - } - fn finish(self) -> Generics { - let mut params: Punctuated = Default::default(); - let mut where_clause = self - .src - .where_clause - .as_ref() - .map(|wc| WhereClause { - where_token: wc.where_token, - predicates: Default::default(), - }) - .unwrap_or_else(|| WhereClause { - where_token: Where(Span::call_site()), - predicates: Default::default(), - }); - for src_param in &self.src.params { - match src_param { - GenericParam::Lifetime(l) if self.lifetimes.contains(&l.lifetime) => { - params.push(src_param.clone()) - } - GenericParam::Type(t) if self.types.contains(&t.ident) => { - params.push(src_param.clone()) - } - _ => (), - } - } - for src_predicate in self.src.where_clause.iter().flat_map(|wc| &wc.predicates) { - match src_predicate { - WherePredicate::Lifetime(l) if self.lifetimes.contains(&l.lifetime) => { - where_clause.predicates.push(src_predicate.clone()) - } - WherePredicate::Type(PredicateType { - bounded_ty: Type::Path(t), - .. - }) if self.types.contains(&t.path.segments.first().unwrap().ident) => { - where_clause.predicates.push(src_predicate.clone()) - } - _ => (), - } - } - Generics { - lt_token: if params.is_empty() { - None - } else { - self.src.lt_token.clone() - }, - gt_token: if params.is_empty() { - None - } else { - self.src.gt_token.clone() - }, - params, - where_clause: if where_clause.predicates.is_empty() { - None - } else { - Some(where_clause) - }, - } - } -} -impl<'a> Fold for GenericFilter<'a> { - fn fold_lifetime(&mut self, i: Lifetime) -> Lifetime { - self.lifetimes - .extend(self.src.params.iter().filter_map(|param| match param { - GenericParam::Lifetime(l) if l.lifetime == i => Some(l.lifetime.clone()), - _ => None, - })); - i - } - fn fold_type(&mut self, i: Type) -> Type { - self.types.extend( - self.src - .params - .iter() - .filter_map(|param| match (param, &i) { - (GenericParam::Type(t), Type::Path(i)) - if &i.path.segments.first().unwrap().ident == &t.ident => - { - Some(t.ident.clone()) - } - _ => None, - }), - ); - i - } -} - -// fn rpc_handler( -// fn_name: &Ident, -// fn_generics: &Generics, -// opt: &Options, -// params: &[ParamType], -// ) -> TokenStream { -// let mut parent_data_ty = quote! { () }; -// let mut generics = fn_generics.clone(); -// generics.params.push(macro_try!(syn::parse2( -// quote! { GenericContext: CommandContextRpc } -// ))); -// if generics.lt_token.is_none() { -// generics.lt_token = Some(Default::default()); -// } -// if generics.gt_token.is_none() { -// generics.gt_token = Some(Default::default()); -// } -// let mut param_def = Vec::new(); -// for param in params { -// match param { -// ParamType::Arg(arg) => { -// let name = arg.name.clone().unwrap(); -// let rename = arg -// .rename -// .clone() -// .unwrap_or_else(|| LitStr::new(&name.to_string(), name.span())); -// let field_name = Ident::new(&format!("arg_{}", name), name.span()); -// let ty = arg.ty.clone(); -// let def = quote! { -// #[serde(rename = #rename)] -// #field_name: #ty, -// }; -// let def = match &arg.default { -// Some(Some(default)) => { -// quote! { -// #[serde(default = #default)] -// #def -// } -// } -// Some(None) => { -// quote! { -// #[serde(default)] -// #def -// } -// } -// None => def, -// }; -// param_def.push(def); -// } -// ParamType::ParentData(ty) => parent_data_ty = quote! { #ty }, -// _ => (), -// } -// } -// let (_, fn_type_generics, _) = fn_generics.split_for_impl(); -// let fn_turbofish = fn_type_generics.as_turbofish(); -// let fn_path: Path = macro_try!(syn::parse2(quote! { super::#fn_name#fn_turbofish })); -// let mut param_generics_filter = GenericFilter::new(fn_generics); -// for param in params { -// if let ParamType::Arg(a) = param { -// param_generics_filter.fold_type(a.ty.clone()); -// } -// } -// let param_generics = param_generics_filter.finish(); -// let (_, param_ty_generics, _) = param_generics.split_for_impl(); -// let param_struct_def = quote! { -// #[allow(dead_code)] -// #[derive(::rpc_toolkit::command_helpers::prelude::Deserialize)] -// pub struct Params#param_ty_generics { -// #( -// #param_def -// )* -// #[serde(flatten)] -// #[serde(default)] -// rest: ::rpc_toolkit::command_helpers::prelude::Value, -// } -// }; -// let param = params.iter().map(|param| match param { -// ParamType::Arg(arg) => { -// let name = arg.name.clone().unwrap(); -// let field_name = Ident::new(&format!("arg_{}", name), name.span()); -// quote! { args.#field_name } -// } -// ParamType::Context(ty) => { -// if matches!(opt, Options::Parent { .. }) { -// quote! { >::into(ctx.clone()) } -// } else { -// quote! { >::into(ctx) } -// } -// } -// ParamType::ParentData(_) => { -// quote! { parent_data } -// } -// ParamType::Request => quote! { request }, -// ParamType::Response => quote! { response }, -// ParamType::None => unreachable!(), -// }); -// match opt { -// Options::Leaf(opt) if matches!(opt.exec_ctx, ExecutionContext::CliOnly(_)) => quote! { -// #param_struct_def - -// pub async fn rpc_handler#generics( -// _ctx: GenericContext, -// _parent_data: #parent_data_ty, -// _request: &::rpc_toolkit::command_helpers::prelude::RequestParts, -// _response: &mut ::rpc_toolkit::command_helpers::prelude::ResponseParts, -// method: &str, -// _args: Params#param_ty_generics, -// ) -> Result<::rpc_toolkit::command_helpers::prelude::Value, ::rpc_toolkit::command_helpers::prelude::RpcError> { -// Err(::rpc_toolkit::command_helpers::prelude::RpcError { -// data: Some(method.into()), -// ..::rpc_toolkit::command_helpers::prelude::yajrc::METHOD_NOT_FOUND_ERROR -// }) -// } -// }, -// Options::Leaf(opt) => { -// let invocation = if opt.is_async { -// quote! { -// #fn_path(#(#param),*).await? -// } -// } else if opt.blocking.is_some() { -// quote! { -// ::rpc_toolkit::command_helpers::prelude::spawn_blocking(move || #fn_path(#(#param),*)).await? -// } -// } else { -// quote! { -// #fn_path(#(#param),*)? -// } -// }; -// quote! { -// #param_struct_def - -// pub async fn rpc_handler#generics( -// ctx: GenericContext, -// parent_data: #parent_data_ty, -// request: &::rpc_toolkit::command_helpers::prelude::RequestParts, -// response: &mut ::rpc_toolkit::command_helpers::prelude::ResponseParts, -// method: &str, -// args: Params#param_ty_generics, -// ) -> Result<::rpc_toolkit::command_helpers::prelude::Value, ::rpc_toolkit::command_helpers::prelude::RpcError> { -// if method.is_empty() { -// Ok(::rpc_toolkit::command_helpers::prelude::to_value(#invocation)?) -// } else { -// Err(::rpc_toolkit::command_helpers::prelude::RpcError { -// data: Some(method.into()), -// ..::rpc_toolkit::command_helpers::prelude::yajrc::METHOD_NOT_FOUND_ERROR -// }) -// } -// } -// } -// } -// Options::Parent(ParentOptions { -// common, -// subcommands, -// self_impl, -// }) => { -// let cmd_preprocess = if common.is_async { -// quote! { -// let parent_data = #fn_path(#(#param),*).await?; -// } -// } else if common.blocking.is_some() { -// quote! { -// let parent_data = ::rpc_toolkit::command_helpers::prelude::spawn_blocking(move || #fn_path(#(#param),*)).await?; -// } -// } else { -// quote! { -// let parent_data = #fn_path(#(#param),*)?; -// } -// }; -// let subcmd_impl = subcommands.iter().map(|subcommand| { -// let mut subcommand = subcommand.clone(); -// let mut rpc_handler = PathSegment { -// ident: Ident::new("rpc_handler", Span::call_site()), -// arguments: std::mem::replace( -// &mut subcommand.segments.last_mut().unwrap().arguments, -// PathArguments::None, -// ), -// }; -// rpc_handler.arguments = match rpc_handler.arguments { -// PathArguments::None => PathArguments::AngleBracketed( -// syn::parse2(quote! { :: }) -// .unwrap(), -// ), -// PathArguments::AngleBracketed(mut a) => { -// a.args.push(syn::parse2(quote! { GenericContext }).unwrap()); -// PathArguments::AngleBracketed(a) -// } -// _ => unreachable!(), -// }; -// quote_spanned!{ subcommand.span() => -// [#subcommand::NAME, rest] => #subcommand::#rpc_handler(ctx, parent_data, request, response, rest, ::rpc_toolkit::command_helpers::prelude::from_value(args.rest)?).await -// } -// }); -// let subcmd_impl = quote! { -// match method.splitn(2, ".").chain(std::iter::repeat("")).take(2).collect::>().as_slice() { -// #( -// #subcmd_impl, -// )* -// _ => Err(::rpc_toolkit::command_helpers::prelude::RpcError { -// data: Some(method.into()), -// ..::rpc_toolkit::command_helpers::prelude::yajrc::METHOD_NOT_FOUND_ERROR -// }) -// } -// }; -// match self_impl { -// Some(self_impl) if !matches!(common.exec_ctx, ExecutionContext::CliOnly(_)) => { -// let self_impl_fn = &self_impl.path; -// let self_impl = if self_impl.is_async { -// quote_spanned! { self_impl_fn.span() => -// #self_impl_fn(Into::into(ctx), parent_data).await? -// } -// } else if self_impl.blocking { -// quote_spanned! { self_impl_fn.span() => -// { -// let ctx = Into::into(ctx); -// ::rpc_toolkit::command_helpers::prelude::spawn_blocking(move || #self_impl_fn(ctx, parent_data)).await? -// } -// } -// } else { -// quote_spanned! { self_impl_fn.span() => -// #self_impl_fn(Into::into(ctx), parent_data)? -// } -// }; -// quote! { -// #param_struct_def - -// pub async fn rpc_handler#generics( -// ctx: GenericContext, -// parent_data: #parent_data_ty, -// request: &::rpc_toolkit::command_helpers::prelude::RequestParts, -// response: &mut ::rpc_toolkit::command_helpers::prelude::ResponseParts, -// method: &str, -// args: Params#param_ty_generics, -// ) -> Result<::rpc_toolkit::command_helpers::prelude::Value, ::rpc_toolkit::command_helpers::prelude::RpcError> { -// #cmd_preprocess - -// if method.is_empty() { -// Ok(::rpc_toolkit::command_helpers::prelude::to_value(&#self_impl)?) -// } else { -// #subcmd_impl -// } -// } -// } -// } -// _ => { -// quote! { -// #param_struct_def - -// pub async fn rpc_handler#generics( -// ctx: GenericContext, -// parent_data: #parent_data_ty, -// request: &::rpc_toolkit::command_helpers::prelude::RequestParts, -// response: &mut ::rpc_toolkit::command_helpers::prelude::ResponseParts, -// method: &str, -// args: Params#param_ty_generics, -// ) -> Result<::rpc_toolkit::command_helpers::prelude::Value, ::rpc_toolkit::command_helpers::prelude::RpcError> { -// #cmd_preprocess - -// #subcmd_impl -// } -// } -// } -// } -// } -// } -// } - -// fn cli_handler( -// fn_name: &Ident, -// fn_generics: &Generics, -// opt: &mut Options, -// params: &[ParamType], -// ) -> TokenStream { -// let mut parent_data_ty = quote! { () }; -// let mut generics = fn_generics.clone(); -// generics.params.push(macro_try!(syn::parse2( -// quote! { ParentParams: ::rpc_toolkit::command_helpers::prelude::Serialize } -// ))); -// generics.params.push(macro_try!(syn::parse2( -// quote! { GenericContext: CommandContextCli } -// ))); -// if generics.lt_token.is_none() { -// generics.lt_token = Some(Default::default()); -// } -// if generics.gt_token.is_none() { -// generics.gt_token = Some(Default::default()); -// } -// let (_, fn_type_generics, _) = fn_generics.split_for_impl(); -// let fn_turbofish = fn_type_generics.as_turbofish(); -// let fn_path: Path = macro_try!(syn::parse2(quote! { super::#fn_name#fn_turbofish })); -// let is_parent = matches!(opt, Options::Parent { .. }); -// let param: Vec<_> = params -// .iter() -// .map(|param| match param { -// ParamType::Arg(arg) => { -// let name = arg.name.clone().unwrap(); -// let field_name = Ident::new(&format!("arg_{}", name), name.span()); -// quote! { params.#field_name.clone() } -// } -// ParamType::Context(ty) => { -// if is_parent { -// quote! { >::into(ctx.clone()) } -// } else { -// quote! { >::into(ctx) } -// } -// } -// ParamType::ParentData(ty) => { -// parent_data_ty = quote! { #ty }; -// quote! { parent_data } -// } -// ParamType::Request => quote! { request }, -// ParamType::Response => quote! { response }, -// ParamType::None => unreachable!(), -// }) -// .collect(); -// let mut param_generics_filter = GenericFilter::new(fn_generics); -// for param in params { -// if let ParamType::Arg(a) = param { -// param_generics_filter.fold_type(a.ty.clone()); -// } -// } -// let mut param_generics = param_generics_filter.finish(); -// param_generics.params.push(macro_try!(syn::parse2(quote! { -// ParentParams: ::rpc_toolkit::command_helpers::prelude::Serialize -// }))); -// if param_generics.lt_token.is_none() { -// param_generics.lt_token = Some(Default::default()); -// } -// if param_generics.gt_token.is_none() { -// param_generics.gt_token = Some(Default::default()); -// } -// let (_, param_ty_generics, _) = param_generics.split_for_impl(); -// let mut arg_def = Vec::new(); -// for param in params { -// match param { -// ParamType::Arg(arg) => { -// let name = arg.name.clone().unwrap(); -// let rename = arg -// .rename -// .clone() -// .unwrap_or_else(|| LitStr::new(&name.to_string(), name.span())); -// let field_name = Ident::new(&format!("arg_{}", name), name.span()); -// let ty = arg.ty.clone(); -// arg_def.push(quote! { -// #[serde(rename = #rename)] -// #field_name: #ty, -// }) -// } -// _ => (), -// } -// } -// let arg = params -// .iter() -// .filter_map(|param| { -// if let ParamType::Arg(a) = param { -// Some(a) -// } else { -// None -// } -// }) -// .map(|arg| { -// let name = arg.name.clone().unwrap(); -// let arg_name = arg.rename.clone().unwrap_or_else(|| LitStr::new(&name.to_string(), name.span())); -// let field_name = Ident::new(&format!("arg_{}", name), name.span()); -// if arg.stdin.is_some() { -// if let Some(parse) = &arg.parse { -// quote! { -// #field_name: #parse(&mut std::io::stdin(), matches)?, -// } -// } else { -// quote! { -// #field_name: ::rpc_toolkit::command_helpers::prelude::default_stdin_parser(&mut std::io::stdin(), matches)?, -// } -// } -// } else if arg.check_is_present { -// quote! { -// #field_name: matches.is_present(#arg_name), -// } -// } else if arg.count.is_some() { -// quote! { -// #field_name: matches.occurrences_of(#arg_name), -// } -// } else { -// let parse_val = if let Some(parse) = &arg.parse { -// quote! { -// #parse(arg_val, matches) -// } -// } else { -// quote! { -// ::rpc_toolkit::command_helpers::prelude::default_arg_parser(arg_val, matches) -// } -// }; -// if arg.optional { -// quote! { -// #field_name: if let Some(arg_val) = matches.value_of(#arg_name) { -// Some(#parse_val?) -// } else { -// None -// }, -// } -// } else if let Some(default) = &arg.default { -// if let Some(default) = default { -// let path: Path = match syn::parse_str(&default.value()) { -// Ok(a) => a, -// Err(e) => return e.into_compile_error(), -// }; -// quote! { -// #field_name: if let Some(arg_val) = matches.value_of(#arg_name) { -// #parse_val? -// } else { -// #path() -// }, -// } -// } else { -// quote! { -// #field_name: if let Some(arg_val) = matches.value_of(#arg_name) { -// #parse_val? -// } else { -// Default::default() -// }, -// } -// } -// } else if arg.multiple.is_some() { -// quote! { -// #field_name: matches.values_of(#arg_name).iter().flatten().map(|arg_val| #parse_val).collect::>()?, -// } -// } else { -// quote! { -// #field_name: { -// let arg_val = matches.value_of(#arg_name).unwrap(); -// #parse_val? -// }, -// } -// } -// } -// }); -// let param_struct_def = quote! { -// #[derive(::rpc_toolkit::command_helpers::prelude::Serialize)] -// struct Params#param_ty_generics { -// #( -// #arg_def -// )* -// #[serde(flatten)] -// rest: ParentParams, -// } -// let params: Params#param_ty_generics = Params { -// #( -// #arg -// )* -// rest: parent_params, -// }; -// }; -// let create_rt = quote! { -// let rt_ref = if let Some(rt) = rt.as_mut() { -// &*rt -// } else { -// rt = Some(::rpc_toolkit::command_helpers::prelude::Runtime::new().map_err(|e| ::rpc_toolkit::command_helpers::prelude::RpcError { -// data: Some(format!("{}", e).into()), -// ..::rpc_toolkit::command_helpers::prelude::yajrc::INTERNAL_ERROR -// })?); -// rt.as_ref().unwrap() -// }; -// }; -// let display = if let Some(display) = &opt.common().display { -// quote! { #display } -// } else { -// quote! { ::rpc_toolkit::command_helpers::prelude::default_display } -// }; -// match opt { -// Options::Leaf(opt) if matches!(opt.exec_ctx, ExecutionContext::RpcOnly(_)) => quote! { -// pub fn cli_handler#generics( -// _ctx: GenericContext, -// _parent_data: #parent_data_ty, -// _rt: Option<::rpc_toolkit::command_helpers::prelude::Runtime>, -// _matches: &::rpc_toolkit::command_helpers::prelude::ArgMatches, -// method: ::rpc_toolkit::command_helpers::prelude::Cow<'_, str>, -// _parent_params: ParentParams, -// ) -> Result<(), ::rpc_toolkit::command_helpers::prelude::RpcError> { -// Err(::rpc_toolkit::command_helpers::prelude::RpcError { -// data: Some(method.into()), -// ..::rpc_toolkit::command_helpers::prelude::yajrc::METHOD_NOT_FOUND_ERROR -// }) -// } -// }, -// Options::Leaf(opt) if matches!(opt.exec_ctx, ExecutionContext::Standard) => { -// let param = param.into_iter().map(|_| quote! { unreachable!() }); -// let invocation = if opt.is_async { -// quote! { -// rt_ref.block_on(#fn_path(#(#param),*))? -// } -// } else { -// quote! { -// #fn_path(#(#param),*)? -// } -// }; -// quote! { -// pub fn cli_handler#generics( -// ctx: GenericContext, -// parent_data: #parent_data_ty, -// mut rt: Option<::rpc_toolkit::command_helpers::prelude::Runtime>, -// matches: &::rpc_toolkit::command_helpers::prelude::ArgMatches, -// method: ::rpc_toolkit::command_helpers::prelude::Cow<'_, str>, -// parent_params: ParentParams, -// ) -> Result<(), ::rpc_toolkit::command_helpers::prelude::RpcError> { -// #param_struct_def - -// #create_rt - -// #[allow(unreachable_code)] -// let return_ty = if true { -// ::rpc_toolkit::command_helpers::prelude::PhantomData -// } else { -// let ctx_new = unreachable!(); -// ::rpc_toolkit::command_helpers::prelude::match_types(&ctx, &ctx_new); -// let ctx = ctx_new; -// ::rpc_toolkit::command_helpers::prelude::make_phantom(#invocation) -// }; - -// let res = rt_ref.block_on(::rpc_toolkit::command_helpers::prelude::call_remote(ctx, method.as_ref(), params, return_ty))?; -// Ok(#display(res.result?, matches)) -// } -// } -// } -// Options::Leaf(opt) => { -// if let ExecutionContext::CustomCli { -// ref cli, is_async, .. -// } = opt.exec_ctx -// { -// let fn_path = cli; -// let cli_param = params.iter().filter_map(|param| match param { -// ParamType::Arg(arg) => { -// let name = arg.name.clone().unwrap(); -// let field_name = Ident::new(&format!("arg_{}", name), name.span()); -// Some(quote! { params.#field_name.clone() }) -// } -// ParamType::Context(_) => Some(quote! { Into::into(ctx) }), -// ParamType::ParentData(_) => Some(quote! { parent_data }), -// ParamType::Request => None, -// ParamType::Response => None, -// ParamType::None => unreachable!(), -// }); -// let invocation = if is_async { -// quote! { -// rt_ref.block_on(#fn_path(#(#cli_param),*))? -// } -// } else { -// quote! { -// #fn_path(#(#cli_param),*)? -// } -// }; -// let display_res = if let Some(display_fn) = &opt.display { -// quote! { -// #display_fn(#invocation, matches) -// } -// } else { -// quote! { -// ::rpc_toolkit::command_helpers::prelude::default_display(#invocation, matches) -// } -// }; -// let rt_action = if is_async { -// create_rt -// } else { -// quote! { -// drop(rt); -// } -// }; -// quote! { -// pub fn cli_handler#generics( -// ctx: GenericContext, -// parent_data: #parent_data_ty, -// mut rt: Option<::rpc_toolkit::command_helpers::prelude::Runtime>, -// matches: &::rpc_toolkit::command_helpers::prelude::ArgMatches, -// _method: ::rpc_toolkit::command_helpers::prelude::Cow<'_, str>, -// parent_params: ParentParams -// ) -> Result<(), ::rpc_toolkit::command_helpers::prelude::RpcError> { -// #param_struct_def - -// #rt_action - -// Ok(#display_res) -// } -// } -// } else { -// let invocation = if opt.is_async { -// quote! { -// rt_ref.block_on(#fn_path(#(#param),*))? -// } -// } else { -// quote! { -// #fn_path(#(#param),*)? -// } -// }; -// let display_res = if let Some(display_fn) = &opt.display { -// quote! { -// #display_fn(#invocation, matches) -// } -// } else { -// quote! { -// ::rpc_toolkit::command_helpers::prelude::default_display(#invocation, matches) -// } -// }; -// let rt_action = if opt.is_async { -// create_rt -// } else { -// quote! { -// drop(rt); -// } -// }; -// quote! { -// pub fn cli_handler#generics( -// ctx: GenericContext, -// parent_data: #parent_data_ty, -// mut rt: Option<::rpc_toolkit::command_helpers::prelude::Runtime>, -// matches: &::rpc_toolkit::command_helpers::prelude::ArgMatches, -// _method: ::rpc_toolkit::command_helpers::prelude::Cow<'_, str>, -// parent_params: ParentParams -// ) -> Result<(), ::rpc_toolkit::command_helpers::prelude::RpcError> { -// #param_struct_def - -// #rt_action - -// Ok(#display_res) -// } -// } -// } -// } -// Options::Parent(ParentOptions { -// common, -// subcommands, -// self_impl, -// }) => { -// let cmd_preprocess = if common.is_async { -// quote! { -// #create_rt -// let parent_data = rt_ref.block_on(#fn_path(#(#param),*))?; -// } -// } else { -// quote! { -// let parent_data = #fn_path(#(#param),*)?; -// } -// }; -// let subcmd_impl = subcommands.iter().map(|subcommand| { -// let mut subcommand = subcommand.clone(); -// let mut cli_handler = PathSegment { -// ident: Ident::new("cli_handler", Span::call_site()), -// arguments: std::mem::replace( -// &mut subcommand.segments.last_mut().unwrap().arguments, -// PathArguments::None, -// ), -// }; -// cli_handler.arguments = match cli_handler.arguments { -// PathArguments::None => PathArguments::AngleBracketed( -// syn::parse2(quote! { :: }) -// .unwrap(), -// ), -// PathArguments::AngleBracketed(mut a) => { -// a.args -// .push(syn::parse2(quote! { Params#param_ty_generics }).unwrap()); -// a.args.push(syn::parse2(quote! { GenericContext }).unwrap()); -// PathArguments::AngleBracketed(a) -// } -// _ => unreachable!(), -// }; -// quote_spanned! { subcommand.span() => -// Some((#subcommand::NAME, sub_m)) => { -// let method = if method.is_empty() { -// #subcommand::NAME.into() -// } else { -// method + "." + #subcommand::NAME -// }; -// #subcommand::#cli_handler(ctx, parent_data, rt, sub_m, method, params) -// }, -// } -// }); -// let self_impl = match (self_impl, &common.exec_ctx) { -// (Some(self_impl), ExecutionContext::CliOnly(_)) -// | (Some(self_impl), ExecutionContext::Local(_)) -// | (Some(self_impl), ExecutionContext::CustomCli { .. }) => { -// let (self_impl_fn, is_async) = -// if let ExecutionContext::CustomCli { cli, is_async, .. } = &common.exec_ctx -// { -// (cli, *is_async) -// } else { -// (&self_impl.path, self_impl.is_async) -// }; -// let create_rt = if common.is_async { -// None -// } else { -// Some(create_rt) -// }; -// let self_impl = if is_async { -// quote_spanned! { self_impl_fn.span() => -// #create_rt -// rt_ref.block_on(#self_impl_fn(Into::into(ctx), parent_data))? -// } -// } else { -// quote_spanned! { self_impl_fn.span() => -// #self_impl_fn(Into::into(ctx), parent_data)? -// } -// }; -// quote! { -// Ok(#display(#self_impl, matches)), -// } -// } -// (Some(self_impl), ExecutionContext::Standard) => { -// let self_impl_fn = &self_impl.path; -// let self_impl = if self_impl.is_async { -// quote! { -// rt_ref.block_on(#self_impl_fn(unreachable!(), parent_data)) -// } -// } else { -// quote! { -// #self_impl_fn(unreachable!(), parent_data) -// } -// }; -// let create_rt = if common.is_async { -// None -// } else { -// Some(create_rt) -// }; -// quote! { -// { -// #create_rt - -// #[allow(unreachable_code)] -// let return_ty = if true { -// ::rpc_toolkit::command_helpers::prelude::PhantomData -// } else { -// ::rpc_toolkit::command_helpers::prelude::make_phantom(#self_impl?) -// }; - -// let res = rt_ref.block_on(::rpc_toolkit::command_helpers::prelude::call_remote(ctx, method.as_ref(), params, return_ty))?; -// Ok(#display(res.result?, matches)) -// } -// } -// } -// (None, _) | (Some(_), ExecutionContext::RpcOnly(_)) => quote! { -// Err(::rpc_toolkit::command_helpers::prelude::RpcError { -// data: Some(method.into()), -// ..::rpc_toolkit::command_helpers::prelude::yajrc::METHOD_NOT_FOUND_ERROR -// }), -// }, -// }; -// quote! { -// pub fn cli_handler#generics( -// ctx: GenericContext, -// parent_data: #parent_data_ty, -// mut rt: Option<::rpc_toolkit::command_helpers::prelude::Runtime>, -// matches: &::rpc_toolkit::command_helpers::prelude::ArgMatches, -// method: ::rpc_toolkit::command_helpers::prelude::Cow<'_, str>, -// parent_params: ParentParams, -// ) -> Result<(), ::rpc_toolkit::command_helpers::prelude::RpcError> { -// #param_struct_def - -// #cmd_preprocess - -// match matches.subcommand() { -// #( -// #subcmd_impl -// )* -// _ => #self_impl -// } -// } -// } -// } -// } -// } - -fn build_params(params: Vec, generics: &Generics) -> (TokenStream, Generics) { - let mut param_generics_filter = GenericFilter::new(generics); - for param in ¶ms { - param_generics_filter.fold_type(param.ty.clone()); - } - let param_generics = param_generics_filter.finish(); - let (impl_generics, ty_generics, where_clause) = param_generics.split_for_impl(); - let param_arg = params.iter().enumerate().map(|(idx, p)| { - let mut res = TokenStream::new(); - let p_ty = &p.ty; - if let Some(rename) = &p.rename { - res.extend(quote! { #[serde(rename = #rename)] }); - } else if let Some(name) = &p.name { - let name = LitStr::new(&name.to_string(), name.span()); - res.extend(quote! { #[serde(rename = #name)] }); - }; - if let Some(default) = &p.default { - res.extend(quote! { #[serde(#default)] }); - } - let arg_ident = Ident::new(&format!("arg_{idx}"), Span::call_site()); - let p_name = p.name.as_ref().unwrap_or(&arg_ident); - res.extend(quote! { pub #p_name: #p_ty, }); - res - }); - let (clap_param_arg, clap_param_from_matches): (Vec<_>, Vec<_>) = params - .iter() - .enumerate() - .map(|(idx, p)| { - let (mut arg, mut from_matches) = (TokenStream::new(), TokenStream::new()); - let arg_ident = Ident::new(&format!("arg_{idx}"), Span::call_site()); - let p_name = p - .name - .as_ref() - .unwrap_or(&arg_ident); - if p.stdin.is_some() { - let parser = p - .parse.as_ref() - .map(|p| quote! { #p }) - .unwrap_or(quote!(::rpc_toolkit::command_helpers::default_stdin_parser)); - from_matches.extend(quote! { #p_name: #parser(&mut std::io::stdin(), matches)? }); - } else if matches!(&p.ty, Type::Path(p) if p.path.is_ident("bool")) { - arg.extend(if p.clap_attr.is_empty() { - quote! { #[arg] } - } else { - let clap_attr = &p.clap_attr; - quote! { #[arg(#(#clap_attr),*)] } - }); - arg.extend(quote! { #p_name: bool, }); - from_matches.extend(quote! { #p_name: clap_args.#p_name, }); - } else if matches!(&p.ty, Type::Path(p) if p.path.segments.first().unwrap().ident == "Option") { - let parser = p - .parse.as_ref() - .map(|p| quote!(#p)) - .unwrap_or(quote!(::rpc_toolkit::command_helpers::default_arg_parser)); - arg.extend(if p.clap_attr.is_empty() { - quote! { #[arg] } - } else { - let clap_attr = &p.clap_attr; - quote! { #[arg(#(#clap_attr),*)] } - }); - arg.extend(quote! { #p_name: Option, }); - from_matches.extend(quote! { #p_name: clap_args.#p_name.as_ref().map(|arg_str| #parser(arg_str, matches)).transpose()?, }); - } else { - let parser = p - .parse.as_ref() - .map(|p| quote!(#p)) - .unwrap_or(quote!(::rpc_toolkit::command_helpers::default_arg_parser)); - arg.extend(if p.clap_attr.is_empty() { - quote! { #[arg] } - } else { - let clap_attr = &p.clap_attr; - quote! { #[arg(#(#clap_attr),*)] } - }); - arg.extend(quote! { #p_name: String, }); - from_matches.extend(quote! { #p_name: #parser(&clap_args.#p_name, matches)?, }); - } - (arg, from_matches) - }) - .unzip(); - ( - quote! { - #[derive(::rpc_toolkit::serde::Serialize, ::rpc_toolkit::serde::Deserialize)] - pub struct Params #ty_generics { - #( - #param_arg - )* - } - #[derive(::rpc_toolkit::clap::Parser)] - struct ClapParams { - #( - #clap_param_arg - )* - } - impl #impl_generics ::rpc_toolkit::command_helpers::clap::FromArgMatches for Params #ty_generics #where_clause { - fn from_arg_matches(matches: &::rpc_toolkit::command_helpers::clap::ArgMatches) -> Result { - let clap_args = ClapParams::from_arg_matches(matches)?; - Ok(Self { - #( - #clap_param_from_matches - )* - }) - } - fn update_from_arg_matches(&mut self, matches: &::rpc_toolkit::command_helpers::clap::ArgMatches) -> Result<(), ::rpc_toolkit::command_helpers::clap::Error> { - unimplemented!() - } - } - impl #impl_generics ::rpc_toolkit::command_helpers::clap::CommandFactory for Params #ty_generics #where_clause { - fn command() -> ::rpc_toolkit::command_helpers::clap::Command { - ClapParams::command() - } - fn command_for_update() -> ::rpc_toolkit::command_helpers::clap::Command { - ClapParams::command_for_update() - } - } - }, - param_generics, - ) -} - -fn build_inherited(parent_data: Option, generics: &Generics) -> (TokenStream, Generics) { - let mut inherited_generics_filter = GenericFilter::new(generics); - if let Some(inherited) = &parent_data { - inherited_generics_filter.fold_type(inherited.clone()); - } - let inherited_generics = inherited_generics_filter.finish(); - if let Some(inherited) = parent_data { - let (_, ty_generics, _) = inherited_generics.split_for_impl(); - ( - quote! { - type InheritedParams #ty_generics = #inherited; - }, - inherited_generics, - ) - } else { - ( - quote! { type InheritedParams = ::rpc_toolkit::Empty; }, - inherited_generics, - ) - } -} - -pub fn build(args: AttributeArgs, mut item: ItemFn) -> TokenStream { - let params = macro_try!(parse_param_attrs(&mut item)); - let mut opt = macro_try!(parse_command_attr(args)); - let fn_vis = &item.vis; - let fn_name = &item.sig.ident; - let (params_impl, params_generics) = build_params( - params - .iter() - .filter_map(|a| { - if let ParamType::Arg(a) = a { - Some(a.clone()) - } else { - None - } - }) - .collect(), - &item.sig.generics, - ); - let (_, params_ty_generics, _) = params_generics.split_for_impl(); - let (inherited_impl, inherited_generics) = build_inherited( - params.iter().find_map(|a| { - if let ParamType::ParentData(a) = a { - Some(a.clone()) - } else { - None - } - }), - &item.sig.generics, - ); - let (_, inherited_ty_generics, _) = inherited_generics.split_for_impl(); - let mut params_and_inherited_generics_filter = GenericFilter::new(&item.sig.generics); - params_and_inherited_generics_filter.fold_generics(params_generics.clone()); - params_and_inherited_generics_filter.fold_generics(inherited_generics.clone()); - let params_and_inherited_generics = params_and_inherited_generics_filter.finish(); - let (_, params_and_inherited_ty_generics, _) = params_and_inherited_generics.split_for_impl(); - let phantom_ty = params_and_inherited_generics - .type_params() - .map(|t| &t.ident) - .map(|t| quote! { #t }) - .chain( - params_and_inherited_generics - .lifetimes() - .map(|l| &l.lifetime) - .map(|l| quote! { &#l () }), - ); - let phantom_ty = quote! { ( #( #phantom_ty, )* ) }; - let module = if let Options::Parent(parent) = &opt { - // let subcommands = parent.subcommands.iter().map(|c| quote! { .subcommand_with_inherited_remote_cli(c::handler(), |a, b| ) }) - // quote! { - // pub type Handler #params_and_inherited_ty_generics = ::rpc_toolkit::ParentHandler; - // #params_impl - // #inherited_impl - // pub fn handler #params_and_inherited_ty_generics () -> Handler #params_and_inherited_ty_generics { - // Handler::new() - // #( - // #subcommands - // )* - // } - // } - Error::new(Span::call_site(), "derived parent handlers not implemented").to_compile_error() - } else { - let (ok_ty, err_ty) = match &item.sig.output { - ReturnType::Type(_, p) => match &**p { - Type::Path(p) if p.path.segments.last().unwrap().ident == "Result" => { - match &p.path.segments.last().unwrap().arguments { - PathArguments::AngleBracketed(a) if a.args.len() == 2 => { - match (a.args.first(), a.args.last()) { - ( - Some(GenericArgument::Type(ok)), - Some(GenericArgument::Type(err)), - ) => (ok, err), - _ => { - return Error::new(a.span(), "return type must be a Result") - .to_compile_error() - } - } - } - a => { - return Error::new(a.span(), "return type must be a Result") - .to_compile_error() - } - } - } - a => { - return Error::new(a.span(), "return type must be a Result").to_compile_error(); - } - }, - a => return Error::new(a.span(), "return type must be a Result").to_compile_error(), - }; - let handler_impl: TokenStream = todo!(); - let cli_bindings_impl: TokenStream = todo!(); - quote! { - pub struct Handler #params_and_inherited_ty_generics (::core::marker::PhantomData<#phantom_ty>); - impl #params_and_inherited_ty_generics ::core::fmt::Debug for Handler #params_and_inherited_ty_generics { - fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { - f.debug_tuple("Handler") - .finish() - } - } - impl #params_and_inherited_ty_generics ::core::clone::Clone for Handler #params_and_inherited_ty_generics { - fn clone(&self) -> Self { - Self(::core::marker::PhantomData) - } - } - impl #params_and_inherited_ty_generics ::core::marker::Copy for Handler #params_and_inherited_ty_generics { } - #params_impl - #inherited_impl - impl #params_and_inherited_ty_generics ::rpc_toolkit::HandlerTypes for Handler #params_and_inherited_ty_generics { - type Params = Params; - type InheritedParams = InheritedParams; - type Ok = #ok_ty; - type Err = #err_ty; - } - #handler_impl - #cli_bindings_impl - pub fn handler #params_and_inherited_ty_generics () -> Handler #params_and_inherited_ty_generics { - Handler(::core::marker::PhantomData) - } - } - }; - - let fn_rename = opt - .common() - .rename - .clone() - .unwrap_or(LitStr::new(&fn_name.to_string(), fn_name.span())); - - let res = quote! { - #item - #fn_vis mod #fn_name { - use super::*; - pub const NAME: &str = #fn_rename; - #module - } - }; - if let Some(debug) = &opt.common().macro_debug { - Error::new(debug.span(), res).into_compile_error() - } else { - res - } -} diff --git a/rpc-toolkit-macro-internals/src/command/mod.rs b/rpc-toolkit-macro-internals/src/command/mod.rs deleted file mode 100644 index 662064a..0000000 --- a/rpc-toolkit-macro-internals/src/command/mod.rs +++ /dev/null @@ -1,102 +0,0 @@ -use std::collections::HashMap; - -use syn::*; - -pub mod build; -mod parse; - -pub enum ExecutionContext { - Standard, - CliOnly(Path), - RpcOnly(Path), - Local(Path), - CustomCli { - custom: Path, - cli: Path, - context: Type, - is_async: bool, - }, -} -impl Default for ExecutionContext { - fn default() -> Self { - ExecutionContext::Standard - } -} - -#[derive(Default)] -pub struct LeafOptions { - macro_debug: Option, - blocking: Option, - is_async: bool, - aliases: Vec, - about: Option, - rename: Option, - exec_ctx: ExecutionContext, - display: Option, - metadata: HashMap, - clap_attr: Vec, -} - -pub struct SelfImplInfo { - path: Path, - context: Type, - is_async: bool, - blocking: bool, -} -pub struct ParentOptions { - common: LeafOptions, - subcommands: Vec, - self_impl: Option, -} -impl From for ParentOptions { - fn from(opt: LeafOptions) -> Self { - ParentOptions { - common: opt, - subcommands: Default::default(), - self_impl: Default::default(), - } - } -} - -pub enum Options { - Leaf(LeafOptions), - Parent(ParentOptions), -} -impl Options { - fn to_parent(&mut self) -> Result<&mut ParentOptions> { - if let Options::Leaf(opt) = self { - *self = Options::Parent(std::mem::replace(opt, Default::default()).into()); - } - Ok(match self { - Options::Parent(a) => a, - _ => unreachable!(), - }) - } - fn common(&mut self) -> &mut LeafOptions { - match self { - Options::Leaf(ref mut opt) => opt, - Options::Parent(opt) => &mut opt.common, - } - } -} - -#[derive(Clone)] -pub struct ArgOptions { - ty: Type, - name: Option, - rename: Option, - parse: Option, - stdin: Option, - default: Option, - clap_attr: Vec, -} - -#[derive(Clone)] -pub enum ParamType { - None, - Arg(ArgOptions), - Context(Type), - ParentData(Type), - Request, - Response, -} diff --git a/rpc-toolkit-macro-internals/src/command/parse.rs b/rpc-toolkit-macro-internals/src/command/parse.rs deleted file mode 100644 index c504b7d..0000000 --- a/rpc-toolkit-macro-internals/src/command/parse.rs +++ /dev/null @@ -1,851 +0,0 @@ -use syn::spanned::Spanned; - -use super::*; - -pub fn parse_command_attr(args: AttributeArgs) -> Result { - let mut opt = Options::Leaf(Default::default()); - for arg in args { - match arg { - NestedMeta::Meta(Meta::Path(p)) if p.is_ident("macro_debug") => { - opt.common().macro_debug = Some(p); - } - NestedMeta::Meta(Meta::List(list)) if list.path.is_ident("subcommands") => { - let inner = opt.to_parent()?; - if !inner.subcommands.is_empty() { - return Err(Error::new(list.span(), "duplicate argument `subcommands`")); - } - for subcmd in list.nested { - match subcmd { - NestedMeta::Meta(Meta::Path(subcmd)) => inner.subcommands.push(subcmd), - NestedMeta::Lit(Lit::Str(s)) => { - inner.subcommands.push(syn::parse_str(&s.value())?) - } - NestedMeta::Meta(Meta::List(mut self_impl)) - if self_impl.path.is_ident("self") => - { - if self_impl.nested.len() == 1 { - match self_impl.nested.pop().unwrap().into_value() { - NestedMeta::Meta(Meta::Path(self_impl)) => { - if inner.self_impl.is_some() { - return Err(Error::new( - self_impl.span(), - "duplicate argument `self`", - )); - } - inner.self_impl = Some(SelfImplInfo { - path: self_impl, - context: parse2(quote::quote! { () }).unwrap(), - is_async: false, - blocking: false, - }) - } - NestedMeta::Meta(Meta::List(l)) => { - if inner.self_impl.is_some() { - return Err(Error::new( - self_impl.span(), - "duplicate argument `self`", - )); - } - let mut is_async = false; - let mut blocking = false; - let mut context: Type = - parse2(quote::quote! { () }).unwrap(); - for meta in &l.nested { - match meta { - NestedMeta::Meta(Meta::Path(p)) - if p.is_ident("async") => - { - is_async = true; - if blocking { - return Err(Error::new( - p.span(), - "`async` and `blocking` are mutually exclusive", - )); - } - } - NestedMeta::Meta(Meta::Path(p)) - if p.is_ident("blocking") => - { - blocking = true; - if is_async { - return Err(Error::new( - p.span(), - "`async` and `blocking` are mutually exclusive", - )); - } - } - NestedMeta::Meta(Meta::List(p)) - if p.path.is_ident("context") => - { - context = if let Some(NestedMeta::Meta( - Meta::Path(context), - )) = p.nested.first() - { - Type::Path(TypePath { - path: context.clone(), - qself: None, - }) - } else { - return Err(Error::new( - p.span(), - "`context` requires a path argument", - )); - } - } - arg => { - return Err(Error::new( - arg.span(), - "unknown argument", - )) - } - } - } - inner.self_impl = Some(SelfImplInfo { - path: l.path, - context, - is_async, - blocking, - }) - } - a => { - return Err(Error::new( - a.span(), - "`self` implementation must be a path, or a list", - )) - } - } - } else { - return Err(Error::new( - self_impl.nested.span(), - "`self` can only have one implementation", - )); - } - } - arg => { - return Err(Error::new(arg.span(), "unknown argument to `subcommands`")) - } - } - } - if inner.subcommands.is_empty() { - return Err(Error::new( - list.path.span(), - "`subcommands` requires at least 1 argument", - )); - } - } - NestedMeta::Meta(Meta::Path(p)) if p.is_ident("subcommands") => { - return Err(Error::new( - p.span(), - "`subcommands` requires at least 1 argument", - )); - } - NestedMeta::Meta(Meta::NameValue(nv)) if nv.path.is_ident("subcommands") => { - return Err(Error::new( - nv.path.span(), - "`subcommands` cannot be assigned to", - )); - } - NestedMeta::Meta(Meta::List(list)) if list.path.is_ident("display") => { - if list.nested.len() == 1 { - match &list.nested[0] { - NestedMeta::Meta(Meta::Path(display_impl)) => { - if opt.common().display.is_some() { - return Err(Error::new( - display_impl.span(), - "duplicate argument `display`", - )); - } - opt.common().display = Some(display_impl.clone()) - } - a => { - return Err(Error::new( - a.span(), - "`display` implementation must be a path", - )) - } - } - } else { - return Err(Error::new( - list.nested.span(), - "`display` can only have one implementation", - )); - } - } - NestedMeta::Meta(Meta::Path(p)) if p.is_ident("display") => { - return Err(Error::new(p.span(), "`display` requires an argument")); - } - NestedMeta::Meta(Meta::NameValue(nv)) if nv.path.is_ident("display") => { - return Err(Error::new( - nv.path.span(), - "`display` cannot be assigned to", - )); - } - NestedMeta::Meta(Meta::List(list)) if list.path.is_ident("custom_cli") => { - if list.nested.len() == 1 { - match &opt.common().exec_ctx { - ExecutionContext::Standard => match &list.nested[0] { - NestedMeta::Meta(Meta::Path(custom_cli_impl)) => { - opt.common().exec_ctx = ExecutionContext::CustomCli { - custom: list.path, - context: parse2(quote::quote!{ () }).unwrap(), - cli: custom_cli_impl.clone(), - is_async: false, - } - } - NestedMeta::Meta(Meta::List(custom_cli_impl)) => { - let mut is_async = false; - let mut context: Type = parse2(quote::quote! { () }).unwrap(); - for meta in &custom_cli_impl.nested { - match meta { - NestedMeta::Meta(Meta::Path(p)) if p.is_ident("async") => is_async = true, - NestedMeta::Meta(Meta::List(p)) if p.path.is_ident("context") => { - context = if let Some(NestedMeta::Meta(Meta::Path(context))) = p.nested.first() { - Type::Path(TypePath { - path: context.clone(), - qself: None, - }) - } else { - return Err(Error::new(p.span(), "`context` requires a path argument")); - } - } - arg => return Err(Error::new(arg.span(), "unknown argument")), - } - } - opt.common().exec_ctx = ExecutionContext::CustomCli { - custom: list.path, - context, - cli: custom_cli_impl.path.clone(), - is_async, - }; - } - a => { - return Err(Error::new( - a.span(), - "`custom_cli` implementation must be a path, or a list with 1 argument", - )) - } - }, - ExecutionContext::CliOnly(_) => { - return Err(Error::new(list.span(), "duplicate argument: `cli_only`")) - } - ExecutionContext::RpcOnly(_) => { - return Err(Error::new( - list.span(), - "`cli_only` and `rpc_only` are mutually exclusive", - )) - } - ExecutionContext::Local(_) => { - return Err(Error::new( - list.span(), - "`cli_only` and `local` are mutually exclusive", - )) - } - ExecutionContext::CustomCli { .. } => { - return Err(Error::new( - list.span(), - "`cli_only` and `custom_cli` are mutually exclusive", - )) - } - } - } else { - return Err(Error::new( - list.nested.span(), - "`custom_cli` can only have one implementation", - )); - } - } - NestedMeta::Meta(Meta::Path(p)) if p.is_ident("custom_cli") => { - return Err(Error::new(p.span(), "`custom_cli` requires an argument")); - } - NestedMeta::Meta(Meta::NameValue(nv)) if nv.path.is_ident("custom_cli") => { - return Err(Error::new( - nv.path.span(), - "`custom_cli` cannot be assigned to", - )); - } - NestedMeta::Meta(Meta::List(list)) if list.path.is_ident("aliases") => { - if !opt.common().aliases.is_empty() { - return Err(Error::new(list.span(), "duplicate argument `alias`")); - } - for nested in list.nested { - match nested { - NestedMeta::Lit(Lit::Str(alias)) => opt.common().aliases.push(alias), - a => return Err(Error::new(a.span(), "`alias` must be a string")), - } - } - } - NestedMeta::Meta(Meta::Path(p)) if p.is_ident("alias") => { - return Err(Error::new(p.span(), "`alias` requires an argument")); - } - NestedMeta::Meta(Meta::NameValue(nv)) if nv.path.is_ident("alias") => { - if !opt.common().aliases.is_empty() { - return Err(Error::new(nv.path.span(), "duplicate argument `alias`")); - } - if let Lit::Str(alias) = nv.lit { - opt.common().aliases.push(alias); - } else { - return Err(Error::new(nv.lit.span(), "`alias` must be a string")); - } - } - NestedMeta::Meta(Meta::Path(p)) if p.is_ident("cli_only") => { - match &opt.common().exec_ctx { - ExecutionContext::Standard => { - opt.common().exec_ctx = ExecutionContext::CliOnly(p) - } - ExecutionContext::CliOnly(_) => { - return Err(Error::new(p.span(), "duplicate argument: `cli_only`")) - } - ExecutionContext::RpcOnly(_) => { - return Err(Error::new( - p.span(), - "`cli_only` and `rpc_only` are mutually exclusive", - )) - } - ExecutionContext::Local(_) => { - return Err(Error::new( - p.span(), - "`cli_only` and `local` are mutually exclusive", - )) - } - ExecutionContext::CustomCli { .. } => { - return Err(Error::new( - p.span(), - "`cli_only` and `custom_cli` are mutually exclusive", - )) - } - } - } - NestedMeta::Meta(Meta::List(list)) if list.path.is_ident("cli_only") => { - return Err(Error::new( - list.path.span(), - "`cli_only` does not take any arguments", - )); - } - NestedMeta::Meta(Meta::NameValue(nv)) if nv.path.is_ident("cli_only") => { - return Err(Error::new( - nv.path.span(), - "`cli_only` cannot be assigned to", - )); - } - NestedMeta::Meta(Meta::Path(p)) if p.is_ident("rpc_only") => { - match &opt.common().exec_ctx { - ExecutionContext::Standard => { - opt.common().exec_ctx = ExecutionContext::RpcOnly(p) - } - ExecutionContext::RpcOnly(_) => { - return Err(Error::new(p.span(), "duplicate argument: `rpc_only`")) - } - ExecutionContext::CliOnly(_) => { - return Err(Error::new( - p.span(), - "`rpc_only` and `cli_only` are mutually exclusive", - )) - } - ExecutionContext::Local(_) => { - return Err(Error::new( - p.span(), - "`rpc_only` and `local` are mutually exclusive", - )) - } - ExecutionContext::CustomCli { .. } => { - return Err(Error::new( - p.span(), - "`rpc_only` and `custom_cli` are mutually exclusive", - )) - } - } - } - NestedMeta::Meta(Meta::List(list)) if list.path.is_ident("rpc_only") => { - return Err(Error::new( - list.path.span(), - "`rpc_only` does not take any arguments", - )); - } - NestedMeta::Meta(Meta::NameValue(nv)) if nv.path.is_ident("rpc_only") => { - return Err(Error::new( - nv.path.span(), - "`rpc_only` cannot be assigned to", - )); - } - NestedMeta::Meta(Meta::Path(p)) if p.is_ident("local") => { - match &opt.common().exec_ctx { - ExecutionContext::Standard => { - opt.common().exec_ctx = ExecutionContext::Local(p) - } - ExecutionContext::Local(_) => { - return Err(Error::new(p.span(), "duplicate argument: `local`")) - } - ExecutionContext::RpcOnly(_) => { - return Err(Error::new( - p.span(), - "`local` and `rpc_only` are mutually exclusive", - )) - } - ExecutionContext::CliOnly(_) => { - return Err(Error::new( - p.span(), - "`local` and `cli_only` are mutually exclusive", - )) - } - ExecutionContext::CustomCli { .. } => { - return Err(Error::new( - p.span(), - "`local` and `custom_cli` are mutually exclusive", - )) - } - } - } - NestedMeta::Meta(Meta::List(list)) if list.path.is_ident("local") => { - return Err(Error::new( - list.path.span(), - "`local` does not take any arguments", - )); - } - NestedMeta::Meta(Meta::NameValue(nv)) if nv.path.is_ident("local") => { - return Err(Error::new(nv.path.span(), "`local` cannot be assigned to")); - } - NestedMeta::Meta(Meta::Path(p)) if p.is_ident("blocking") => { - if opt.common().blocking.is_some() { - return Err(Error::new(p.span(), "duplicate argument `blocking`")); - } - opt.common().blocking = Some(p); - } - NestedMeta::Meta(Meta::List(list)) if list.path.is_ident("blocking") => { - return Err(Error::new( - list.path.span(), - "`blocking` does not take any arguments", - )); - } - NestedMeta::Meta(Meta::NameValue(nv)) if nv.path.is_ident("blocking") => { - return Err(Error::new( - nv.path.span(), - "`blocking` cannot be assigned to", - )); - } - NestedMeta::Meta(Meta::NameValue(nv)) if nv.path.is_ident("about") => { - if let Lit::Str(about) = nv.lit { - if opt.common().about.is_some() { - return Err(Error::new(about.span(), "duplicate argument `about`")); - } - opt.common().about = Some(about); - } else { - return Err(Error::new(nv.lit.span(), "about message must be a string")); - } - } - NestedMeta::Meta(Meta::List(list)) if list.path.is_ident("about") => { - return Err(Error::new( - list.path.span(), - "`about` does not take any arguments", - )); - } - NestedMeta::Meta(Meta::Path(p)) if p.is_ident("about") => { - return Err(Error::new(p.span(), "`about` must be assigned to")); - } - NestedMeta::Meta(Meta::NameValue(nv)) if nv.path.is_ident("rename") => { - if let Lit::Str(rename) = nv.lit { - if opt.common().rename.is_some() { - return Err(Error::new(rename.span(), "duplicate argument `rename`")); - } - opt.common().rename = Some(rename); - } else { - return Err(Error::new(nv.lit.span(), "`rename` must be a string")); - } - } - NestedMeta::Meta(Meta::List(list)) if list.path.is_ident("rename") => { - return Err(Error::new( - list.path.span(), - "`rename` does not take any arguments", - )); - } - NestedMeta::Meta(Meta::Path(p)) if p.is_ident("rename") => { - return Err(Error::new(p.span(), "`rename` must be assigned to")); - } - NestedMeta::Meta(Meta::List(list)) if list.path.is_ident("metadata") => { - for meta in list.nested { - match meta { - NestedMeta::Meta(Meta::NameValue(metadata_pair)) => { - let ident = metadata_pair.path.get_ident().ok_or(Error::new( - metadata_pair.path.span(), - "must be an identifier", - ))?; - if opt - .common() - .metadata - .insert(ident.clone(), metadata_pair.lit) - .is_some() - { - return Err(Error::new( - ident.span(), - format!("duplicate metadata `{}`", ident), - )); - } - } - a => { - return Err(Error::new( - a.span(), - "`metadata` takes a list of identifiers to be assigned to", - )) - } - } - } - } - NestedMeta::Meta(Meta::Path(p)) if p.is_ident("metadata") => { - return Err(Error::new( - p.span(), - "`metadata` takes a list of identifiers to be assigned to", - )); - } - NestedMeta::Meta(Meta::NameValue(nv)) if nv.path.is_ident("metadata") => { - return Err(Error::new( - nv.path.span(), - "`metadata` cannot be assigned to", - )); - } - _ => { - return Err(Error::new(arg.span(), "unknown argument")); - } - } - } - if let Options::Parent(opt) = &opt { - if opt.self_impl.is_none() { - if let Some(display) = &opt.common.display { - return Err(Error::new( - display.span(), - "cannot define `display` for a command without an implementation", - )); - } - match &opt.common.exec_ctx { - ExecutionContext::CliOnly(cli_only) => { - return Err(Error::new( - cli_only.span(), - "cannot define `cli_only` for a command without an implementation", - )) - } - ExecutionContext::RpcOnly(rpc_only) => { - return Err(Error::new( - rpc_only.span(), - "cannot define `rpc_only` for a command without an implementation", - )) - } - _ => (), - } - } - } - Ok(opt) -} - -pub fn parse_arg_attr(attr: Attribute, arg: PatType) -> Result { - let arg_span = arg.span(); - let meta = attr.parse_meta()?; - let mut opt = ArgOptions { - ty: *arg.ty, - name: match *arg.pat { - Pat::Ident(i) => Some(i.ident), - _ => None, - }, - rename: None, - parse: None, - stdin: None, - default: None, - clap_attr: Vec::new(), - }; - match meta { - Meta::List(list) => { - for arg in list.nested { - match arg { - NestedMeta::Meta(Meta::List(mut list)) if list.path.is_ident("parse") => { - if list.nested.len() == 1 { - match list.nested.pop().unwrap().into_value() { - NestedMeta::Meta(Meta::Path(parse_impl)) => { - if opt.parse.is_some() { - return Err(Error::new( - list.span(), - "duplicate argument `parse`", - )); - } - opt.parse = Some(parse_impl) - } - a => { - return Err(Error::new( - a.span(), - "`parse` implementation must be a path", - )) - } - } - } else { - return Err(Error::new( - list.nested.span(), - "`parse` can only have one implementation", - )); - } - } - NestedMeta::Meta(Meta::Path(p)) if p.is_ident("parse") => { - return Err(Error::new(p.span(), "`parse` requires an argument")); - } - NestedMeta::Meta(Meta::NameValue(nv)) if nv.path.is_ident("parse") => { - return Err(Error::new(nv.path.span(), "`parse` cannot be assigned to")); - } - NestedMeta::Meta(Meta::Path(p)) if p.is_ident("stdin") => { - if !opt.clap_attr.is_empty() { - return Err(Error::new( - p.span(), - "`stdin` and clap parser attributes are mutually exclusive", - )); - } - opt.stdin = Some(p); - } - NestedMeta::Meta(Meta::List(list)) if list.path.is_ident("stdin") => { - return Err(Error::new( - list.path.span(), - "`stdin` does not take any arguments", - )); - } - NestedMeta::Meta(Meta::NameValue(nv)) if nv.path.is_ident("stdin") => { - return Err(Error::new(nv.path.span(), "`stdin` cannot be assigned to")); - } - NestedMeta::Meta(Meta::NameValue(nv)) if nv.path.is_ident("rename") => { - if let Lit::Str(rename) = nv.lit { - if opt.rename.is_some() { - return Err(Error::new( - rename.span(), - "duplicate argument `rename`", - )); - } - opt.clap_attr - .push(NestedMeta::Meta(Meta::NameValue(MetaNameValue { - path: Path::from(Ident::new("name", nv.path.span())), - eq_token: nv.eq_token, - lit: Lit::Str(rename.clone()), - }))); - opt.rename = Some(rename); - } else { - return Err(Error::new(nv.lit.span(), "`rename` must be a string")); - } - } - NestedMeta::Meta(Meta::List(list)) if list.path.is_ident("rename") => { - return Err(Error::new( - list.path.span(), - "`rename` does not take any arguments", - )); - } - NestedMeta::Meta(Meta::Path(p)) if p.is_ident("rename") => { - return Err(Error::new(p.span(), "`rename` must be assigned to")); - } - NestedMeta::Meta(Meta::NameValue(nv)) if nv.path.is_ident("default") => { - return Err(Error::new(nv.lit.span(), "`default` cannot be assigned to")); - } - NestedMeta::Meta(Meta::List(list)) if list.path.is_ident("default") => { - return Err(Error::new( - list.path.span(), - "`default` does not take any arguments", - )); - } - NestedMeta::Meta(Meta::Path(p)) if p.is_ident("default") => { - opt.clap_attr - .push(NestedMeta::Meta(Meta::Path(Path::from(Ident::new( - "default_value_t", - p.span(), - ))))); - opt.default = Some(p); - } - unknown => { - if opt.stdin.is_some() { - return Err(Error::new( - unknown.span(), - "`stdin` and clap parser attributes are mutually exclusive", - )); - } - opt.clap_attr.push(unknown); - } - } - } - } - Meta::Path(_) => (), - Meta::NameValue(nv) => return Err(Error::new(nv.span(), "`arg` cannot be assigned to")), - } - if opt.name.is_none() { - return Err(Error::new( - arg_span, - "cannot infer name for pattern argument", - )); - } - Ok(opt) -} - -pub fn parse_param_attrs(item: &mut ItemFn) -> Result> { - let mut params = Vec::new(); - for param in item.sig.inputs.iter_mut() { - if let FnArg::Typed(param) = param { - let mut ty = ParamType::None; - let mut i = 0; - while i != param.attrs.len() { - if param.attrs[i].path.is_ident("arg") { - let attr = param.attrs.remove(i); - if matches!(ty, ParamType::None) { - ty = ParamType::Arg(parse_arg_attr(attr, param.clone())?); - } else if matches!(ty, ParamType::Arg(_)) { - return Err(Error::new( - attr.span(), - "`arg` attribute may only be specified once", - )); - } else if matches!(ty, ParamType::Context(_)) { - return Err(Error::new( - attr.span(), - "`arg` and `context` are mutually exclusive", - )); - } else if matches!(ty, ParamType::ParentData(_)) { - return Err(Error::new( - attr.span(), - "`arg` and `parent_data` are mutually exclusive", - )); - } else if matches!(ty, ParamType::Request) { - return Err(Error::new( - attr.span(), - "`arg` and `request` are mutually exclusive", - )); - } else if matches!(ty, ParamType::Response) { - return Err(Error::new( - attr.span(), - "`arg` and `response` are mutually exclusive", - )); - } - } else if param.attrs[i].path.is_ident("context") { - let attr = param.attrs.remove(i); - if matches!(ty, ParamType::None) { - ty = ParamType::Context(*param.ty.clone()); - } else if matches!(ty, ParamType::Context(_)) { - return Err(Error::new( - attr.span(), - "`context` attribute may only be specified once", - )); - } else if matches!(ty, ParamType::Arg(_)) { - return Err(Error::new( - attr.span(), - "`arg` and `context` are mutually exclusive", - )); - } else if matches!(ty, ParamType::ParentData(_)) { - return Err(Error::new( - attr.span(), - "`context` and `parent_data` are mutually exclusive", - )); - } else if matches!(ty, ParamType::Request) { - return Err(Error::new( - attr.span(), - "`context` and `request` are mutually exclusive", - )); - } else if matches!(ty, ParamType::Response) { - return Err(Error::new( - attr.span(), - "`context` and `response` are mutually exclusive", - )); - } - } else if param.attrs[i].path.is_ident("parent_data") { - let attr = param.attrs.remove(i); - if matches!(ty, ParamType::None) { - ty = ParamType::ParentData(*param.ty.clone()); - } else if matches!(ty, ParamType::ParentData(_)) { - return Err(Error::new( - attr.span(), - "`parent_data` attribute may only be specified once", - )); - } else if matches!(ty, ParamType::Arg(_)) { - return Err(Error::new( - attr.span(), - "`arg` and `parent_data` are mutually exclusive", - )); - } else if matches!(ty, ParamType::Context(_)) { - return Err(Error::new( - attr.span(), - "`context` and `parent_data` are mutually exclusive", - )); - } else if matches!(ty, ParamType::Request) { - return Err(Error::new( - attr.span(), - "`parent_data` and `request` are mutually exclusive", - )); - } else if matches!(ty, ParamType::Response) { - return Err(Error::new( - attr.span(), - "`parent_data` and `response` are mutually exclusive", - )); - } - } else if param.attrs[i].path.is_ident("request") { - let attr = param.attrs.remove(i); - if matches!(ty, ParamType::None) { - ty = ParamType::Request; - } else if matches!(ty, ParamType::Request) { - return Err(Error::new( - attr.span(), - "`request` attribute may only be specified once", - )); - } else if matches!(ty, ParamType::Arg(_)) { - return Err(Error::new( - attr.span(), - "`arg` and `request` are mutually exclusive", - )); - } else if matches!(ty, ParamType::Context(_)) { - return Err(Error::new( - attr.span(), - "`context` and `request` are mutually exclusive", - )); - } else if matches!(ty, ParamType::ParentData(_)) { - return Err(Error::new( - attr.span(), - "`parent_data` and `request` are mutually exclusive", - )); - } else if matches!(ty, ParamType::Response) { - return Err(Error::new( - attr.span(), - "`request` and `response` are mutually exclusive", - )); - } - } else if param.attrs[i].path.is_ident("response") { - let attr = param.attrs.remove(i); - if matches!(ty, ParamType::None) { - ty = ParamType::Response; - } else if matches!(ty, ParamType::Response) { - return Err(Error::new( - attr.span(), - "`response` attribute may only be specified once", - )); - } else if matches!(ty, ParamType::Arg(_)) { - return Err(Error::new( - attr.span(), - "`arg` and `response` are mutually exclusive", - )); - } else if matches!(ty, ParamType::Context(_)) { - return Err(Error::new( - attr.span(), - "`context` and `response` are mutually exclusive", - )); - } else if matches!(ty, ParamType::ParentData(_)) { - return Err(Error::new( - attr.span(), - "`parent_data` and `response` are mutually exclusive", - )); - } else if matches!(ty, ParamType::Request) { - return Err(Error::new( - attr.span(), - "`request` and `response` are mutually exclusive", - )); - } - } else { - i += 1; - } - } - if matches!(ty, ParamType::None) { - return Err(Error::new( - param.span(), - "must specify either `arg`, `context`, `parent_data`, `request`, or `response` attributes", - )); - } - params.push(ty) - } else { - return Err(Error::new( - param.span(), - "commands may not take `self` as an argument", - )); - } - } - Ok(params) -} diff --git a/rpc-toolkit-macro-internals/src/lib.rs b/rpc-toolkit-macro-internals/src/lib.rs deleted file mode 100644 index 2bbe0cb..0000000 --- a/rpc-toolkit-macro-internals/src/lib.rs +++ /dev/null @@ -1,12 +0,0 @@ -macro_rules! macro_try { - ($x:expr) => { - match $x { - Ok(a) => a, - Err(e) => return e.to_compile_error(), - } - }; -} - -mod command; - -pub use command::build::build as build_command; diff --git a/rpc-toolkit-macro/.gitignore b/rpc-toolkit-macro/.gitignore deleted file mode 100644 index 96ef6c0..0000000 --- a/rpc-toolkit-macro/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/target -Cargo.lock diff --git a/rpc-toolkit-macro/Cargo.toml b/rpc-toolkit-macro/Cargo.toml deleted file mode 100644 index 4be093c..0000000 --- a/rpc-toolkit-macro/Cargo.toml +++ /dev/null @@ -1,17 +0,0 @@ -[package] -authors = ["Aiden McClelland "] -edition = "2018" -name = "rpc-toolkit-macro" -version = "0.2.2" -description = "macros for rpc-toolkit" -license = "MIT" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[lib] -proc-macro = true - -[dependencies] -proc-macro2 = "1.0" -rpc-toolkit-macro-internals = { version = "=0.2.2", path = "../rpc-toolkit-macro-internals" } -syn = "1.0" diff --git a/rpc-toolkit-macro/src/lib.rs b/rpc-toolkit-macro/src/lib.rs deleted file mode 100644 index 63965e3..0000000 --- a/rpc-toolkit-macro/src/lib.rs +++ /dev/null @@ -1,44 +0,0 @@ -use proc_macro::{Span, TokenStream}; -use rpc_toolkit_macro_internals::*; - -#[proc_macro_attribute] -pub fn command(args: TokenStream, item: TokenStream) -> TokenStream { - let args = syn::parse_macro_input!(args as syn::AttributeArgs); - let item = syn::parse_macro_input!(item as syn::ItemFn); - build_command(args, item).into() -} - -/// `#[arg(...)]` -> Take this argument as a parameter -/// - `#[arg(help = "Help text")]` -> Set help text for the arg -/// - `#[arg(alias = "ls")]` -> Set the alias `ls` in the CLI -/// - `#[arg(aliases("show", "ls"))]` -> Set the aliases `ls` and `show` in the CLI -/// - `#[arg(rename = "new_name")]` -> Set the name of the arg to `new_name` in the RPC and CLI -/// - `#[arg(short = "a")]` -> Set the "short" representation of the arg to `-a` on the CLI -/// - `#[arg(long = "arg")]` -> Set the "long" representation of the arg to `--arg` on the CLI -/// - `#[arg(parse(custom_parse_fn))]` -> Use the function `custom_parse_fn` to parse the arg from the CLI -/// - `custom_parse_fn :: Into err => (&str, &ArgMatches) -> Result` -/// - note: `arg` is the type of the argument -/// - `#[arg(stdin)]` -> Parse the argument from stdin when using the CLI -/// - `custom_parse_fn :: Into err => (&[u8], &ArgMatches) -> Result` -/// - `#[arg(count)]` -> Treat argument as flag, count occurrences -/// - `#[arg(multiple)]` -> Allow the arg to be specified multiple times. Collect the args after parsing. -#[proc_macro_attribute] -pub fn arg(_: TokenStream, _: TokenStream) -> TokenStream { - syn::Error::new( - Span::call_site().into(), - "`arg` is only allowed on arguments of a function with the `command` attribute", - ) - .to_compile_error() - .into() -} - -/// - `#[context]` -> Passes the application context into this parameter -#[proc_macro_attribute] -pub fn context(_: TokenStream, _: TokenStream) -> TokenStream { - syn::Error::new( - Span::call_site().into(), - "`context` is only allowed on arguments of a function with the `command` attribute", - ) - .to_compile_error() - .into() -} diff --git a/rpc-toolkit/.gitignore b/rpc-toolkit/.gitignore deleted file mode 100644 index 96ef6c0..0000000 --- a/rpc-toolkit/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/target -Cargo.lock diff --git a/rpc-toolkit/Cargo.toml b/rpc-toolkit/Cargo.toml deleted file mode 100644 index 839835f..0000000 --- a/rpc-toolkit/Cargo.toml +++ /dev/null @@ -1,42 +0,0 @@ -[package] -authors = ["Aiden McClelland "] -edition = "2018" -name = "rpc-toolkit" -version = "0.2.3" -description = "A toolkit for creating JSON-RPC 2.0 servers with automatic cli bindings" -license = "MIT" -documentation = "https://docs.rs/rpc-toolkit" -keywords = ["json", "rpc", "cli"] -repository = "https://github.com/Start9Labs/rpc-toolkit" - - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html -[features] -cbor = ["serde_cbor"] -default = [] - -[dependencies] -axum = "0.7.3" -async-stream = "0.3" -async-trait = "0.1" -clap = { version = "4", features = ["derive"] } -futures = "0.3" -http = "1" -http-body-util = "0.1" -# hyper = { version = "1", features = ["server", "http1", "http2", "client"] } -itertools = "0.12" -imbl-value = { git = "https://github.com/Start9Labs/imbl-value.git" } -lazy_format = "2" -lazy_static = "1.4" -openssl = { version = "0.10", features = ["vendored"] } -pin-project = "1" -reqwest = { version = "0.11" } -rpc-toolkit-macro = { version = "0.2.2", path = "../rpc-toolkit-macro" } -serde = { version = "1.0", features = ["derive"] } -serde_cbor = { version = "0.11", optional = true } -serde_json = "1.0" -thiserror = "1.0" -tokio = { version = "1", features = ["full"] } -tokio-stream = { version = "0.1", features = ["io-util", "net"] } -url = "2" -yajrc = "0.1" diff --git a/rpc-toolkit/src/cli.rs b/src/cli.rs similarity index 100% rename from rpc-toolkit/src/cli.rs rename to src/cli.rs diff --git a/rpc-toolkit/src/command_helpers.rs b/src/command_helpers.rs similarity index 100% rename from rpc-toolkit/src/command_helpers.rs rename to src/command_helpers.rs diff --git a/rpc-toolkit/src/context.rs b/src/context.rs similarity index 100% rename from rpc-toolkit/src/context.rs rename to src/context.rs diff --git a/rpc-toolkit/src/handler/adapters.rs b/src/handler/adapters.rs similarity index 100% rename from rpc-toolkit/src/handler/adapters.rs rename to src/handler/adapters.rs diff --git a/rpc-toolkit/src/handler/from_fn.rs b/src/handler/from_fn.rs similarity index 100% rename from rpc-toolkit/src/handler/from_fn.rs rename to src/handler/from_fn.rs diff --git a/rpc-toolkit/src/handler/mod.rs b/src/handler/mod.rs similarity index 100% rename from rpc-toolkit/src/handler/mod.rs rename to src/handler/mod.rs diff --git a/rpc-toolkit/src/handler/parent.rs b/src/handler/parent.rs similarity index 100% rename from rpc-toolkit/src/handler/parent.rs rename to src/handler/parent.rs diff --git a/rpc-toolkit/src/lib.rs b/src/lib.rs similarity index 100% rename from rpc-toolkit/src/lib.rs rename to src/lib.rs diff --git a/rpc-toolkit/src/metadata.rs b/src/metadata.rs similarity index 100% rename from rpc-toolkit/src/metadata.rs rename to src/metadata.rs diff --git a/rpc-toolkit/src/server/http.rs b/src/server/http.rs similarity index 100% rename from rpc-toolkit/src/server/http.rs rename to src/server/http.rs diff --git a/rpc-toolkit/src/server/mod.rs b/src/server/mod.rs similarity index 100% rename from rpc-toolkit/src/server/mod.rs rename to src/server/mod.rs diff --git a/rpc-toolkit/src/server/socket.rs b/src/server/socket.rs similarity index 100% rename from rpc-toolkit/src/server/socket.rs rename to src/server/socket.rs diff --git a/rpc-toolkit/src/util.rs b/src/util.rs similarity index 100% rename from rpc-toolkit/src/util.rs rename to src/util.rs diff --git a/rpc-toolkit/tests/handler.rs b/tests/handler.rs similarity index 100% rename from rpc-toolkit/tests/handler.rs rename to tests/handler.rs diff --git a/rpc-toolkit/tests/test.rs b/tests/test.rs similarity index 100% rename from rpc-toolkit/tests/test.rs rename to tests/test.rs