From be228f19940d38e484809116c1bd84bb8edf5ee8 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Tue, 14 Apr 2015 09:32:29 +0200 Subject: [PATCH] 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 --- src/mako/cli/lib/cli.py | 3 +++ src/mako/cli/lib/engine.mako | 44 ++++++++++++++++++++++++++++++++---- src/mako/cli/main.rs.mako | 16 ++++++++----- src/rust/api/cmn.rs | 2 +- src/rust/cli/cmn.rs | 26 ++++++++++----------- 5 files changed, 66 insertions(+), 25 deletions(-) diff --git a/src/mako/cli/lib/cli.py b/src/mako/cli/lib/cli.py index b92074c0bf..2c6e37a628 100644 --- a/src/mako/cli/lib/cli.py +++ b/src/mako/cli/lib/cli.py @@ -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): diff --git a/src/mako/cli/lib/engine.mako b/src/mako/cli/lib/engine.mako index 23278d9772..055a22fe21 100644 --- a/src/mako/cli/lib/engine.mako +++ b/src/mako/cli/lib/engine.mako @@ -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 { + // Please note that this call will fail if any part of the opt can't be handled + fn new(opt: Options) -> Result { 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 { + None + } } \ No newline at end of file diff --git a/src/mako/cli/main.rs.mako b/src/mako/cli/main.rs.mako index 7630ea3d43..96d6a1eb26 100644 --- a/src/mako/cli/main.rs.mako +++ b/src/mako/cli/main.rs.mako @@ -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); + } } } } \ No newline at end of file diff --git a/src/rust/api/cmn.rs b/src/rust/api/cmn.rs index dbef7eabc4..8a872adc32 100644 --- a/src/rust/api/cmn.rs +++ b/src/rust/api/cmn.rs @@ -215,7 +215,7 @@ pub struct DefaultDelegate; impl Delegate for DefaultDelegate {} - +#[derive(Debug)] pub enum Error { /// The http connection failed HttpError(hyper::HttpError), diff --git a/src/rust/cli/cmn.rs b/src/rust/cli/cmn.rs index 7a570965e2..cb68e46490 100644 --- a/src/rust/cli/cmn.rs +++ b/src/rust/cli/cmn.rs @@ -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, + pub issues: Vec, 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 { +pub fn assure_config_dir_exists(dir: &str) -> Result { 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 { 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 { Ok(expanded_config_dir) } -pub fn application_secret_from_directory(dir: &str, secret_basename: &str) -> Result { +pub fn application_secret_from_directory(dir: &str, secret_basename: &str) -> Result { 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::(&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()) )))