Skip to content

Commit

Permalink
feat(CLI): parse method parameters and set them
Browse files Browse the repository at this point in the history
It's implemented in a working fashion, except that the default value
is not currently set to something sensible, causing duplicate errors in
case the key-value syntax is wrong.

Related to #61
  • Loading branch information
Byron committed Apr 15, 2015
1 parent fa01131 commit 6ae6ee8
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 21 deletions.
8 changes: 1 addition & 7 deletions src/mako/api/lib/mbuild.mako
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
DELEGATE_PROPERTY_NAME, struct_type_bounds_s, scope_url_to_variant,
re_find_replacements, ADD_PARAM_FN, ADD_PARAM_MEDIA_EXAMPLE, upload_action_fn, METHODS_RESOURCE,
method_name_to_variant, unique_type_name, size_to_bytes, method_default_scope,
is_repeated_property)
is_repeated_property, setter_fn_name)
def get_parts(part_prop):
if not part_prop:
Expand All @@ -27,12 +27,6 @@
part_desc += ''.join('* *%s*\n' % part for part in parts)
part_desc = part_desc[:-1]
return part_desc
def setter_fn_name(p):
fn_name = p.name
if is_repeated_property(p):
fn_name = 'add_' + fn_name
return fn_name
%>\
<%namespace name="util" file="../../lib/util.mako"/>\
<%namespace name="lib" file="lib.mako"/>\
Expand Down
42 changes: 33 additions & 9 deletions src/mako/cli/lib/engine.mako
Original file line number Diff line number Diff line change
@@ -1,21 +1,28 @@
<%namespace name="util" file="../../lib/util.mako"/>\
<%!
from util import (hub_type, mangle_ident, indent_all_but_first_by, activity_rust_type)
from util import (hub_type, mangle_ident, indent_all_but_first_by, activity_rust_type, setter_fn_name)
from cli import (mangle_subcommand, new_method_context, PARAM_FLAG, STRUCT_FLAG, UPLOAD_FLAG, OUTPUT_FLAG, VALUE_ARG,
CONFIG_DIR, SCOPE_FLAG, is_request_value_property, FIELD_SEP, docopt_mode, FILE_ARG, MIME_ARG, OUT_ARG,
cmd_ident, call_method_ident, arg_ident, POD_TYPES, flag_ident)
cmd_ident, call_method_ident, arg_ident, POD_TYPES, flag_ident, ident)
v_arg = '<%s>' % VALUE_ARG
SOPT = 'self.opt.'
def to_opt_arg_ident(p):
return SOPT + arg_ident(p.name)
def borrow_prefix(p):
ptype = p.get('type', None)
borrow = ''
if ptype not in POD_TYPES or ptype in ('string', None):
borrow = '&'
return borrow
%>\
<%def name="new(c)">\
<%
hub_type_name = 'api::' + hub_type(c.schemas, util.canonical_name())
%>\
mod cmn;
use cmn::{InvalidOptionsError, CLIError, JsonTokenStorage, arg_from_str, writer_from_opts};
use cmn::{InvalidOptionsError, CLIError, JsonTokenStorage, arg_from_str, writer_from_opts, parse_kv_arg};
use std::default::Default;
use std::str::FromStr;
Expand Down Expand Up @@ -122,6 +129,7 @@ self.opt.${cmd_ident(method)} {
<%
mc = new_method_context(resource, method, c)
handle_output = mc.response_schema or mc.m.get('supportsMediaDownload', False)
handle_props = mc.optional_props or parameters is not UNDEFINED
%>\
## REQUIRED PARAMETERS
% for p in mc.required_props:
Expand All @@ -141,17 +149,33 @@ let ${prop_name}: ${prop_type} = arg_from_str(&${opt_ident}, err, "<${mangle_sub
for p in mc.required_props:
borrow = ''
# if type is not available, we know it's the request value, which should also be borrowed
ptype = p.get('type', None)
if ptype not in POD_TYPES or ptype in ('string', None):
borrow = '&'
borrow = borrow_prefix(p)
arg_name = mangle_ident(p.name)
if ptype == 'string':
if p.get('type', '') == 'string':
arg_name = to_opt_arg_ident(p)
call_args.append(borrow + arg_name)
# end for each required prop
%>\
let call = self.hub.${mangle_ident(resource)}().${mangle_ident(method)}(${', '.join(call_args)});
## TODO: set parameters
let mut call = self.hub.${mangle_ident(resource)}().${mangle_ident(method)}(${', '.join(call_args)});
% if handle_props:
<%
optional_props = [p for p in mc.optional_props if not p.get('skip_example', False)]
%>\
for parg in ${SOPT + arg_ident(VALUE_ARG)}.iter() {
let (key, value) = parse_kv_arg(&*parg, err, "default");
match key {
% for p in optional_props:
"${ident(p.name)}" => call = call.${mangle_ident(setter_fn_name(p))}(\
% if p.type != 'string':
arg_from_str(value, err, "${ident(p.name)}", "${p.type}")),
% else:
value),
% endif # handle conversion
% endfor # each property
_ => err.issues.push(CLIError::UnknownParameter(key.to_string())),
}
}
% endif # handle call parameters
## TODO: parse upload
if dry_run {
None
Expand Down
6 changes: 6 additions & 0 deletions src/mako/lib/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -551,6 +551,12 @@ def is_required_property(p):
def is_repeated_property(p):
return p.get('repeated', False)

def setter_fn_name(p):
fn_name = p.name
if is_repeated_property(p):
fn_name = 'add_' + fn_name
return fn_name

# method_params(...), request_value|None -> (required_properties, optional_properties, part_prop|None)
def organize_params(params, request_value):
part_prop = None
Expand Down
36 changes: 31 additions & 5 deletions src/rust/cli/cmn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,25 @@ use std::io::{Write, Read, stdout};

use std::default::Default;

pub fn parse_kv_arg<'a>(kv: &'a str, err: &mut InvalidOptionsError, default: &'static str)
-> (&'a str, &'a str) {
let mut add_err = || err.issues.push(CLIError::InvalidKeyValueSyntax(kv.to_string()));
match kv.rfind('=') {
None => {
add_err();
return (kv, default)
},
Some(pos) => {
let key = &kv[..pos];
if kv.len() <= pos + 1 {
add_err();
return (key, default)
}
(key, &kv[pos+1..])
}
}
}

// May panic if we can't open the file - this is anticipated, we can't currently communicate this
// kind of error: TODO: fix this architecture :)
pub fn writer_from_opts(flag: bool, arg: &str) -> Box<Write> {
Expand All @@ -31,7 +50,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, format!("{}", perr)))
CLIError::ParseError((arg_name, arg_type, arg.to_string(), format!("{}", perr)))
);
Default::default()
},
Expand Down Expand Up @@ -122,16 +141,23 @@ impl fmt::Display for ConfigurationError {
#[derive(Debug)]
pub enum CLIError {
Configuration(ConfigurationError),
ParseError((&'static str, &'static str, String)),
ParseError((&'static str, &'static str, String, String)),
UnknownParameter(String),
InvalidKeyValueSyntax(String),
}

impl fmt::Display for CLIError {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
match *self {
CLIError::Configuration(ref err) => writeln!(f, "Configuration -> {}", err),
CLIError::ParseError((arg_name, type_name, ref err_desc))
=> writeln!(f, "Failed to parse argument {} as {} with error: {}",
arg_name, type_name, err_desc),
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)
=> writeln!(f, "Parameter '{}' is unknown.", param_name),
CLIError::InvalidKeyValueSyntax(ref kv)
=> writeln!(f, "'{}' does not match <key>=<value>", kv),

}
}
}
Expand Down

0 comments on commit 6ae6ee8

Please sign in to comment.