feat(CLI): engine checks resource and method args

We are now at a spot where we can actually start parsing arguments.

* ArgumentError -> ClIError - seems more fitting

Fixes #55
This commit is contained in:
Sebastian Thiel
2015-04-14 09:32:29 +02:00
parent e45eb053d5
commit be228f1994
5 changed files with 66 additions and 25 deletions

View File

@@ -74,6 +74,9 @@ def is_request_value_property(mc, p):
def mangle_subcommand(name):
return util.camel_to_under(name).replace('_', '-').replace('.', '-')
# return the identifier of a command for the given name, suitable to address the command field in the docopt structure
def cmd_ident(name):
return 'cmd_' + mangle_subcommand(name).replace('-', '_')
# transform the resource name into a suitable filename to contain the markdown documentation for it
def subcommand_md_filename(resource, method):

View File

@@ -1,7 +1,8 @@
<%namespace name="util" file="../../lib/util.mako"/>\
<%!
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)
CONFIG_DIR, SCOPE_FLAG, is_request_value_property, FIELD_SEP, docopt_mode, FILE_ARG, MIME_ARG, OUT_ARG,
cmd_ident)
v_arg = '<%s>' % VALUE_ARG
%>\
@@ -12,16 +13,16 @@ use cmn::InvalidOptionsError;
use oauth2::ApplicationSecret;
struct Engine {
opts: Options,
config_dir: String,
secret: ApplicationSecret,
}
impl Engine {
fn new(options: Options) -> Result<Engine, InvalidOptionsError> {
// Please note that this call will fail if any part of the opt can't be handled
fn new(opt: Options) -> Result<Engine, InvalidOptionsError> {
let (config_dir, secret) = {
let config_dir = match cmn::assure_config_dir_exists(&options.flag_config_dir) {
let config_dir = match cmn::assure_config_dir_exists(&opt.flag_config_dir) {
Err(e) => return Err(InvalidOptionsError::single(e, 3)),
Ok(p) => p,
};
@@ -32,13 +33,46 @@ impl Engine {
}
};
## RESOURCE LOOP: check for set primary subcommand
% for resource in sorted(c.rta_map.keys()):
% if loop.first:
if \
% else:
else if \
% endif
opt.${cmd_ident(resource)} {
## METHOD LOOP: Check for method subcommand
% for method in sorted(c.rta_map[resource]):
% if loop.first:
if \
% else:
else if \
% endif
opt.${cmd_ident(method)} {
}\
% endfor # each method
else {
unreachable!();
}
}\
% endfor # each resource
else {
unreachable!();
}
let mut engine = Engine {
opts: options,
config_dir: config_dir,
secret: secret,
};
Ok(engine)
}
// Execute the call with all the bells and whistles, informing the caller only if there was an error.
// The absense of one indicates success.
fn doit(&self) -> Option<api::Error> {
None
}
}
</%def>

View File

@@ -2,7 +2,7 @@
<%namespace name="engine" file="lib/engine.mako"/>\
<%namespace name="util" file="../lib/util.mako"/>\
<%
from util import (new_context, rust_comment)
from util import (new_context, rust_comment, to_extern_crate_name, library_to_crate_name, library_name)
c = new_context(schemas, resources, context.get('methods'))
default_user_agent = "google-cli-rust-client/" + cargo.build_version
@@ -16,6 +16,7 @@
extern crate docopt;
extern crate yup_oauth2 as oauth2;
extern crate rustc_serialize;
extern crate ${to_extern_crate_name(library_to_crate_name(library_name(name, version), make.depends_on_suffix))} as api;
use std::io;
use std::env;
@@ -29,12 +30,15 @@ fn main() {
let opts: Options = Options::docopt().decode().unwrap_or_else(|e| e.exit());
println!("{:?}", opts);
match Engine::new(opts) {
Err(e) => {
write!(io::stderr(), "{}", e).ok();
env::set_exit_status(e.exit_code);
Err(err) => {
write!(io::stderr(), "{}", err).ok();
env::set_exit_status(err.exit_code);
},
Ok(mut engine) => {
Ok(engine) => {
if let Some(err) = engine.doit() {
write!(io::stderr(), "TODO: display {:?}", err).ok();
env::set_exit_status(1);
}
}
}
}

View File

@@ -215,7 +215,7 @@ pub struct DefaultDelegate;
impl Delegate for DefaultDelegate {}
#[derive(Debug)]
pub enum Error {
/// The http connection failed
HttpError(hyper::HttpError),

View File

@@ -58,21 +58,21 @@ impl fmt::Display for ConfigurationError {
}
#[derive(Debug)]
pub enum ArgumentError {
pub enum CLIError {
Configuration(ConfigurationError),
}
impl fmt::Display for ArgumentError {
impl fmt::Display for CLIError {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
match *self {
ArgumentError::Configuration(ref err) => writeln!(f, "Configuration -> {}", err)
CLIError::Configuration(ref err) => writeln!(f, "Configuration -> {}", err)
}
}
}
#[derive(Debug)]
pub struct InvalidOptionsError {
pub issues: Vec<ArgumentError>,
pub issues: Vec<CLIError>,
pub exit_code: i32,
}
@@ -86,7 +86,7 @@ impl fmt::Display for InvalidOptionsError {
}
impl InvalidOptionsError {
pub fn single(err: ArgumentError, exit_code: i32) -> InvalidOptionsError {
pub fn single(err: CLIError, exit_code: i32) -> InvalidOptionsError {
InvalidOptionsError {
issues: vec![err],
exit_code: exit_code,
@@ -94,16 +94,16 @@ impl InvalidOptionsError {
}
}
pub fn assure_config_dir_exists(dir: &str) -> Result<String, ArgumentError> {
pub fn assure_config_dir_exists(dir: &str) -> Result<String, CLIError> {
let trdir = dir.trim();
if trdir.len() == 0 {
return Err(ArgumentError::Configuration(ConfigurationError::DirectoryUnset))
return Err(CLIError::Configuration(ConfigurationError::DirectoryUnset))
}
let expanded_config_dir =
if trdir.as_bytes()[0] == b'~' {
match env::var("HOME").ok().or(env::var("UserProfile").ok()) {
None => return Err(ArgumentError::Configuration(ConfigurationError::HomeExpansionFailed(trdir.to_string()))),
None => return Err(CLIError::Configuration(ConfigurationError::HomeExpansionFailed(trdir.to_string()))),
Some(mut user) => {
user.push_str(&trdir[1..]);
user
@@ -115,7 +115,7 @@ pub fn assure_config_dir_exists(dir: &str) -> Result<String, ArgumentError> {
if let Err(err) = fs::create_dir(&expanded_config_dir) {
if err.kind() != io::ErrorKind::AlreadyExists {
return Err(ArgumentError::Configuration(
return Err(CLIError::Configuration(
ConfigurationError::DirectoryCreationFailed((expanded_config_dir, err))))
}
}
@@ -123,11 +123,11 @@ pub fn assure_config_dir_exists(dir: &str) -> Result<String, ArgumentError> {
Ok(expanded_config_dir)
}
pub fn application_secret_from_directory(dir: &str, secret_basename: &str) -> Result<ApplicationSecret, ArgumentError> {
pub fn application_secret_from_directory(dir: &str, secret_basename: &str) -> Result<ApplicationSecret, CLIError> {
let secret_path = Path::new(dir).join(secret_basename);
let secret_str = || secret_path.as_path().to_str().unwrap().to_string();
let secret_io_error = |io_err: io::Error| {
Err(ArgumentError::Configuration(ConfigurationError::IOError(
Err(CLIError::Configuration(ConfigurationError::IOError(
(secret_str(), io_err)
)))
};
@@ -173,14 +173,14 @@ pub fn application_secret_from_directory(dir: &str, secret_basename: &str) -> Re
return secret_io_error(io_err)
}
match json::decode::<ConsoleApplicationSecret>(&json_encoded_secret) {
Err(json_decode_error) => return Err(ArgumentError::Configuration(
Err(json_decode_error) => return Err(CLIError::Configuration(
ConfigurationError::Secret(ApplicationSecretError::DecoderError(
(secret_str(), json_decode_error)
)))),
Ok(console_secret) => match console_secret.installed {
Some(secret) => return Ok(secret),
None => return Err(
ArgumentError::Configuration(
CLIError::Configuration(
ConfigurationError::Secret(
ApplicationSecretError::FormatError(secret_str())
)))