From ee9b302ecd3c5846cf5b8621167208e8529ae332 Mon Sep 17 00:00:00 2001 From: Aiden McClelland Date: Tue, 31 Aug 2021 11:42:31 -0600 Subject: [PATCH] custom_cli --- .../src/command/build.rs | 126 +++++++++++++----- .../src/command/mod.rs | 5 + .../src/command/parse.rs | 85 ++++++++++++ 3 files changed, 180 insertions(+), 36 deletions(-) diff --git a/rpc-toolkit-macro-internals/src/command/build.rs b/rpc-toolkit-macro-internals/src/command/build.rs index 72c15c3..205e332 100644 --- a/rpc-toolkit-macro-internals/src/command/build.rs +++ b/rpc-toolkit-macro-internals/src/command/build.rs @@ -493,7 +493,14 @@ fn rpc_handler( method: &str, args: Params#param_ty_generics, ) -> Result<::rpc_toolkit::command_helpers::prelude::Value, ::rpc_toolkit::command_helpers::prelude::RpcError> { - Ok(::rpc_toolkit::command_helpers::prelude::to_value(#invocation)?) + 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 + }) + } } } } @@ -818,44 +825,91 @@ fn cli_handler( } } Options::Leaf(opt) => { - let invocation = if opt.is_async { + if let ExecutionContext::CustomCli { + ref cli, is_async, .. + } = opt.exec_ctx + { + let fn_path = cli; + let invocation = if 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 is_async { + create_rt + } else { + quote! { + drop(rt); + } + }; 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: #ctx_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 + pub fn cli_handler#generics( + ctx: #ctx_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 + #rt_action - Ok(#display_res) + 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: #ctx_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) + } } } } diff --git a/rpc-toolkit-macro-internals/src/command/mod.rs b/rpc-toolkit-macro-internals/src/command/mod.rs index 9417e6c..9b221a6 100644 --- a/rpc-toolkit-macro-internals/src/command/mod.rs +++ b/rpc-toolkit-macro-internals/src/command/mod.rs @@ -10,6 +10,11 @@ pub enum ExecutionContext { CliOnly(Path), RpcOnly(Path), Local(Path), + CustomCli { + custom: Path, + cli: Path, + is_async: bool, + }, } impl Default for ExecutionContext { fn default() -> Self { diff --git a/rpc-toolkit-macro-internals/src/command/parse.rs b/rpc-toolkit-macro-internals/src/command/parse.rs index a5f6f4b..0108a3d 100644 --- a/rpc-toolkit-macro-internals/src/command/parse.rs +++ b/rpc-toolkit-macro-internals/src/command/parse.rs @@ -126,6 +126,73 @@ pub fn parse_command_attr(args: AttributeArgs) -> Result { "`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, + cli: custom_cli_impl.clone(), + is_async: false, + } + } + NestedMeta::Meta(Meta::List(custom_cli_impl)) if custom_cli_impl.nested.len() == 1 => { + let is_async = match custom_cli_impl.nested.first().unwrap() { + NestedMeta::Meta(Meta::Path(p)) if p.is_ident("async") => false, + arg => return Err(Error::new(arg.span(), "unknown argument")), + }; + opt.common().exec_ctx = ExecutionContext::CustomCli { + custom: list.path, + 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`")); @@ -170,6 +237,12 @@ pub fn parse_command_attr(args: AttributeArgs) -> Result { "`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") => { @@ -204,6 +277,12 @@ pub fn parse_command_attr(args: AttributeArgs) -> Result { "`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") => { @@ -238,6 +317,12 @@ pub fn parse_command_attr(args: AttributeArgs) -> Result { "`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") => {