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::NoParams; }, 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 } }