Skip to content

Commit

Permalink
feat(CLI): -u <mode> <file> parsing
Browse files Browse the repository at this point in the history
* As `possible_values()` applies to all arguments, we cannot use it
  anymore but have to check the UploadProtocol type ourselves.
  Besides that, switching to the latest `clap` simplified our lives
  a little.
* ajusted docs to not enforce using `-r` all the time

Fixes #92
[skip ci]
  • Loading branch information
Byron committed May 1, 2015
1 parent c346645 commit 75b80de
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 57 deletions.
14 changes: 9 additions & 5 deletions src/mako/cli/docs/commands.md.mako
Original file line number Diff line number Diff line change
Expand Up @@ -169,19 +169,21 @@ ${SPLIT_END}
- ${p.get('description') or NO_DESC | xml_escape ,indent_all_but_first_by(2)}
</%def>

<%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):
Expand All @@ -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.
Expand All @@ -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:
Expand Down
67 changes: 30 additions & 37 deletions src/mako/cli/lib/argparse.mako
Original file line number Diff line number Diff line change
Expand Up @@ -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()):
<%
Expand Down Expand Up @@ -157,7 +172,6 @@ let arg_data = [
mangle_subcommand(p.name),
True,
False,
None,
))
# end for each required property
Expand All @@ -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
Expand All @@ -191,7 +202,6 @@ let arg_data = [
VALUE_ARG,
False,
True,
None
))
# end paramters
Expand All @@ -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::${'<Vec<UploadProtocol>>'}\
% else:
None\
% endif
% else:
${rust_optional(upload_protocols)}\
% endif
),
${rust_optional(multi)}),
% if not loop.last:
% endif
Expand Down Expand Up @@ -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);
}
Expand All @@ -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));
Expand Down
12 changes: 7 additions & 5 deletions src/mako/cli/lib/engine.mako
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
36 changes: 26 additions & 10 deletions src/rust/cli/cmn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,15 @@ impl AsRef<str> 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<UploadProtocol, String> {
match s {
"simple" => Ok(UploadProtocol::Simple),
"resumable" => Ok(UploadProtocol::Resumable),
_ => Err(format!("Protocol '{}' is unknown", s)),
}
}
}

Expand Down Expand Up @@ -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<String>, 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<fs::File> {
match fs::File::open(file_path) {
Ok(f) => Some(f),
Expand Down Expand Up @@ -189,7 +202,7 @@ pub fn arg_from_str<T>(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()
},
Expand Down Expand Up @@ -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<String>),
InvalidKeyValueSyntax(String, bool),
Input(InputError),
Field(FieldError),
Expand All @@ -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)
Expand Down

0 comments on commit 75b80de

Please sign in to comment.