feat(CLI):required arg parsing + first doit() call

We are parsing required scalar values and handle parse-errors correctly,
to the point were we make a simple, non-upload doit() call.

It shows that we seem to build invalid calls, for now,but that's nothing
we can't fix once the time is ripe.

Next goals will be related to finalizing the argument parsing code.

Fixes #60
This commit is contained in:
Sebastian Thiel
2015-04-14 17:17:21 +02:00
parent d6919f1eb6
commit e34e24e049
7 changed files with 87 additions and 18 deletions

View File

@@ -3,6 +3,11 @@ api:
# exclude APIs which currently don't build correctly. State the reason for the exclusion as well
# to allow looking at it at a later point.
-
terms:
# how to actually do something with the API
action: doit
# when a resource is supposed to be uploaded
upload_action: upload
# Contains values shared among all API implementations
directories:
# directory under which all generated sources should reside

View File

@@ -1,10 +1,5 @@
api:
base_path: "etc/api"
terms:
# how to actually do something with the API
action: doit
# when a resource is supposed to be uploaded
upload_action: upload
properties:
# additional fields specified by the user
params: '_additional_params'

View File

@@ -26,6 +26,6 @@ cargo:
keywords: [cli]
is_executable: YES
dependencies:
- docopt = "*"
- docopt_macros = "*"
- docopt = "= 0.6.59"
- docopt_macros = "= 0.6.59"
- rustc-serialize = "*"

View File

@@ -81,6 +81,10 @@ def ident(name):
def cmd_ident(name):
return 'cmd_' + ident(name)
# Similar to cmd_ident, but for arguments
def arg_ident(name):
return 'arg_' + ident(name)
# Returns identifier for method dealing with options for the given resource-method pair
def call_method_ident(resource, method):
return '_%s_%s' % (ident(resource), ident(method))

View File

@@ -1,9 +1,9 @@
<%namespace name="util" file="../../lib/util.mako"/>\
<%!
from util import hub_type
from util import (hub_type, mangle_ident, indent_all_but_first_by, activity_rust_type)
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)
cmd_ident, call_method_ident, arg_ident, POD_TYPES)
v_arg = '<%s>' % VALUE_ARG
%>\
@@ -12,13 +12,14 @@
hub_type_name = 'api::' + hub_type(c.schemas, util.canonical_name())
%>\
mod cmn;
use cmn::{InvalidOptionsError, JsonTokenStorage};
use cmn::{InvalidOptionsError, CLIError, JsonTokenStorage};
use std::default::Default;
use std::str::FromStr;
use oauth2::{Authenticator, DefaultAuthenticatorDelegate};
struct Engine {
opt: Options,
config_dir: String,
hub: ${hub_type_name}<hyper::Client, Authenticator<DefaultAuthenticatorDelegate, JsonTokenStorage, hyper::Client>>,
}
@@ -26,15 +27,16 @@ struct Engine {
impl Engine {
% for resource in sorted(c.rta_map.keys()):
% for method in sorted(c.rta_map[resource]):
fn ${call_method_ident(resource, method)}(&self, dry_run: bool, err: &mut InvalidOptionsError) -> Option<api::Error> {
${self._method_call_impl(c, resource, method)}\
fn ${call_method_ident(resource, method)}(&self, dry_run: bool, err: &mut InvalidOptionsError)
-> Option<api::Error> {
${self._method_call_impl(c, resource, method) | indent_all_but_first_by(2)}
}
% endfor # each method
% endfor
fn _doit(&self, dry_run: bool) -> (Option<api::Error>, Option<InvalidOptionsError>) {
let mut err = InvalidOptionsError::new();
let mut call_result: Option<api::Error> = None;
let mut call_result: Option<api::Error>;
let mut err_opt: Option<InvalidOptionsError> = None;
## RESOURCE LOOP: check for set primary subcommand
@@ -95,7 +97,6 @@ self.opt.${cmd_ident(method)} {
}, None);
let engine = Engine {
opt: opt,
config_dir: config_dir,
hub: ${hub_type_name}::new(hyper::Client::new(), auth),
};
@@ -113,6 +114,67 @@ self.opt.${cmd_ident(method)} {
}
</%def>
<%def name="_method_call_impl(c, resource, method)">\
None
<%def name="_method_call_impl(c, resource, method)" buffered="True">\
<%
mc = new_method_context(resource, method, c)
## if is_request_value_property(mc, p):
## continue
## args.append('<%s>' % mangle_subcommand(p.name))
%>\
## REQUIRED PARAMETERS
% for p in mc.required_props:
<%
prop_name = mangle_ident(p.name)
prop_type = activity_rust_type(c.schemas, p, allow_optionals=False)
opt_ident = 'self.opt.' + arg_ident(p.name)
%>\
% if is_request_value_property(mc, p):
let ${prop_name}: api::${prop_type} = Default::default();
% else:
let ${prop_name}: ${prop_type} = \
% if p.type == 'string':
${opt_ident}.clone();
% else:
match FromStr::from_str(&${opt_ident}) {
Err(perr) => {
err.issues.push(CLIError::ParseError(format!("Failed to parse argument <${mangle_subcommand(p.name)}> as ${p.type} with error: {}", perr)));
Default::default()
},
Ok(v) => v,
};
% endif # handle argument type
% endif # handle request value
% endfor # each required parameter
<%
call_args = list()
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', 'string')
if ptype not in POD_TYPES or ptype == 'string':
borrow = '&'
call_args.append(borrow + mangle_ident(p.name))
# end for each required prop
%>\
let call = self.hub.${mangle_ident(resource)}().${mangle_ident(method)}(${', '.join(call_args)});
## TODO: set parameters
## TODO: parse upload and output information
if dry_run {
None
} else {
## Make the call, handle uploads, handle downloads (also media downloads|json decoding)
## TODO: unify error handling
% if mc.media_params:
return None
% else:
match call.${api.terms.action}() {
Err(api_err) => Some(api_err),
Ok(res) => {
println!("DEBUG: {:?}", res);
None
}
}
% endif
}\
</%def>

View File

@@ -12,6 +12,7 @@
</%block>
#![feature(plugin, exit_status)]
#![plugin(docopt_macros)]
#![allow(unused_variables, unused_imports, dead_code)]
extern crate docopt;
extern crate yup_oauth2 as oauth2;

View File

@@ -95,12 +95,14 @@ impl fmt::Display for ConfigurationError {
#[derive(Debug)]
pub enum CLIError {
Configuration(ConfigurationError),
ParseError(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::Configuration(ref err) => writeln!(f, "Configuration -> {}", err),
CLIError::ParseError(ref desc) => desc.fmt(f),
}
}
}