From 9eed4056e53d71f2b8165fd4099fda6fc0d0798a Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Wed, 15 Apr 2015 17:23:01 +0200 Subject: [PATCH] feat(CLI): upload flag parsing We handle errors gracefully with costum types and minimal amount of code. Unfortunately, Mime type parsing is very 'flexible', allowing nonesense types to be passed easily. Related to #62 --- src/mako/cli/lib/engine.mako | 22 +++++++++++++++++-- src/mako/cli/main.rs.mako | 6 +++--- src/rust/cli/cmn.rs | 42 +++++++++++++++++++++++++++++++++++- 3 files changed, 64 insertions(+), 6 deletions(-) diff --git a/src/mako/cli/lib/engine.mako b/src/mako/cli/lib/engine.mako index 83fb103075..c6708daf10 100644 --- a/src/mako/cli/lib/engine.mako +++ b/src/mako/cli/lib/engine.mako @@ -22,7 +22,9 @@ hub_type_name = 'api::' + hub_type(c.schemas, util.canonical_name()) %>\ mod cmn; -use cmn::{InvalidOptionsError, CLIError, JsonTokenStorage, arg_from_str, writer_from_opts, parse_kv_arg}; +use cmn::{InvalidOptionsError, CLIError, JsonTokenStorage, arg_from_str, writer_from_opts, parse_kv_arg, + input_file_from_opts, input_mime_from_opts}; + use std::default::Default; use std::str::FromStr; @@ -227,7 +229,23 @@ ${value_unwrap}\ } } % endif # handle call parameters -## TODO: parse upload +% if mc.media_params: +let protocol = +% for p in mc.media_params: + % if loop.first: + if \ + % else: + } else if \ + % endif +${SOPT + cmd_ident(p.protocol)} { + "${p.protocol}" +% endfor # each media param + } else { + unreachable!() + }; +let mut input_file = input_file_from_opts(&${SOPT + arg_ident(FILE_ARG[1:-1])}, err); +let mime_type = input_mime_from_opts(&${SOPT + arg_ident(MIME_ARG[1:-1])}, err); +% endif # support upload if dry_run { None } else { diff --git a/src/mako/cli/main.rs.mako b/src/mako/cli/main.rs.mako index 94674fcbd0..6ff5cf729b 100644 --- a/src/mako/cli/main.rs.mako +++ b/src/mako/cli/main.rs.mako @@ -12,18 +12,18 @@ #![feature(plugin, exit_status)] #![plugin(docopt_macros)] -#![allow(unused_variables, unused_imports, dead_code, unsed_mut)] +#![allow(unused_variables, unused_imports, dead_code, unused_mut)] extern crate docopt; extern crate yup_oauth2 as oauth2; extern crate rustc_serialize; extern crate serde; extern crate hyper; +extern crate mime; 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; -use std::io::Write; +use std::io::{self, Write}; ${docopt.new(c)}\ diff --git a/src/rust/cli/cmn.rs b/src/rust/cli/cmn.rs index 771c028440..64e91d6287 100644 --- a/src/rust/cli/cmn.rs +++ b/src/rust/cli/cmn.rs @@ -1,5 +1,6 @@ use oauth2::{ApplicationSecret, ConsoleApplicationSecret, TokenStorage, Token}; use rustc_serialize::json; +use mime::Mime; use std::fs; use std::env; @@ -31,6 +32,26 @@ pub fn parse_kv_arg<'a>(kv: &'a str, err: &mut InvalidOptionsError) } } +pub fn input_file_from_opts(file_path: &str, err: &mut InvalidOptionsError) -> Option { + match fs::File::open(file_path) { + Ok(f) => Some(f), + Err(io_err) => { + err.issues.push(CLIError::Input(InputError::IOError((file_path.to_string(), io_err)))); + None + } + } +} + +pub fn input_mime_from_opts(mime: &str, err: &mut InvalidOptionsError) -> Option { + match mime.parse() { + Ok(m) => Some(m), + Err(_) => { + err.issues.push(CLIError::Input(InputError::Mime(mime.to_string()))); + None + } + } +} + // 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 { @@ -138,18 +159,37 @@ impl fmt::Display for ConfigurationError { } } +#[derive(Debug)] +pub enum InputError { + IOError((String, io::Error)), + Mime(String), +} + +impl fmt::Display for InputError { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + match *self { + InputError::IOError((ref file_path, ref io_err)) + => writeln!(f, "Failed to open '{}' for reading with error: {}", file_path, io_err), + InputError::Mime(ref mime) + => writeln!(f, "'{}' is not a known mime-type", mime), + } + } +} + #[derive(Debug)] pub enum CLIError { Configuration(ConfigurationError), ParseError((&'static str, &'static str, String, String)), UnknownParameter(String), InvalidKeyValueSyntax(String), + Input(InputError), } 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) => write!(f, "Configuration -> {}", err), + CLIError::Input(ref err) => write!(f, "Input -> {}", err), 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),