From 8ac8d3b1cb59249d492a657fa8cd39fbe3fd99a7 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Wed, 29 Apr 2015 10:56:10 +0200 Subject: [PATCH] fix(clap): generate command data structure We do this in the hopes to circumvent a stack overflow. This means we will setup the parser entirely at runtime, which actually saves a little bit of code. --- src/mako/cli/lib/argparse.mako | 58 +++++++++++++++++++--------------- src/mako/cli/lib/engine.mako | 2 +- src/mako/cli/main.rs.mako | 1 - src/rust/cli/cmn.rs | 17 ++++++++++ 4 files changed, 51 insertions(+), 27 deletions(-) diff --git a/src/mako/cli/lib/argparse.mako b/src/mako/cli/lib/argparse.mako index 483cb5abd6..d273bbb331 100644 --- a/src/mako/cli/lib/argparse.mako +++ b/src/mako/cli/lib/argparse.mako @@ -7,6 +7,15 @@ def rust_boolean(v): return v and 'true' or 'false' + + def rust_optional(v): + if v is None: + return 'None' + if isinstance(v, bool): + v = v and 'true' or 'false' + elif isinstance(v, basestring): + v = '"%s"' % v + return 'Some(%s)' % v %>\ <%def name="grammar(c)">\ % for resource in sorted(c.rta_map.keys()): @@ -109,7 +118,7 @@ Configuration: None )) %>\ -App::new("${util.program_name()}") +let mut app = App::new("${util.program_name()}") <%block filter="indent_by(7)">\ .author("${', '.join(cargo.authors)}") .version("${cargo.build_version}") @@ -121,11 +130,17 @@ App::new("${util.program_name()}") .arg(Arg::with_name("${arg_name or flag}") .long("${flag}") .help("${desc}") - .takes_value(${rust_boolean(arg_name)})) + .takes_value(${rust_boolean(arg_name)}))\ +% if loop.last: +; +% else: + +% endif % endfor +let arg_data = [ % for resource in sorted(c.rta_map.keys()): -.subcommand( - SubCommand::new("${mangle_subcommand(resource)}") +<%block filter="indent_by(4)">\ +("${mangle_subcommand(resource)}", vec![ % for method in sorted(c.rta_map[resource]): <% mc = new_method_context(resource, method, c) @@ -193,30 +208,23 @@ App::new("${util.program_name()}") )) # handle output %>\ - .subcommand( - SubCommand::new("${mangle_subcommand(method)}") - % if mc.m.get('description') is not None: - .about("${mc.m.description}") - % endif + ("${mangle_subcommand(method)}", ${rust_optional(mc.m.get('description'))}, + vec![ % for flag, desc, arg_name, required, multi in args: - .arg( - Arg::with_name("${arg_name or flag}") - % if flag: - .short("${flag}") - % endif - % if desc: - .help("${desc}") - % endif - % if flag is not None: - .takes_value(${rust_boolean(arg_name)}) - % endif - .required(${rust_boolean(required)}) - .multiple(${rust_boolean(multi)})) + (${rust_optional(arg_name)}, + ${rust_optional(flag)}, + ${rust_optional(desc)}, + ${rust_optional(required)}, + ${rust_optional(multi)}), + % if not loop.last: + + % endif % endfor - ) + ]), % endfor # each method - ) + ]), + % endfor # end for each resource -.get_matches(); +]; \ No newline at end of file diff --git a/src/mako/cli/lib/engine.mako b/src/mako/cli/lib/engine.mako index 8667bbc547..48eafd7251 100644 --- a/src/mako/cli/lib/engine.mako +++ b/src/mako/cli/lib/engine.mako @@ -29,7 +29,7 @@ %>\ mod cmn; use cmn::{InvalidOptionsError, CLIError, JsonTokenStorage, arg_from_str, writer_from_opts, parse_kv_arg, - input_file_from_opts, input_mime_from_opts, FieldCursor, FieldError}; + input_file_from_opts, input_mime_from_opts, FieldCursor, FieldError, make_subcommand}; use std::default::Default; use std::str::FromStr; diff --git a/src/mako/cli/main.rs.mako b/src/mako/cli/main.rs.mako index 0f234cc2a1..7f322659fa 100644 --- a/src/mako/cli/main.rs.mako +++ b/src/mako/cli/main.rs.mako @@ -30,7 +30,6 @@ use clap::{App, SubCommand, Arg}; ## ${engine.new(c)}\ fn main() { - let matches = ${argparse.new(c) | indent_all_but_first_by(1)}\ ## let opts: Options = Options::docopt().decode().unwrap_or_else(|e| e.exit()); diff --git a/src/rust/cli/cmn.rs b/src/rust/cli/cmn.rs index 1f6b51b934..25feecc78d 100644 --- a/src/rust/cli/cmn.rs +++ b/src/rust/cli/cmn.rs @@ -1,6 +1,7 @@ use oauth2::{ApplicationSecret, ConsoleApplicationSecret, TokenStorage, Token}; use rustc_serialize::json; use mime::Mime; +use clap::{App, SubCommand}; use std::fs; use std::env; @@ -15,6 +16,22 @@ use std::default::Default; const FIELD_SEP: char = '.'; + +fn make_subcommand(command_name: &str, desc: Option<&str>, + args: &Vec<(Option<&str>, Option<&str>, Option<&str>, + Option, Option)>) + -> App<'a, 'v, 'ab, 'u, 'h, 'ar> { + // arg_name: Option<&str>, + // short_name: Option<&str>, + // help: Option<&str>, + // % if flag is not None: + // .takes_value(${rust_boolean(arg_name)}) + // required: Option, + // multiple: Option + SubCommand::new(command_name) +} + + #[derive(Clone, Default)] pub struct FieldCursor(Vec);