diff --git a/src/mako/cli/docs/commands.md.mako b/src/mako/cli/docs/commands.md.mako index 0870762f066..79f2c793979 100644 --- a/src/mako/cli/docs/commands.md.mako +++ b/src/mako/cli/docs/commands.md.mako @@ -169,19 +169,21 @@ ${SPLIT_END} - ${p.get('description') or NO_DESC | xml_escape ,indent_all_but_first_by(2)} -<%def name="_list_schem_args(schema, cursor_tokens=list())">\ +<%def name="_list_schem_args(schema, cursor_tokens=list(), first_flag=None)">\ <% if len(cursor_tokens) == 0: cursor_tokens = [FIELD_SEP] + if first_flag is None: + first_flag = '-%s ' % STRUCT_FLAG + def cursor_fmt(cursor): - flag = '-%s ' % STRUCT_FLAG fndfi = 0 # first non-dot field index for (fndfi, v) in enumerate(cursor): if v != FIELD_SEP: break res = ''.join(cursor[:fndfi]) + FIELD_SEP.join(cursor[fndfi:]) - res += ' ' + flag + res += ' ' return res def cursor_arg(field): @@ -194,9 +196,11 @@ ${SPLIT_END} % for fni, fn in enumerate(sorted(schema.fields.keys())): <% f = schema.fields[fn] + if fni > 0: + first_flag = '' %>\ % if isinstance(f, SchemaEntry): -* `-${STRUCT_FLAG} ${cursor_arg(mangle_subcommand(fn))}=${field_to_value(f)}` +* `${first_flag}${cursor_arg(mangle_subcommand(fn))}=${field_to_value(f)}` - ${f.property.get('description', NO_DESC) | xml_escape, indent_all_but_first_by(2)} % if f.container_type == CTYPE_ARRAY: - Each invocation of this argument appends the given value to the array. @@ -207,7 +211,7 @@ ${SPLIT_END} <% cursor_tokens.append(mangle_subcommand(fn)) %>\ -${self._list_schem_args(f, cursor_tokens)} +${self._list_schem_args(f, cursor_tokens, first_flag)} <% assert not cursor_tokens or cursor_tokens[-1] == FIELD_SEP if not cursor_tokens: diff --git a/src/mako/cli/lib/argparse.mako b/src/mako/cli/lib/argparse.mako index 7bbf372a6ad..24d789ceb0d 100644 --- a/src/mako/cli/lib/argparse.mako +++ b/src/mako/cli/lib/argparse.mako @@ -130,6 +130,21 @@ Configuration: False, )) %>\ +<% + have_media_params = False + for resource in sorted(c.rta_map.keys()): + methods = sorted(c.rta_map[resource]) + for method in methods: + mc = new_method_context(resource, method, c) + if mc.media_params: + have_media_params = True + break + # end for each method + # end for each resource +%>\ +% if have_media_params: +let upload_value_names = ["${MODE_ARG}", "${FILE_ARG}"]; +% endif let arg_data = [ % for resource in sorted(c.rta_map.keys()): <% @@ -157,7 +172,6 @@ let arg_data = [ mangle_subcommand(p.name), True, False, - None, )) # end for each required property @@ -168,19 +182,16 @@ let arg_data = [ KEY_VALUE_ARG, True, True, - None )) # end request_value if mc.media_params: - upload_protocols = [mp.protocol for mp in mc.media_params] args.append(( UPLOAD_FLAG, - "Specify which file to upload", + "Specify the upload protocol (%s) and the file to upload" % '|'.join(mp.protocol for mp in mc.media_params), MODE_ARG, True, - False, - upload_protocols + True, )) # end upload handling @@ -191,7 +202,6 @@ let arg_data = [ VALUE_ARG, False, True, - None )) # end paramters @@ -202,29 +212,17 @@ let arg_data = [ OUT_ARG, False, False, - None )) # handle output %>\ ("${mangle_subcommand(method)}", ${rust_optional(mc.m.get('description'))}, vec![ - % for flag, desc, arg_name, required, multi, upload_protocols in args: + % for flag, desc, arg_name, required, multi in args: (${rust_optional(arg_name)}, ${rust_optional(flag)}, ${rust_optional(desc)}, ${rust_optional(required)}, - ${rust_optional(multi)}, - % if not mc.media_params: - ## Make sure the type is set, even though we don't have any protocol information - % if loop.first: - None::${'>'}\ - % else: - None\ - % endif - % else: - ${rust_optional(upload_protocols)}\ - % endif -), + ${rust_optional(multi)}), % if not loop.last: % endif @@ -266,12 +264,14 @@ for &(main_command_name, ref about, ref subcommands) in arg_data.iter() { scmd = scmd.about(desc); } - for &(ref arg_name, ref flag, ref desc, ref required, ref multi, ref protocols) in args { - let mut arg = Arg::with_name(match (arg_name, flag) { - (&Some(an), _ ) => an, - (_ , &Some(f)) => f, - _ => unreachable!(), - }); + for &(ref arg_name, ref flag, ref desc, ref required, ref multi) in args { + let arg_name_str = + match (arg_name, flag) { + (&Some(an), _ ) => an, + (_ , &Some(f)) => f, + _ => unreachable!(), + }; + let mut arg = Arg::with_name(arg_name_str); if let &Some(short_flag) = flag { arg = arg.short(short_flag); } @@ -287,20 +287,13 @@ for &(main_command_name, ref about, ref subcommands) in arg_data.iter() { if let &Some(multi) = multi { arg = arg.multiple(multi); } - if let &Some(ref protocols) = protocols { - arg = arg.possible_values(protocols); - arg = arg.requires("${FILE_ARG}"); + if arg_name_str == "${MODE_ARG}" { + arg = arg.number_of_values(2); + arg = arg.value_names(&upload_value_names); - scmd = scmd.arg(Arg::with_name("${FILE_ARG}") - .short("${FILE_FLAG}") - .required(true) - .requires("${MODE_ARG}") - .help("The file to upload") - .takes_value(true)); scmd = scmd.arg(Arg::with_name("${MIME_ARG}") .short("${MIME_FLAG}") .requires("${MODE_ARG}") - .requires("${FILE_ARG}") .required(false) .help("The file's mime time, like 'application/octet-stream'") .takes_value(true)); diff --git a/src/mako/cli/lib/engine.mako b/src/mako/cli/lib/engine.mako index 4c6fd4ad747..989c2ffeda3 100644 --- a/src/mako/cli/lib/engine.mako +++ b/src/mako/cli/lib/engine.mako @@ -2,8 +2,8 @@ <%! from util import (hub_type, mangle_ident, indent_all_but_first_by, activity_rust_type, setter_fn_name, ADD_PARAM_FN, upload_action_fn, is_schema_with_optionals, schema_markers, indent_by, method_default_scope, - ADD_SCOPE_FN, TREF) - from cli import (mangle_subcommand, new_method_context, PARAM_FLAG, STRUCT_FLAG, UPLOAD_FLAG, OUTPUT_FLAG, VALUE_ARG, + ADD_SCOPE_FN, TREF, enclose_in) + from cli import (mangle_subcommand, new_method_context, PARAM_FLAG, STRUCT_FLAG, OUTPUT_FLAG, VALUE_ARG, CONFIG_DIR, SCOPE_FLAG, is_request_value_property, FIELD_SEP, docopt_mode, FILE_ARG, MIME_ARG, OUT_ARG, call_method_ident, POD_TYPES, opt_value, ident, JSON_TYPE_VALUE_MAP, KEY_VALUE_ARG, to_cli_schema, SchemaEntry, CTYPE_POD, actual_json_type, CTYPE_MAP, CTYPE_ARRAY, @@ -25,7 +25,8 @@ hub_type_name = 'api::' + hub_type(c.schemas, util.canonical_name()) %>\ use cmn::{InvalidOptionsError, CLIError, JsonTokenStorage, arg_from_str, writer_from_opts, parse_kv_arg, - input_file_from_opts, input_mime_from_opts, FieldCursor, FieldError, CallType, UploadProtocol}; + input_file_from_opts, input_mime_from_opts, FieldCursor, FieldError, CallType, UploadProtocol, + protocol_from_str}; use std::default::Default; use std::str::FromStr; @@ -263,8 +264,9 @@ ${value_unwrap}\ } % endif # handle call parameters % if mc.media_params: -let protocol = CallType::Upload(UploadProtocol::from(${req_value(MODE_ARG)})); -let mut input_file = input_file_from_opts(${req_value(FILE_ARG)}, err); +let vals = opt.values_of("${MODE_ARG}").unwrap(); +let protocol = protocol_from_str(vals[0], [${', '.join('"%s"' % mp.protocol for mp in mc.media_params)}].iter().map(|&v| v.to_string()).collect(), err); +let mut input_file = input_file_from_opts(vals[1], err); let mime_type = input_mime_from_opts(${opt_value(MIME_ARG, default=DEFAULT_MIME)}, err); % else: let protocol = CallType::Standard; diff --git a/src/rust/cli/cmn.rs b/src/rust/cli/cmn.rs index 1ac51088a0f..2a8e1e2b527 100644 --- a/src/rust/cli/cmn.rs +++ b/src/rust/cli/cmn.rs @@ -44,13 +44,15 @@ impl AsRef for CallType { } } -impl<'a> From<&'a str> for UploadProtocol { - fn from(this: &'a str) -> UploadProtocol { - match this { - "simple" => UploadProtocol::Simple, - "resumable" => UploadProtocol::Resumable, - _ => panic!("We don't expect to see anything else here, the CLI parser takes care") - } +impl FromStr for UploadProtocol { + type Err = String; + + fn from_str(s: &str) -> Result { + match s { + "simple" => Ok(UploadProtocol::Simple), + "resumable" => Ok(UploadProtocol::Resumable), + _ => Err(format!("Protocol '{}' is unknown", s)), + } } } @@ -149,6 +151,17 @@ pub fn parse_kv_arg<'a>(kv: &'a str, err: &mut InvalidOptionsError, for_hashmap: } } +pub fn protocol_from_str(name: &str, valid_protocols: Vec, err: &mut InvalidOptionsError) -> CallType { + CallType::Upload( + match UploadProtocol::from_str(name) { + Ok(up) => up, + Err(msg) => { + err.issues.push(CLIError::InvalidUploadProtocol(name.to_string(), valid_protocols)); + UploadProtocol::Simple + } + }) +} + pub fn input_file_from_opts(file_path: &str, err: &mut InvalidOptionsError) -> Option { match fs::File::open(file_path) { Ok(f) => Some(f), @@ -189,7 +202,7 @@ pub fn arg_from_str(arg: &str, err: &mut InvalidOptionsError, match FromStr::from_str(arg) { Err(perr) => { err.issues.push( - CLIError::ParseError((arg_name, arg_type, arg.to_string(), format!("{}", perr))) + CLIError::ParseError(arg_name, arg_type, arg.to_string(), format!("{}", perr)) ); Default::default() }, @@ -347,8 +360,9 @@ impl fmt::Display for FieldError { #[derive(Debug)] pub enum CLIError { Configuration(ConfigurationError), - ParseError((&'static str, &'static str, String, String)), + ParseError(&'static str, &'static str, String, String), UnknownParameter(String), + InvalidUploadProtocol(String, Vec), InvalidKeyValueSyntax(String, bool), Input(InputError), Field(FieldError), @@ -362,7 +376,9 @@ impl fmt::Display for CLIError { CLIError::Configuration(ref err) => write!(f, "Configuration -> {}", err), CLIError::Input(ref err) => write!(f, "Input -> {}", err), CLIError::Field(ref err) => write!(f, "Field -> {}", err), - CLIError::ParseError((arg_name, type_name, ref value, ref err_desc)) + CLIError::InvalidUploadProtocol(ref proto_name, ref valid_names) + => writeln!(f, "'{}' is not a valid upload protocol. Choose from one of {}", proto_name, valid_names.connect(", ")), + CLIError::ParseError(arg_name, type_name, ref value, ref err_desc) => writeln!(f, "Failed to parse argument '{}' with value '{}' as {} with error: {}", arg_name, value, type_name, err_desc), CLIError::UnknownParameter(ref param_name)