mirror of
https://github.com/Start9Labs/rpc-toolkit.git
synced 2026-03-26 02:11:56 +00:00
1250 lines
48 KiB
Rust
1250 lines
48 KiB
Rust
use std::collections::HashSet;
|
|
|
|
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<Type>, opt: &mut Options) -> TokenStream {
|
|
let mut bounds: Punctuated<TypeParamBound, Add> = 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<T> CommandContextCli for T where T: #cli_bounds {}
|
|
|
|
pub trait CommandContextRpc: #rpc_bounds {}
|
|
impl<T> 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::<Vec<_>>().as_slice() {
|
|
#(
|
|
#subcmd_handler
|
|
)*
|
|
_ => ()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
_ => quote! {},
|
|
}
|
|
};
|
|
fn impl_getter<I: Iterator<Item = TokenStream>>(
|
|
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(default) = &arg.default {
|
|
modifications.extend(quote_spanned! { ty_span =>
|
|
arg = arg.default_value(#default);
|
|
});
|
|
} 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::new(#name_str);
|
|
#(
|
|
arg = arg.help(#help);
|
|
)*
|
|
#(
|
|
arg = arg.short(#short);
|
|
)*
|
|
#(
|
|
arg = arg.long(#long);
|
|
)*
|
|
#modifications
|
|
|
|
arg
|
|
}
|
|
})
|
|
} else {
|
|
None
|
|
}
|
|
})
|
|
.collect::<Vec<_>>();
|
|
let required = LitBool::new(subcommand_required, Span::call_site());
|
|
let alias = &opt.common().aliases;
|
|
quote! {
|
|
pub fn build_app() -> ::rpc_toolkit::command_helpers::prelude::Command<'static> {
|
|
let mut app = ::rpc_toolkit::command_helpers::prelude::Command::new(#name);
|
|
#(
|
|
app = app.about(#about);
|
|
)*
|
|
#(
|
|
app = app.alias(#alias);
|
|
)*
|
|
#(
|
|
app = app.arg(#arg);
|
|
)*
|
|
#(
|
|
app = app.subcommand(#subcommand::build_app());
|
|
)*
|
|
if #required {
|
|
app = app.subcommand_required(true);
|
|
}
|
|
app
|
|
}
|
|
}
|
|
}
|
|
|
|
struct GenericFilter<'a> {
|
|
src: &'a Generics,
|
|
lifetimes: HashSet<Lifetime>,
|
|
types: HashSet<Ident>,
|
|
}
|
|
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<GenericParam, Comma> = 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.last().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.is_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();
|
|
param_def.push(quote! {
|
|
#[serde(rename = #rename)]
|
|
#field_name: #ty,
|
|
})
|
|
}
|
|
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! { <GenericContext as Into<#ty>>::into(ctx.clone()) }
|
|
} else {
|
|
quote! { <GenericContext as Into<#ty>>::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! { ::<GenericContext> })
|
|
.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::<Vec<_>>().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 = 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! { <GenericContext as Into<#ty>>::into(ctx.clone()) }
|
|
} else {
|
|
quote! { <GenericContext as Into<#ty>>::into(ctx) }
|
|
}
|
|
}
|
|
ParamType::ParentData(ty) => {
|
|
parent_data_ty = quote! { #ty };
|
|
quote! { parent_data }
|
|
}
|
|
ParamType::Request => quote! { request },
|
|
ParamType::Response => quote! { response },
|
|
ParamType::None => unreachable!(),
|
|
});
|
|
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 arg.multiple.is_some() {
|
|
quote! {
|
|
#field_name: matches.values_of(#arg_name).iter().flatten().map(|arg_val| #parse_val).collect::<Result<_, _>>()?,
|
|
}
|
|
} 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.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! { ::<Params#param_ty_generics, GenericContext> })
|
|
.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() =>
|
|
(#subcommand::NAME, Some(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
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn build(args: AttributeArgs, mut item: ItemFn) -> TokenStream {
|
|
let mut params = macro_try!(parse_param_attrs(&mut item));
|
|
let mut opt = macro_try!(parse_command_attr(args));
|
|
if let Some(a) = &opt.common().blocking {
|
|
if item.sig.asyncness.is_some() {
|
|
return Error::new(a.span(), "cannot use `blocking` on an async fn").to_compile_error();
|
|
}
|
|
}
|
|
opt.common().is_async = item.sig.asyncness.is_some();
|
|
let fn_vis = &item.vis;
|
|
let fn_name = &item.sig.ident;
|
|
let fn_generics = &item.sig.generics;
|
|
let command_name_str = opt
|
|
.common()
|
|
.rename
|
|
.clone()
|
|
.unwrap_or_else(|| LitStr::new(&fn_name.to_string(), fn_name.span()));
|
|
let is_async = LitBool::new(
|
|
opt.common().is_async,
|
|
item.sig
|
|
.asyncness
|
|
.map(|a| a.span())
|
|
.unwrap_or_else(Span::call_site),
|
|
);
|
|
let ctx_ty = params.iter().find_map(|a| {
|
|
if let ParamType::Context(ty) = a {
|
|
Some(ty.clone())
|
|
} else {
|
|
None
|
|
}
|
|
});
|
|
let ctx_trait = ctx_trait(ctx_ty, &mut opt);
|
|
let metadata = metadata(&mut opt);
|
|
let build_app = build_app(command_name_str.clone(), &mut opt, &mut params);
|
|
let rpc_handler = rpc_handler(fn_name, fn_generics, &opt, ¶ms);
|
|
let cli_handler = cli_handler(fn_name, fn_generics, &mut opt, ¶ms);
|
|
|
|
let res = quote! {
|
|
#item
|
|
#fn_vis mod #fn_name {
|
|
use super::*;
|
|
|
|
pub const NAME: &'static str = #command_name_str;
|
|
pub const ASYNC: bool = #is_async;
|
|
|
|
#ctx_trait
|
|
|
|
#metadata
|
|
|
|
#build_app
|
|
|
|
#rpc_handler
|
|
|
|
#cli_handler
|
|
}
|
|
};
|
|
if opt.common().macro_debug {
|
|
panic!("EXPANDED MACRO:\n{}", res);
|
|
}
|
|
res
|
|
}
|