mirror of
https://github.com/OMGeeky/google-apis-rs.git
synced 2026-01-04 02:20:54 +01:00
fix(CLI): Display for Errors + refactor
* refactored errors into a hierarchy * implemented `Display` trait for all error types, including some 'hierarchy-aware' printing. Fixes #54
This commit is contained in:
@@ -15,6 +15,7 @@ UPLOAD_FLAG = 'u'
|
||||
OUTPUT_FLAG = 'o'
|
||||
VALUE_ARG = 'v'
|
||||
SCOPE_FLAG = 'scope'
|
||||
CONFIG_DIR_FLAG = 'config-dir'
|
||||
|
||||
FILE_ARG = '<file>'
|
||||
MIME_ARG = '<mime>'
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
<%!
|
||||
from util import (put_and, supports_scopes)
|
||||
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,
|
||||
CONFIG_DIR_FLAG)
|
||||
|
||||
v_arg = '<%s>' % VALUE_ARG
|
||||
%>\
|
||||
@@ -63,7 +64,7 @@ Configuration:
|
||||
the user to grant this application permission to use it.
|
||||
If unset, it defaults to the shortest scope url for a particular method.
|
||||
% endif scopes
|
||||
--config-dir <folder>
|
||||
--${CONFIG_DIR_FLAG} <folder>
|
||||
A directory into which we will store our persistent data. Defaults to a user-writable
|
||||
directory that we will create during the first invocation.
|
||||
[default: ${CONFIG_DIR}]
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
%>\
|
||||
<%def name="new(c)">\
|
||||
mod cmn;
|
||||
use cmn::{InvalidOptionsError};
|
||||
use cmn::InvalidOptionsError;
|
||||
|
||||
use oauth2::ApplicationSecret;
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ fn main() {
|
||||
println!("{:?}", opts);
|
||||
match Engine::new(opts) {
|
||||
Err(e) => {
|
||||
write!(io::stderr(), "{:?}", e).ok();
|
||||
write!(io::stderr(), "{}", e).ok();
|
||||
env::set_exit_status(e.exit_code);
|
||||
},
|
||||
Ok(mut engine) => {
|
||||
|
||||
@@ -4,20 +4,70 @@ use rustc_serialize::json;
|
||||
use std::fs;
|
||||
use std::env;
|
||||
use std::io;
|
||||
use std::fmt;
|
||||
use std::path::Path;
|
||||
use std::cell::RefCell;
|
||||
|
||||
use std::io::{Write, Read};
|
||||
|
||||
use std::default::Default;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ArgumentError {
|
||||
ConfigurationDirectoryInaccessible((String, io::Error)),
|
||||
ConfigurationDirectoryUnset,
|
||||
UsernameExpansionFailed(String),
|
||||
pub enum ApplicationSecretError {
|
||||
DecoderError((String, json::DecoderError)),
|
||||
FormatError(String),
|
||||
}
|
||||
|
||||
impl fmt::Display for ApplicationSecretError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
match *self {
|
||||
ApplicationSecretError::DecoderError((ref path, ref err))
|
||||
=> writeln!(f, "Could not decode file at '{}' with error: {}"
|
||||
, path, err),
|
||||
ApplicationSecretError::FormatError(ref path)
|
||||
=> writeln!(f, "'installed' field is unset in secret file at '{}'"
|
||||
, path),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ConfigurationError {
|
||||
DirectoryCreationFailed((String, io::Error)),
|
||||
DirectoryUnset,
|
||||
HomeExpansionFailed(String),
|
||||
Secret(ApplicationSecretError),
|
||||
IOError((String, io::Error)),
|
||||
SecretDecoderError((String, json::DecoderError)),
|
||||
SecretFormatError(String),
|
||||
}
|
||||
|
||||
impl fmt::Display for ConfigurationError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
match *self {
|
||||
ConfigurationError::DirectoryCreationFailed((ref dir, ref err))
|
||||
=> writeln!(f, "Directory '{}' could not be created with error: {}", dir, err),
|
||||
ConfigurationError::DirectoryUnset
|
||||
=> writeln!(f, "--config-dir was unset or empty"),
|
||||
ConfigurationError::HomeExpansionFailed(ref dir)
|
||||
=> writeln!(f, "Couldn't find HOME directory of current user, failed to expand '{}'", dir),
|
||||
ConfigurationError::Secret(ref err)
|
||||
=> writeln!(f, "Secret -> {}", err),
|
||||
ConfigurationError::IOError((ref path, ref err))
|
||||
=> writeln!(f, "IO operation failed on path '{}' with error: {}", path, err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ArgumentError {
|
||||
Configuration(ConfigurationError),
|
||||
}
|
||||
|
||||
impl fmt::Display for ArgumentError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
match *self {
|
||||
ArgumentError::Configuration(ref err) => writeln!(f, "Configuration -> {}", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -26,6 +76,15 @@ pub struct InvalidOptionsError {
|
||||
pub exit_code: i32,
|
||||
}
|
||||
|
||||
impl fmt::Display for InvalidOptionsError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
for issue in &self.issues {
|
||||
try!(issue.fmt(f));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl InvalidOptionsError {
|
||||
pub fn single(err: ArgumentError, exit_code: i32) -> InvalidOptionsError {
|
||||
InvalidOptionsError {
|
||||
@@ -38,13 +97,13 @@ impl InvalidOptionsError {
|
||||
pub fn assure_config_dir_exists(dir: &str) -> Result<String, ArgumentError> {
|
||||
let trdir = dir.trim();
|
||||
if trdir.len() == 0 {
|
||||
return Err(ArgumentError::ConfigurationDirectoryUnset)
|
||||
return Err(ArgumentError::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::UsernameExpansionFailed(trdir.to_string())),
|
||||
None => return Err(ArgumentError::Configuration(ConfigurationError::HomeExpansionFailed(trdir.to_string()))),
|
||||
Some(mut user) => {
|
||||
user.push_str(&trdir[1..]);
|
||||
user
|
||||
@@ -56,7 +115,8 @@ 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::ConfigurationDirectoryInaccessible((expanded_config_dir, err)))
|
||||
return Err(ArgumentError::Configuration(
|
||||
ConfigurationError::DirectoryCreationFailed((expanded_config_dir, err))))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,15 +127,15 @@ pub fn application_secret_from_directory(dir: &str, secret_basename: &str) -> Re
|
||||
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::IOError(
|
||||
Err(ArgumentError::Configuration(ConfigurationError::IOError(
|
||||
(secret_str(), io_err)
|
||||
))
|
||||
)))
|
||||
};
|
||||
|
||||
for _ in 0..2 {
|
||||
match fs::File::open(&secret_path) {
|
||||
Err(mut e) => {
|
||||
if e.kind() == io::ErrorKind::NotFound {
|
||||
Err(mut err) => {
|
||||
if err.kind() == io::ErrorKind::NotFound {
|
||||
// Write our built-in one - user may adjust the written file at will
|
||||
let secret = ApplicationSecret {
|
||||
client_id: "14070749909-vgip2f1okm7bkvajhi9jugan6126io9v.apps.googleusercontent.com".to_string(),
|
||||
@@ -94,7 +154,7 @@ pub fn application_secret_from_directory(dir: &str, secret_basename: &str) -> Re
|
||||
};
|
||||
|
||||
let json_enocded_secret = json::encode(&app_secret).unwrap();
|
||||
e = match fs::OpenOptions::new().create(true).write(true).open(&secret_path) {
|
||||
err = match fs::OpenOptions::new().create(true).write(true).open(&secret_path) {
|
||||
Err(cfe) => cfe,
|
||||
Ok(mut f) => {
|
||||
match f.write(json_enocded_secret.as_bytes()) {
|
||||
@@ -105,7 +165,7 @@ pub fn application_secret_from_directory(dir: &str, secret_basename: &str) -> Re
|
||||
};
|
||||
// fall through to IO error handling
|
||||
}
|
||||
return secret_io_error(e)
|
||||
return secret_io_error(err)
|
||||
},
|
||||
Ok(mut f) => {
|
||||
let mut json_encoded_secret = String::new();
|
||||
@@ -113,12 +173,17 @@ 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::SecretDecoderError(
|
||||
Err(json_decode_error) => return Err(ArgumentError::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::SecretFormatError(secret_str()))
|
||||
None => return Err(
|
||||
ArgumentError::Configuration(
|
||||
ConfigurationError::Secret(
|
||||
ApplicationSecretError::FormatError(secret_str())
|
||||
)))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user