mirror of
https://github.com/OMGeeky/google-apis-rs.git
synced 2026-02-23 15:49:49 +01:00
feat(API): improved error handling
We are now able to decode detailed errors and pass them on. This allows the CLI to provide more useful error responses. Additionally, the CLI will only print debug responses in --debug mode. Fixes #82
This commit is contained in:
31
gen/admin1_directory-cli/Cargo.toml
Normal file
31
gen/admin1_directory-cli/Cargo.toml
Normal file
@@ -0,0 +1,31 @@
|
||||
# DO NOT EDIT !
|
||||
# This file was generated automatically from 'src/mako/Cargo.toml.mako'
|
||||
# DO NOT EDIT !
|
||||
[package]
|
||||
|
||||
name = "google-admin1_directory-cli"
|
||||
version = "0.0.1+20150314"
|
||||
authors = ["Sebastian Thiel <byronimo@gmail.com>"]
|
||||
description = "A complete library to interact with directory (protocol directory_v1)"
|
||||
repository = "https://github.com/Byron/google-apis-rs/tree/master/gen/admin1_directory-cli"
|
||||
homepage = "https://developers.google.com/admin-sdk/directory/"
|
||||
documentation = "http://byron.github.io/google-apis-rs/google_admin1_directory_cli"
|
||||
license = "MIT"
|
||||
keywords = ["admin", "google", "cli"]
|
||||
|
||||
[[bin]]
|
||||
name = "admin1-directory"
|
||||
|
||||
[dependencies]
|
||||
hyper = "*"
|
||||
mime = "*"
|
||||
yup-oauth2 = "*"
|
||||
docopt = "*"
|
||||
docopt_macros = "*"
|
||||
rustc-serialize = "*"
|
||||
yup-hyper-mock = "*"
|
||||
serde = ">= 0.3.0"
|
||||
serde_macros = "*"
|
||||
|
||||
[dependencies.google-admin1_directory]
|
||||
path = "../admin1_directory"
|
||||
30
gen/admin1_directory-cli/LICENSE.md
Normal file
30
gen/admin1_directory-cli/LICENSE.md
Normal file
@@ -0,0 +1,30 @@
|
||||
<!---
|
||||
DO NOT EDIT !
|
||||
This file was generated automatically from 'src/mako/LICENSE.md.mako'
|
||||
DO NOT EDIT !
|
||||
-->
|
||||
The MIT License (MIT)
|
||||
=====================
|
||||
|
||||
Copyright © `2015` `Sebastian Thiel`
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the “Software”), to deal in the Software without
|
||||
restriction, including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
4
gen/admin1_directory-cli/README.md
Normal file
4
gen/admin1_directory-cli/README.md
Normal file
@@ -0,0 +1,4 @@
|
||||
# HELLO ADMIN:DIRECTORY_V1
|
||||
|
||||
|
||||
Include information about application secret files, and how we automatically write a default one.
|
||||
83
gen/admin1_directory-cli/mkdocs.yml
Normal file
83
gen/admin1_directory-cli/mkdocs.yml
Normal file
@@ -0,0 +1,83 @@
|
||||
site_name: directory v0.0.1+20150314
|
||||
site_url: http://byron.github.io/google-apis-rs/google-admin1_directory-cli
|
||||
site_description: Write integrating applications with bcore
|
||||
|
||||
repo_url: https://github.com/Byron/google-apis-rs/tree/master/gen/admin1_directory-cli
|
||||
|
||||
docs_dir: docs
|
||||
site_dir: build_html
|
||||
|
||||
pages:
|
||||
- ['index.md', 'Home']
|
||||
- ['asps_delete.md', 'Asps', 'Delete']
|
||||
- ['asps_get.md', 'Asps', 'Get']
|
||||
- ['asps_list.md', 'Asps', 'List']
|
||||
- ['channels_stop.md', 'Channels', 'Stop']
|
||||
- ['chromeosdevices_get.md', 'Chromeosdevices', 'Get']
|
||||
- ['chromeosdevices_list.md', 'Chromeosdevices', 'List']
|
||||
- ['chromeosdevices_patch.md', 'Chromeosdevices', 'Patch']
|
||||
- ['chromeosdevices_update.md', 'Chromeosdevices', 'Update']
|
||||
- ['groups_aliases-delete.md', 'Groups', 'Aliases Delete']
|
||||
- ['groups_aliases-insert.md', 'Groups', 'Aliases Insert']
|
||||
- ['groups_aliases-list.md', 'Groups', 'Aliases List']
|
||||
- ['groups_delete.md', 'Groups', 'Delete']
|
||||
- ['groups_get.md', 'Groups', 'Get']
|
||||
- ['groups_insert.md', 'Groups', 'Insert']
|
||||
- ['groups_list.md', 'Groups', 'List']
|
||||
- ['groups_patch.md', 'Groups', 'Patch']
|
||||
- ['groups_update.md', 'Groups', 'Update']
|
||||
- ['members_delete.md', 'Members', 'Delete']
|
||||
- ['members_get.md', 'Members', 'Get']
|
||||
- ['members_insert.md', 'Members', 'Insert']
|
||||
- ['members_list.md', 'Members', 'List']
|
||||
- ['members_patch.md', 'Members', 'Patch']
|
||||
- ['members_update.md', 'Members', 'Update']
|
||||
- ['mobiledevices_action.md', 'Mobiledevices', 'Action']
|
||||
- ['mobiledevices_delete.md', 'Mobiledevices', 'Delete']
|
||||
- ['mobiledevices_get.md', 'Mobiledevices', 'Get']
|
||||
- ['mobiledevices_list.md', 'Mobiledevices', 'List']
|
||||
- ['notifications_delete.md', 'Notifications', 'Delete']
|
||||
- ['notifications_get.md', 'Notifications', 'Get']
|
||||
- ['notifications_list.md', 'Notifications', 'List']
|
||||
- ['notifications_patch.md', 'Notifications', 'Patch']
|
||||
- ['notifications_update.md', 'Notifications', 'Update']
|
||||
- ['orgunits_delete.md', 'Orgunits', 'Delete']
|
||||
- ['orgunits_get.md', 'Orgunits', 'Get']
|
||||
- ['orgunits_insert.md', 'Orgunits', 'Insert']
|
||||
- ['orgunits_list.md', 'Orgunits', 'List']
|
||||
- ['orgunits_patch.md', 'Orgunits', 'Patch']
|
||||
- ['orgunits_update.md', 'Orgunits', 'Update']
|
||||
- ['schemas_delete.md', 'Schemas', 'Delete']
|
||||
- ['schemas_get.md', 'Schemas', 'Get']
|
||||
- ['schemas_insert.md', 'Schemas', 'Insert']
|
||||
- ['schemas_list.md', 'Schemas', 'List']
|
||||
- ['schemas_patch.md', 'Schemas', 'Patch']
|
||||
- ['schemas_update.md', 'Schemas', 'Update']
|
||||
- ['tokens_delete.md', 'Tokens', 'Delete']
|
||||
- ['tokens_get.md', 'Tokens', 'Get']
|
||||
- ['tokens_list.md', 'Tokens', 'List']
|
||||
- ['users_aliases-delete.md', 'Users', 'Aliases Delete']
|
||||
- ['users_aliases-insert.md', 'Users', 'Aliases Insert']
|
||||
- ['users_aliases-list.md', 'Users', 'Aliases List']
|
||||
- ['users_aliases-watch.md', 'Users', 'Aliases Watch']
|
||||
- ['users_delete.md', 'Users', 'Delete']
|
||||
- ['users_get.md', 'Users', 'Get']
|
||||
- ['users_insert.md', 'Users', 'Insert']
|
||||
- ['users_list.md', 'Users', 'List']
|
||||
- ['users_make-admin.md', 'Users', 'Make Admin']
|
||||
- ['users_patch.md', 'Users', 'Patch']
|
||||
- ['users_photos-delete.md', 'Users', 'Photos Delete']
|
||||
- ['users_photos-get.md', 'Users', 'Photos Get']
|
||||
- ['users_photos-patch.md', 'Users', 'Photos Patch']
|
||||
- ['users_photos-update.md', 'Users', 'Photos Update']
|
||||
- ['users_undelete.md', 'Users', 'Undelete']
|
||||
- ['users_update.md', 'Users', 'Update']
|
||||
- ['users_watch.md', 'Users', 'Watch']
|
||||
- ['verification-codes_generate.md', 'Verification Codes', 'Generate']
|
||||
- ['verification-codes_invalidate.md', 'Verification Codes', 'Invalidate']
|
||||
- ['verification-codes_list.md', 'Verification Codes', 'List']
|
||||
|
||||
theme: readthedocs
|
||||
|
||||
copyright: Copyright © 2015, `Sebastian Thiel`
|
||||
|
||||
454
gen/admin1_directory-cli/src/cmn.rs
Normal file
454
gen/admin1_directory-cli/src/cmn.rs
Normal file
@@ -0,0 +1,454 @@
|
||||
// COPY OF 'src/rust/cli/cmn.rs'
|
||||
// DO NOT EDIT
|
||||
use oauth2::{ApplicationSecret, ConsoleApplicationSecret, TokenStorage, Token};
|
||||
use rustc_serialize::json;
|
||||
use mime::Mime;
|
||||
|
||||
use std::fs;
|
||||
use std::env;
|
||||
use std::io;
|
||||
use std::fmt;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::str::FromStr;
|
||||
use std::string::ToString;
|
||||
use std::io::{Write, Read, stdout};
|
||||
|
||||
use std::default::Default;
|
||||
|
||||
const FIELD_SEP: char = '.';
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct FieldCursor(Vec<String>);
|
||||
|
||||
impl ToString for FieldCursor {
|
||||
fn to_string(&self) -> String {
|
||||
self.0.connect(".")
|
||||
}
|
||||
}
|
||||
|
||||
impl FieldCursor {
|
||||
pub fn set(&mut self, value: &str) -> Result<(), CLIError> {
|
||||
if value.len() == 0 {
|
||||
return Err(CLIError::Field(FieldError::Empty))
|
||||
}
|
||||
|
||||
let mut first_is_field_sep = false;
|
||||
let mut char_count: usize = 0;
|
||||
let mut last_c = FIELD_SEP;
|
||||
let mut num_conscutive_field_seps = 0;
|
||||
|
||||
let mut field = String::new();
|
||||
let mut fields = self.0.clone();
|
||||
|
||||
let push_field = |fs: &mut Vec<String>, f: &mut String| {
|
||||
if f.len() > 0 {
|
||||
fs.push(f.clone());
|
||||
f.truncate(0);
|
||||
}
|
||||
};
|
||||
|
||||
for (cid, c) in value.chars().enumerate() {
|
||||
char_count += 1;
|
||||
|
||||
if c == FIELD_SEP {
|
||||
if cid == 0 {
|
||||
first_is_field_sep = true;
|
||||
}
|
||||
num_conscutive_field_seps += 1;
|
||||
if cid > 0 && last_c == FIELD_SEP {
|
||||
if fields.pop().is_none() {
|
||||
return Err(CLIError::Field(FieldError::PopOnEmpty(value.to_string())))
|
||||
}
|
||||
} else {
|
||||
push_field(&mut fields, &mut field);
|
||||
}
|
||||
} else {
|
||||
num_conscutive_field_seps = 0;
|
||||
if cid == 1 {
|
||||
if first_is_field_sep {
|
||||
fields.truncate(0);
|
||||
}
|
||||
}
|
||||
field.push(c);
|
||||
}
|
||||
|
||||
last_c = c;
|
||||
}
|
||||
|
||||
push_field(&mut fields, &mut field);
|
||||
|
||||
if char_count == 1 && first_is_field_sep {
|
||||
fields.truncate(0);
|
||||
}
|
||||
if char_count > 1 && num_conscutive_field_seps == 1 {
|
||||
return Err(CLIError::Field(FieldError::TrailingFieldSep(value.to_string())))
|
||||
}
|
||||
|
||||
self.0 = fields;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn num_fields(&self) -> usize {
|
||||
self.0.len()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_kv_arg<'a>(kv: &'a str, err: &mut InvalidOptionsError, for_hashmap: bool)
|
||||
-> (&'a str, Option<&'a str>) {
|
||||
let mut add_err = || err.issues.push(CLIError::InvalidKeyValueSyntax(kv.to_string(),for_hashmap));
|
||||
match kv.find('=') {
|
||||
None => {
|
||||
add_err();
|
||||
return (kv, None)
|
||||
},
|
||||
Some(pos) => {
|
||||
let key = &kv[..pos];
|
||||
if kv.len() <= pos + 1 {
|
||||
add_err();
|
||||
return (key, None)
|
||||
}
|
||||
(key, Some(&kv[pos+1..]))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn input_file_from_opts(file_path: &str, err: &mut InvalidOptionsError) -> Option<fs::File> {
|
||||
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<Mime> {
|
||||
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<Write> {
|
||||
if !flag || arg == "-" {
|
||||
Box::new(stdout())
|
||||
} else {
|
||||
Box::new(fs::OpenOptions::new().create(true).write(true).open(arg).unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn arg_from_str<T>(arg: &str, err: &mut InvalidOptionsError,
|
||||
arg_name: &'static str,
|
||||
arg_type: &'static str) -> T
|
||||
where T: FromStr + Default,
|
||||
<T as FromStr>::Err: fmt::Display {
|
||||
match FromStr::from_str(arg) {
|
||||
Err(perr) => {
|
||||
err.issues.push(
|
||||
CLIError::ParseError((arg_name, arg_type, arg.to_string(), format!("{}", perr)))
|
||||
);
|
||||
Default::default()
|
||||
},
|
||||
Ok(v) => v,
|
||||
}
|
||||
}
|
||||
|
||||
pub struct JsonTokenStorage {
|
||||
pub program_name: &'static str,
|
||||
pub db_dir: String,
|
||||
}
|
||||
|
||||
impl JsonTokenStorage {
|
||||
fn path(&self, scope_hash: u64) -> PathBuf {
|
||||
Path::new(&self.db_dir).join(&format!("{}-token-{}.json", self.program_name, scope_hash))
|
||||
}
|
||||
}
|
||||
|
||||
impl TokenStorage for JsonTokenStorage {
|
||||
type Error = io::Error;
|
||||
|
||||
// NOTE: logging might be interesting, currently we swallow all errors
|
||||
fn set(&mut self, scope_hash: u64, _: &Vec<&str>, token: Option<Token>) -> Option<io::Error> {
|
||||
match token {
|
||||
None => {
|
||||
match fs::remove_file(self.path(scope_hash)) {
|
||||
Err(err) =>
|
||||
match err.kind() {
|
||||
io::ErrorKind::NotFound => None,
|
||||
_ => Some(err)
|
||||
},
|
||||
Ok(_) => None
|
||||
}
|
||||
}
|
||||
Some(token) => {
|
||||
let json_token = json::encode(&token).unwrap();
|
||||
match fs::OpenOptions::new().create(true).write(true).open(&self.path(scope_hash)) {
|
||||
Ok(mut f) => {
|
||||
match f.write(json_token.as_bytes()) {
|
||||
Ok(_) => None,
|
||||
Err(io_err) => Some(io_err),
|
||||
}
|
||||
},
|
||||
Err(io_err) => Some(io_err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get(&self, scope_hash: u64, _: &Vec<&str>) -> Result<Option<Token>, io::Error> {
|
||||
match fs::File::open(&self.path(scope_hash)) {
|
||||
Ok(mut f) => {
|
||||
let mut json_string = String::new();
|
||||
match f.read_to_string(&mut json_string) {
|
||||
Ok(_) => Ok(Some(json::decode::<Token>(&json_string).unwrap())),
|
||||
Err(io_err) => Err(io_err),
|
||||
}
|
||||
},
|
||||
Err(io_err) => {
|
||||
match io_err.kind() {
|
||||
io::ErrorKind::NotFound => Ok(None),
|
||||
_ => Err(io_err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug)]
|
||||
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)),
|
||||
}
|
||||
|
||||
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 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 FieldError {
|
||||
PopOnEmpty(String),
|
||||
TrailingFieldSep(String),
|
||||
Unknown(String),
|
||||
Empty,
|
||||
}
|
||||
|
||||
|
||||
impl fmt::Display for FieldError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
match *self {
|
||||
FieldError::PopOnEmpty(ref field)
|
||||
=> writeln!(f, "'{}': Cannot move up on empty field cursor", field),
|
||||
FieldError::TrailingFieldSep(ref field)
|
||||
=> writeln!(f, "'{}': Single field separator may not be last character", field),
|
||||
FieldError::Unknown(ref field)
|
||||
=> writeln!(f, "Field '{}' does not exist", field),
|
||||
FieldError::Empty
|
||||
=> writeln!(f, "Field names must not be empty"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum CLIError {
|
||||
Configuration(ConfigurationError),
|
||||
ParseError((&'static str, &'static str, String, String)),
|
||||
UnknownParameter(String),
|
||||
InvalidKeyValueSyntax(String, bool),
|
||||
Input(InputError),
|
||||
Field(FieldError),
|
||||
}
|
||||
|
||||
impl fmt::Display for CLIError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
match *self {
|
||||
CLIError::Configuration(ref err) => write!(f, "Configuration -> {}", err),
|
||||
CLIError::Input(ref err) => write!(f, "Input -> {}", err),
|
||||
CLIError::Field(ref err) => write!(f, "Field -> {}", 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),
|
||||
CLIError::UnknownParameter(ref param_name)
|
||||
=> writeln!(f, "Parameter '{}' is unknown.", param_name),
|
||||
CLIError::InvalidKeyValueSyntax(ref kv, is_hashmap) => {
|
||||
let hashmap_info = if is_hashmap { "hashmap " } else { "" };
|
||||
writeln!(f, "'{}' does not match {}pattern <key>=<value>", kv, hashmap_info)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct InvalidOptionsError {
|
||||
pub issues: Vec<CLIError>,
|
||||
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: CLIError, exit_code: i32) -> InvalidOptionsError {
|
||||
InvalidOptionsError {
|
||||
issues: vec![err],
|
||||
exit_code: exit_code,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new() -> InvalidOptionsError {
|
||||
InvalidOptionsError {
|
||||
issues: Vec::new(),
|
||||
exit_code: 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn assure_config_dir_exists(dir: &str) -> Result<String, CLIError> {
|
||||
let trdir = dir.trim();
|
||||
if trdir.len() == 0 {
|
||||
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(CLIError::Configuration(ConfigurationError::HomeExpansionFailed(trdir.to_string()))),
|
||||
Some(mut user) => {
|
||||
user.push_str(&trdir[1..]);
|
||||
user
|
||||
}
|
||||
}
|
||||
} else {
|
||||
trdir.to_string()
|
||||
};
|
||||
|
||||
if let Err(err) = fs::create_dir(&expanded_config_dir) {
|
||||
if err.kind() != io::ErrorKind::AlreadyExists {
|
||||
return Err(CLIError::Configuration(
|
||||
ConfigurationError::DirectoryCreationFailed((expanded_config_dir, err))))
|
||||
}
|
||||
}
|
||||
|
||||
Ok(expanded_config_dir)
|
||||
}
|
||||
|
||||
pub fn application_secret_from_directory(dir: &str,
|
||||
secret_basename: &str,
|
||||
json_app_secret: &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(CLIError::Configuration(ConfigurationError::IOError(
|
||||
(secret_str(), io_err)
|
||||
)))
|
||||
};
|
||||
|
||||
for _ in 0..2 {
|
||||
match fs::File::open(&secret_path) {
|
||||
Err(mut err) => {
|
||||
if err.kind() == io::ErrorKind::NotFound {
|
||||
// Write our built-in one - user may adjust the written file at will
|
||||
|
||||
err = match fs::OpenOptions::new().create(true).write(true).open(&secret_path) {
|
||||
Err(cfe) => cfe,
|
||||
Ok(mut f) => {
|
||||
match f.write(json_app_secret.as_bytes()) {
|
||||
Err(io_err) => io_err,
|
||||
Ok(_) => continue,
|
||||
}
|
||||
}
|
||||
};
|
||||
// fall through to IO error handling
|
||||
}
|
||||
return secret_io_error(err)
|
||||
},
|
||||
Ok(mut f) => {
|
||||
let mut json_encoded_secret = String::new();
|
||||
if let Err(io_err) = f.read_to_string(&mut json_encoded_secret) {
|
||||
return secret_io_error(io_err)
|
||||
}
|
||||
match json::decode::<ConsoleApplicationSecret>(&json_encoded_secret) {
|
||||
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(
|
||||
CLIError::Configuration(
|
||||
ConfigurationError::Secret(
|
||||
ApplicationSecretError::FormatError(secret_str())
|
||||
)))
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
unreachable!();
|
||||
}
|
||||
4775
gen/admin1_directory-cli/src/main.rs
Normal file
4775
gen/admin1_directory-cli/src/main.rs
Normal file
File diff suppressed because it is too large
Load Diff
31
gen/admin1_reports-cli/Cargo.toml
Normal file
31
gen/admin1_reports-cli/Cargo.toml
Normal file
@@ -0,0 +1,31 @@
|
||||
# DO NOT EDIT !
|
||||
# This file was generated automatically from 'src/mako/Cargo.toml.mako'
|
||||
# DO NOT EDIT !
|
||||
[package]
|
||||
|
||||
name = "google-admin1_reports-cli"
|
||||
version = "0.0.1+20150115"
|
||||
authors = ["Sebastian Thiel <byronimo@gmail.com>"]
|
||||
description = "A complete library to interact with reports (protocol reports_v1)"
|
||||
repository = "https://github.com/Byron/google-apis-rs/tree/master/gen/admin1_reports-cli"
|
||||
homepage = "https://developers.google.com/admin-sdk/reports/"
|
||||
documentation = "http://byron.github.io/google-apis-rs/google_admin1_reports_cli"
|
||||
license = "MIT"
|
||||
keywords = ["admin", "google", "cli"]
|
||||
|
||||
[[bin]]
|
||||
name = "admin1-reports"
|
||||
|
||||
[dependencies]
|
||||
hyper = "*"
|
||||
mime = "*"
|
||||
yup-oauth2 = "*"
|
||||
docopt = "*"
|
||||
docopt_macros = "*"
|
||||
rustc-serialize = "*"
|
||||
yup-hyper-mock = "*"
|
||||
serde = ">= 0.3.0"
|
||||
serde_macros = "*"
|
||||
|
||||
[dependencies.google-admin1_reports]
|
||||
path = "../admin1_reports"
|
||||
30
gen/admin1_reports-cli/LICENSE.md
Normal file
30
gen/admin1_reports-cli/LICENSE.md
Normal file
@@ -0,0 +1,30 @@
|
||||
<!---
|
||||
DO NOT EDIT !
|
||||
This file was generated automatically from 'src/mako/LICENSE.md.mako'
|
||||
DO NOT EDIT !
|
||||
-->
|
||||
The MIT License (MIT)
|
||||
=====================
|
||||
|
||||
Copyright © `2015` `Sebastian Thiel`
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the “Software”), to deal in the Software without
|
||||
restriction, including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
4
gen/admin1_reports-cli/README.md
Normal file
4
gen/admin1_reports-cli/README.md
Normal file
@@ -0,0 +1,4 @@
|
||||
# HELLO ADMIN:REPORTS_V1
|
||||
|
||||
|
||||
Include information about application secret files, and how we automatically write a default one.
|
||||
21
gen/admin1_reports-cli/mkdocs.yml
Normal file
21
gen/admin1_reports-cli/mkdocs.yml
Normal file
@@ -0,0 +1,21 @@
|
||||
site_name: reports v0.0.1+20150115
|
||||
site_url: http://byron.github.io/google-apis-rs/google-admin1_reports-cli
|
||||
site_description: Write integrating applications with bcore
|
||||
|
||||
repo_url: https://github.com/Byron/google-apis-rs/tree/master/gen/admin1_reports-cli
|
||||
|
||||
docs_dir: docs
|
||||
site_dir: build_html
|
||||
|
||||
pages:
|
||||
- ['index.md', 'Home']
|
||||
- ['activities_list.md', 'Activities', 'List']
|
||||
- ['activities_watch.md', 'Activities', 'Watch']
|
||||
- ['channels_stop.md', 'Channels', 'Stop']
|
||||
- ['customer-usage-reports_get.md', 'Customer Usage Reports', 'Get']
|
||||
- ['user-usage-report_get.md', 'User Usage Report', 'Get']
|
||||
|
||||
theme: readthedocs
|
||||
|
||||
copyright: Copyright © 2015, `Sebastian Thiel`
|
||||
|
||||
454
gen/admin1_reports-cli/src/cmn.rs
Normal file
454
gen/admin1_reports-cli/src/cmn.rs
Normal file
@@ -0,0 +1,454 @@
|
||||
// COPY OF 'src/rust/cli/cmn.rs'
|
||||
// DO NOT EDIT
|
||||
use oauth2::{ApplicationSecret, ConsoleApplicationSecret, TokenStorage, Token};
|
||||
use rustc_serialize::json;
|
||||
use mime::Mime;
|
||||
|
||||
use std::fs;
|
||||
use std::env;
|
||||
use std::io;
|
||||
use std::fmt;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::str::FromStr;
|
||||
use std::string::ToString;
|
||||
use std::io::{Write, Read, stdout};
|
||||
|
||||
use std::default::Default;
|
||||
|
||||
const FIELD_SEP: char = '.';
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct FieldCursor(Vec<String>);
|
||||
|
||||
impl ToString for FieldCursor {
|
||||
fn to_string(&self) -> String {
|
||||
self.0.connect(".")
|
||||
}
|
||||
}
|
||||
|
||||
impl FieldCursor {
|
||||
pub fn set(&mut self, value: &str) -> Result<(), CLIError> {
|
||||
if value.len() == 0 {
|
||||
return Err(CLIError::Field(FieldError::Empty))
|
||||
}
|
||||
|
||||
let mut first_is_field_sep = false;
|
||||
let mut char_count: usize = 0;
|
||||
let mut last_c = FIELD_SEP;
|
||||
let mut num_conscutive_field_seps = 0;
|
||||
|
||||
let mut field = String::new();
|
||||
let mut fields = self.0.clone();
|
||||
|
||||
let push_field = |fs: &mut Vec<String>, f: &mut String| {
|
||||
if f.len() > 0 {
|
||||
fs.push(f.clone());
|
||||
f.truncate(0);
|
||||
}
|
||||
};
|
||||
|
||||
for (cid, c) in value.chars().enumerate() {
|
||||
char_count += 1;
|
||||
|
||||
if c == FIELD_SEP {
|
||||
if cid == 0 {
|
||||
first_is_field_sep = true;
|
||||
}
|
||||
num_conscutive_field_seps += 1;
|
||||
if cid > 0 && last_c == FIELD_SEP {
|
||||
if fields.pop().is_none() {
|
||||
return Err(CLIError::Field(FieldError::PopOnEmpty(value.to_string())))
|
||||
}
|
||||
} else {
|
||||
push_field(&mut fields, &mut field);
|
||||
}
|
||||
} else {
|
||||
num_conscutive_field_seps = 0;
|
||||
if cid == 1 {
|
||||
if first_is_field_sep {
|
||||
fields.truncate(0);
|
||||
}
|
||||
}
|
||||
field.push(c);
|
||||
}
|
||||
|
||||
last_c = c;
|
||||
}
|
||||
|
||||
push_field(&mut fields, &mut field);
|
||||
|
||||
if char_count == 1 && first_is_field_sep {
|
||||
fields.truncate(0);
|
||||
}
|
||||
if char_count > 1 && num_conscutive_field_seps == 1 {
|
||||
return Err(CLIError::Field(FieldError::TrailingFieldSep(value.to_string())))
|
||||
}
|
||||
|
||||
self.0 = fields;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn num_fields(&self) -> usize {
|
||||
self.0.len()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_kv_arg<'a>(kv: &'a str, err: &mut InvalidOptionsError, for_hashmap: bool)
|
||||
-> (&'a str, Option<&'a str>) {
|
||||
let mut add_err = || err.issues.push(CLIError::InvalidKeyValueSyntax(kv.to_string(),for_hashmap));
|
||||
match kv.find('=') {
|
||||
None => {
|
||||
add_err();
|
||||
return (kv, None)
|
||||
},
|
||||
Some(pos) => {
|
||||
let key = &kv[..pos];
|
||||
if kv.len() <= pos + 1 {
|
||||
add_err();
|
||||
return (key, None)
|
||||
}
|
||||
(key, Some(&kv[pos+1..]))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn input_file_from_opts(file_path: &str, err: &mut InvalidOptionsError) -> Option<fs::File> {
|
||||
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<Mime> {
|
||||
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<Write> {
|
||||
if !flag || arg == "-" {
|
||||
Box::new(stdout())
|
||||
} else {
|
||||
Box::new(fs::OpenOptions::new().create(true).write(true).open(arg).unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn arg_from_str<T>(arg: &str, err: &mut InvalidOptionsError,
|
||||
arg_name: &'static str,
|
||||
arg_type: &'static str) -> T
|
||||
where T: FromStr + Default,
|
||||
<T as FromStr>::Err: fmt::Display {
|
||||
match FromStr::from_str(arg) {
|
||||
Err(perr) => {
|
||||
err.issues.push(
|
||||
CLIError::ParseError((arg_name, arg_type, arg.to_string(), format!("{}", perr)))
|
||||
);
|
||||
Default::default()
|
||||
},
|
||||
Ok(v) => v,
|
||||
}
|
||||
}
|
||||
|
||||
pub struct JsonTokenStorage {
|
||||
pub program_name: &'static str,
|
||||
pub db_dir: String,
|
||||
}
|
||||
|
||||
impl JsonTokenStorage {
|
||||
fn path(&self, scope_hash: u64) -> PathBuf {
|
||||
Path::new(&self.db_dir).join(&format!("{}-token-{}.json", self.program_name, scope_hash))
|
||||
}
|
||||
}
|
||||
|
||||
impl TokenStorage for JsonTokenStorage {
|
||||
type Error = io::Error;
|
||||
|
||||
// NOTE: logging might be interesting, currently we swallow all errors
|
||||
fn set(&mut self, scope_hash: u64, _: &Vec<&str>, token: Option<Token>) -> Option<io::Error> {
|
||||
match token {
|
||||
None => {
|
||||
match fs::remove_file(self.path(scope_hash)) {
|
||||
Err(err) =>
|
||||
match err.kind() {
|
||||
io::ErrorKind::NotFound => None,
|
||||
_ => Some(err)
|
||||
},
|
||||
Ok(_) => None
|
||||
}
|
||||
}
|
||||
Some(token) => {
|
||||
let json_token = json::encode(&token).unwrap();
|
||||
match fs::OpenOptions::new().create(true).write(true).open(&self.path(scope_hash)) {
|
||||
Ok(mut f) => {
|
||||
match f.write(json_token.as_bytes()) {
|
||||
Ok(_) => None,
|
||||
Err(io_err) => Some(io_err),
|
||||
}
|
||||
},
|
||||
Err(io_err) => Some(io_err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get(&self, scope_hash: u64, _: &Vec<&str>) -> Result<Option<Token>, io::Error> {
|
||||
match fs::File::open(&self.path(scope_hash)) {
|
||||
Ok(mut f) => {
|
||||
let mut json_string = String::new();
|
||||
match f.read_to_string(&mut json_string) {
|
||||
Ok(_) => Ok(Some(json::decode::<Token>(&json_string).unwrap())),
|
||||
Err(io_err) => Err(io_err),
|
||||
}
|
||||
},
|
||||
Err(io_err) => {
|
||||
match io_err.kind() {
|
||||
io::ErrorKind::NotFound => Ok(None),
|
||||
_ => Err(io_err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug)]
|
||||
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)),
|
||||
}
|
||||
|
||||
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 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 FieldError {
|
||||
PopOnEmpty(String),
|
||||
TrailingFieldSep(String),
|
||||
Unknown(String),
|
||||
Empty,
|
||||
}
|
||||
|
||||
|
||||
impl fmt::Display for FieldError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
match *self {
|
||||
FieldError::PopOnEmpty(ref field)
|
||||
=> writeln!(f, "'{}': Cannot move up on empty field cursor", field),
|
||||
FieldError::TrailingFieldSep(ref field)
|
||||
=> writeln!(f, "'{}': Single field separator may not be last character", field),
|
||||
FieldError::Unknown(ref field)
|
||||
=> writeln!(f, "Field '{}' does not exist", field),
|
||||
FieldError::Empty
|
||||
=> writeln!(f, "Field names must not be empty"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum CLIError {
|
||||
Configuration(ConfigurationError),
|
||||
ParseError((&'static str, &'static str, String, String)),
|
||||
UnknownParameter(String),
|
||||
InvalidKeyValueSyntax(String, bool),
|
||||
Input(InputError),
|
||||
Field(FieldError),
|
||||
}
|
||||
|
||||
impl fmt::Display for CLIError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
match *self {
|
||||
CLIError::Configuration(ref err) => write!(f, "Configuration -> {}", err),
|
||||
CLIError::Input(ref err) => write!(f, "Input -> {}", err),
|
||||
CLIError::Field(ref err) => write!(f, "Field -> {}", 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),
|
||||
CLIError::UnknownParameter(ref param_name)
|
||||
=> writeln!(f, "Parameter '{}' is unknown.", param_name),
|
||||
CLIError::InvalidKeyValueSyntax(ref kv, is_hashmap) => {
|
||||
let hashmap_info = if is_hashmap { "hashmap " } else { "" };
|
||||
writeln!(f, "'{}' does not match {}pattern <key>=<value>", kv, hashmap_info)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct InvalidOptionsError {
|
||||
pub issues: Vec<CLIError>,
|
||||
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: CLIError, exit_code: i32) -> InvalidOptionsError {
|
||||
InvalidOptionsError {
|
||||
issues: vec![err],
|
||||
exit_code: exit_code,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new() -> InvalidOptionsError {
|
||||
InvalidOptionsError {
|
||||
issues: Vec::new(),
|
||||
exit_code: 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn assure_config_dir_exists(dir: &str) -> Result<String, CLIError> {
|
||||
let trdir = dir.trim();
|
||||
if trdir.len() == 0 {
|
||||
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(CLIError::Configuration(ConfigurationError::HomeExpansionFailed(trdir.to_string()))),
|
||||
Some(mut user) => {
|
||||
user.push_str(&trdir[1..]);
|
||||
user
|
||||
}
|
||||
}
|
||||
} else {
|
||||
trdir.to_string()
|
||||
};
|
||||
|
||||
if let Err(err) = fs::create_dir(&expanded_config_dir) {
|
||||
if err.kind() != io::ErrorKind::AlreadyExists {
|
||||
return Err(CLIError::Configuration(
|
||||
ConfigurationError::DirectoryCreationFailed((expanded_config_dir, err))))
|
||||
}
|
||||
}
|
||||
|
||||
Ok(expanded_config_dir)
|
||||
}
|
||||
|
||||
pub fn application_secret_from_directory(dir: &str,
|
||||
secret_basename: &str,
|
||||
json_app_secret: &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(CLIError::Configuration(ConfigurationError::IOError(
|
||||
(secret_str(), io_err)
|
||||
)))
|
||||
};
|
||||
|
||||
for _ in 0..2 {
|
||||
match fs::File::open(&secret_path) {
|
||||
Err(mut err) => {
|
||||
if err.kind() == io::ErrorKind::NotFound {
|
||||
// Write our built-in one - user may adjust the written file at will
|
||||
|
||||
err = match fs::OpenOptions::new().create(true).write(true).open(&secret_path) {
|
||||
Err(cfe) => cfe,
|
||||
Ok(mut f) => {
|
||||
match f.write(json_app_secret.as_bytes()) {
|
||||
Err(io_err) => io_err,
|
||||
Ok(_) => continue,
|
||||
}
|
||||
}
|
||||
};
|
||||
// fall through to IO error handling
|
||||
}
|
||||
return secret_io_error(err)
|
||||
},
|
||||
Ok(mut f) => {
|
||||
let mut json_encoded_secret = String::new();
|
||||
if let Err(io_err) = f.read_to_string(&mut json_encoded_secret) {
|
||||
return secret_io_error(io_err)
|
||||
}
|
||||
match json::decode::<ConsoleApplicationSecret>(&json_encoded_secret) {
|
||||
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(
|
||||
CLIError::Configuration(
|
||||
ConfigurationError::Secret(
|
||||
ApplicationSecretError::FormatError(secret_str())
|
||||
)))
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
unreachable!();
|
||||
}
|
||||
561
gen/admin1_reports-cli/src/main.rs
Normal file
561
gen/admin1_reports-cli/src/main.rs
Normal file
@@ -0,0 +1,561 @@
|
||||
// DO NOT EDIT !
|
||||
// This file was generated automatically from 'src/mako/cli/main.rs.mako'
|
||||
// DO NOT EDIT !
|
||||
#![feature(plugin, exit_status)]
|
||||
#![plugin(docopt_macros)]
|
||||
#![allow(unused_variables, unused_imports, dead_code, unused_mut)]
|
||||
|
||||
extern crate docopt;
|
||||
extern crate yup_oauth2 as oauth2;
|
||||
extern crate yup_hyper_mock as mock;
|
||||
extern crate rustc_serialize;
|
||||
extern crate serde;
|
||||
extern crate hyper;
|
||||
extern crate mime;
|
||||
extern crate google_admin1_reports as api;
|
||||
|
||||
use std::env;
|
||||
use std::io::{self, Write};
|
||||
|
||||
docopt!(Options derive Debug, "
|
||||
Usage:
|
||||
admin1-reports [options] activities list <user-key> <application-name> [-p <v>]... [-o <out>]
|
||||
admin1-reports [options] activities watch <user-key> <application-name> -r <kv>... [-p <v>]... [-o <out>]
|
||||
admin1-reports [options] channels stop -r <kv>... [-p <v>]...
|
||||
admin1-reports [options] customer-usage-reports get <date> [-p <v>]... [-o <out>]
|
||||
admin1-reports [options] user-usage-report get <user-key> <date> [-p <v>]... [-o <out>]
|
||||
admin1-reports --help
|
||||
|
||||
All documentation details can be found TODO: <URL to github.io docs here, see #51>
|
||||
|
||||
Configuration:
|
||||
--scope <url>
|
||||
Specify the authentication a method should be executed in. Each scope requires
|
||||
the user to grant this application permission to use it.
|
||||
If unset, it defaults to the shortest scope url for a particular method.
|
||||
--config-dir <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: ~/.google-service-cli]
|
||||
--debug
|
||||
Output all server communication to standard error. `tx` and `rx` are placed into
|
||||
the same stream.
|
||||
--debug-auth
|
||||
Output all communication related to authentication to standard error. `tx` and `rx` are placed into
|
||||
the same stream.
|
||||
");
|
||||
|
||||
mod cmn;
|
||||
use cmn::{InvalidOptionsError, CLIError, JsonTokenStorage, arg_from_str, writer_from_opts, parse_kv_arg,
|
||||
input_file_from_opts, input_mime_from_opts, FieldCursor, FieldError};
|
||||
|
||||
use std::default::Default;
|
||||
use std::str::FromStr;
|
||||
|
||||
use oauth2::{Authenticator, DefaultAuthenticatorDelegate};
|
||||
use rustc_serialize::json;
|
||||
|
||||
struct Engine {
|
||||
opt: Options,
|
||||
hub: api::Reports<hyper::Client, Authenticator<DefaultAuthenticatorDelegate, JsonTokenStorage, hyper::Client>>,
|
||||
}
|
||||
|
||||
|
||||
impl Engine {
|
||||
fn _activities_list(&self, dry_run: bool, err: &mut InvalidOptionsError)
|
||||
-> Option<api::Error> {
|
||||
let mut call = self.hub.activities().list(&self.opt.arg_user_key, &self.opt.arg_application_name);
|
||||
for parg in self.opt.arg_v.iter() {
|
||||
let (key, value) = parse_kv_arg(&*parg, err, false);
|
||||
match key {
|
||||
"start-time" => {
|
||||
call = call.start_time(value.unwrap_or(""));
|
||||
},
|
||||
"page-token" => {
|
||||
call = call.page_token(value.unwrap_or(""));
|
||||
},
|
||||
"max-results" => {
|
||||
call = call.max_results(arg_from_str(value.unwrap_or("-0"), err, "max-results", "integer"));
|
||||
},
|
||||
"filters" => {
|
||||
call = call.filters(value.unwrap_or(""));
|
||||
},
|
||||
"event-name" => {
|
||||
call = call.event_name(value.unwrap_or(""));
|
||||
},
|
||||
"end-time" => {
|
||||
call = call.end_time(value.unwrap_or(""));
|
||||
},
|
||||
"customer-id" => {
|
||||
call = call.customer_id(value.unwrap_or(""));
|
||||
},
|
||||
"actor-ip-address" => {
|
||||
call = call.actor_ip_address(value.unwrap_or(""));
|
||||
},
|
||||
"alt"
|
||||
|"fields"
|
||||
|"key"
|
||||
|"oauth-token"
|
||||
|"pretty-print"
|
||||
|"quota-user"
|
||||
|"user-ip" => {
|
||||
let map = [
|
||||
("oauth-token", "oauth_token"),
|
||||
("pretty-print", "prettyPrint"),
|
||||
("quota-user", "quotaUser"),
|
||||
("user-ip", "userIp"),
|
||||
];
|
||||
call = call.param(map.iter().find(|t| t.0 == key).unwrap_or(&("", key)).1, value.unwrap_or("unset"))
|
||||
},
|
||||
_ => err.issues.push(CLIError::UnknownParameter(key.to_string())),
|
||||
}
|
||||
}
|
||||
let protocol = "standard-request";
|
||||
if dry_run {
|
||||
None
|
||||
} else {
|
||||
assert!(err.issues.len() == 0);
|
||||
let mut ostream = writer_from_opts(self.opt.flag_o, &self.opt.arg_out);
|
||||
match match protocol {
|
||||
"standard-request" => call.doit(),
|
||||
_ => unreachable!(),
|
||||
} {
|
||||
Err(api_err) => Some(api_err),
|
||||
Ok((mut response, output_schema)) => {
|
||||
serde::json::to_writer_pretty(&mut ostream, &output_schema).unwrap();
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn _activities_watch(&self, dry_run: bool, err: &mut InvalidOptionsError)
|
||||
-> Option<api::Error> {
|
||||
let mut request = api::Channel::default();
|
||||
let mut call = self.hub.activities().watch(&request, &self.opt.arg_user_key, &self.opt.arg_application_name);
|
||||
for parg in self.opt.arg_v.iter() {
|
||||
let (key, value) = parse_kv_arg(&*parg, err, false);
|
||||
match key {
|
||||
"start-time" => {
|
||||
call = call.start_time(value.unwrap_or(""));
|
||||
},
|
||||
"page-token" => {
|
||||
call = call.page_token(value.unwrap_or(""));
|
||||
},
|
||||
"max-results" => {
|
||||
call = call.max_results(arg_from_str(value.unwrap_or("-0"), err, "max-results", "integer"));
|
||||
},
|
||||
"filters" => {
|
||||
call = call.filters(value.unwrap_or(""));
|
||||
},
|
||||
"event-name" => {
|
||||
call = call.event_name(value.unwrap_or(""));
|
||||
},
|
||||
"end-time" => {
|
||||
call = call.end_time(value.unwrap_or(""));
|
||||
},
|
||||
"customer-id" => {
|
||||
call = call.customer_id(value.unwrap_or(""));
|
||||
},
|
||||
"actor-ip-address" => {
|
||||
call = call.actor_ip_address(value.unwrap_or(""));
|
||||
},
|
||||
"alt"
|
||||
|"fields"
|
||||
|"key"
|
||||
|"oauth-token"
|
||||
|"pretty-print"
|
||||
|"quota-user"
|
||||
|"user-ip" => {
|
||||
let map = [
|
||||
("oauth-token", "oauth_token"),
|
||||
("pretty-print", "prettyPrint"),
|
||||
("quota-user", "quotaUser"),
|
||||
("user-ip", "userIp"),
|
||||
];
|
||||
call = call.param(map.iter().find(|t| t.0 == key).unwrap_or(&("", key)).1, value.unwrap_or("unset"))
|
||||
},
|
||||
_ => err.issues.push(CLIError::UnknownParameter(key.to_string())),
|
||||
}
|
||||
}
|
||||
|
||||
let mut field_name = FieldCursor::default();
|
||||
for kvarg in self.opt.arg_kv.iter() {
|
||||
let (key, value) = parse_kv_arg(&*kvarg, err, false);
|
||||
if let Err(field_err) = field_name.set(&*key) {
|
||||
err.issues.push(field_err);
|
||||
}
|
||||
match &field_name.to_string()[..] {
|
||||
"resource-uri" => {
|
||||
request.resource_uri = Some(value.unwrap_or("").to_string());
|
||||
},
|
||||
"kind" => {
|
||||
request.kind = Some(value.unwrap_or("").to_string());
|
||||
},
|
||||
"resource-id" => {
|
||||
request.resource_id = Some(value.unwrap_or("").to_string());
|
||||
},
|
||||
"payload" => {
|
||||
request.payload = Some(arg_from_str(value.unwrap_or("false"), err, "payload", "boolean"));
|
||||
},
|
||||
"token" => {
|
||||
request.token = Some(value.unwrap_or("").to_string());
|
||||
},
|
||||
"params" => {
|
||||
if request.params.is_none() {
|
||||
request.params = Some(Default::default());
|
||||
}
|
||||
let (key, value) = parse_kv_arg(value.unwrap_or(""), err, true);
|
||||
request.params.as_mut().unwrap().insert(key.to_string(), value.unwrap_or("").to_string());
|
||||
},
|
||||
"expiration" => {
|
||||
request.expiration = Some(value.unwrap_or("").to_string());
|
||||
},
|
||||
"address" => {
|
||||
request.address = Some(value.unwrap_or("").to_string());
|
||||
},
|
||||
"type" => {
|
||||
request.type_ = Some(value.unwrap_or("").to_string());
|
||||
},
|
||||
"id" => {
|
||||
request.id = Some(value.unwrap_or("").to_string());
|
||||
},
|
||||
_ => {
|
||||
err.issues.push(CLIError::Field(FieldError::Unknown(field_name.to_string())));
|
||||
}
|
||||
}
|
||||
}
|
||||
let protocol = "standard-request";
|
||||
if dry_run {
|
||||
None
|
||||
} else {
|
||||
assert!(err.issues.len() == 0);
|
||||
let mut ostream = writer_from_opts(self.opt.flag_o, &self.opt.arg_out);
|
||||
match match protocol {
|
||||
"standard-request" => call.doit(),
|
||||
_ => unreachable!(),
|
||||
} {
|
||||
Err(api_err) => Some(api_err),
|
||||
Ok((mut response, output_schema)) => {
|
||||
serde::json::to_writer_pretty(&mut ostream, &output_schema).unwrap();
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn _channels_stop(&self, dry_run: bool, err: &mut InvalidOptionsError)
|
||||
-> Option<api::Error> {
|
||||
let mut request = api::Channel::default();
|
||||
let mut call = self.hub.channels().stop(&request);
|
||||
for parg in self.opt.arg_v.iter() {
|
||||
let (key, value) = parse_kv_arg(&*parg, err, false);
|
||||
match key {
|
||||
"alt"
|
||||
|"fields"
|
||||
|"key"
|
||||
|"oauth-token"
|
||||
|"pretty-print"
|
||||
|"quota-user"
|
||||
|"user-ip" => {
|
||||
let map = [
|
||||
("oauth-token", "oauth_token"),
|
||||
("pretty-print", "prettyPrint"),
|
||||
("quota-user", "quotaUser"),
|
||||
("user-ip", "userIp"),
|
||||
];
|
||||
call = call.param(map.iter().find(|t| t.0 == key).unwrap_or(&("", key)).1, value.unwrap_or("unset"))
|
||||
},
|
||||
_ => err.issues.push(CLIError::UnknownParameter(key.to_string())),
|
||||
}
|
||||
}
|
||||
|
||||
let mut field_name = FieldCursor::default();
|
||||
for kvarg in self.opt.arg_kv.iter() {
|
||||
let (key, value) = parse_kv_arg(&*kvarg, err, false);
|
||||
if let Err(field_err) = field_name.set(&*key) {
|
||||
err.issues.push(field_err);
|
||||
}
|
||||
match &field_name.to_string()[..] {
|
||||
"resource-uri" => {
|
||||
request.resource_uri = Some(value.unwrap_or("").to_string());
|
||||
},
|
||||
"kind" => {
|
||||
request.kind = Some(value.unwrap_or("").to_string());
|
||||
},
|
||||
"resource-id" => {
|
||||
request.resource_id = Some(value.unwrap_or("").to_string());
|
||||
},
|
||||
"payload" => {
|
||||
request.payload = Some(arg_from_str(value.unwrap_or("false"), err, "payload", "boolean"));
|
||||
},
|
||||
"token" => {
|
||||
request.token = Some(value.unwrap_or("").to_string());
|
||||
},
|
||||
"params" => {
|
||||
if request.params.is_none() {
|
||||
request.params = Some(Default::default());
|
||||
}
|
||||
let (key, value) = parse_kv_arg(value.unwrap_or(""), err, true);
|
||||
request.params.as_mut().unwrap().insert(key.to_string(), value.unwrap_or("").to_string());
|
||||
},
|
||||
"expiration" => {
|
||||
request.expiration = Some(value.unwrap_or("").to_string());
|
||||
},
|
||||
"address" => {
|
||||
request.address = Some(value.unwrap_or("").to_string());
|
||||
},
|
||||
"type" => {
|
||||
request.type_ = Some(value.unwrap_or("").to_string());
|
||||
},
|
||||
"id" => {
|
||||
request.id = Some(value.unwrap_or("").to_string());
|
||||
},
|
||||
_ => {
|
||||
err.issues.push(CLIError::Field(FieldError::Unknown(field_name.to_string())));
|
||||
}
|
||||
}
|
||||
}
|
||||
let protocol = "standard-request";
|
||||
if dry_run {
|
||||
None
|
||||
} else {
|
||||
assert!(err.issues.len() == 0);
|
||||
match match protocol {
|
||||
"standard-request" => call.doit(),
|
||||
_ => unreachable!(),
|
||||
} {
|
||||
Err(api_err) => Some(api_err),
|
||||
Ok(mut response) => {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn _customer_usage_reports_get(&self, dry_run: bool, err: &mut InvalidOptionsError)
|
||||
-> Option<api::Error> {
|
||||
let mut call = self.hub.customer_usage_reports().get(&self.opt.arg_date);
|
||||
for parg in self.opt.arg_v.iter() {
|
||||
let (key, value) = parse_kv_arg(&*parg, err, false);
|
||||
match key {
|
||||
"parameters" => {
|
||||
call = call.parameters(value.unwrap_or(""));
|
||||
},
|
||||
"page-token" => {
|
||||
call = call.page_token(value.unwrap_or(""));
|
||||
},
|
||||
"customer-id" => {
|
||||
call = call.customer_id(value.unwrap_or(""));
|
||||
},
|
||||
"alt"
|
||||
|"fields"
|
||||
|"key"
|
||||
|"oauth-token"
|
||||
|"pretty-print"
|
||||
|"quota-user"
|
||||
|"user-ip" => {
|
||||
let map = [
|
||||
("oauth-token", "oauth_token"),
|
||||
("pretty-print", "prettyPrint"),
|
||||
("quota-user", "quotaUser"),
|
||||
("user-ip", "userIp"),
|
||||
];
|
||||
call = call.param(map.iter().find(|t| t.0 == key).unwrap_or(&("", key)).1, value.unwrap_or("unset"))
|
||||
},
|
||||
_ => err.issues.push(CLIError::UnknownParameter(key.to_string())),
|
||||
}
|
||||
}
|
||||
let protocol = "standard-request";
|
||||
if dry_run {
|
||||
None
|
||||
} else {
|
||||
assert!(err.issues.len() == 0);
|
||||
let mut ostream = writer_from_opts(self.opt.flag_o, &self.opt.arg_out);
|
||||
match match protocol {
|
||||
"standard-request" => call.doit(),
|
||||
_ => unreachable!(),
|
||||
} {
|
||||
Err(api_err) => Some(api_err),
|
||||
Ok((mut response, output_schema)) => {
|
||||
serde::json::to_writer_pretty(&mut ostream, &output_schema).unwrap();
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn _user_usage_report_get(&self, dry_run: bool, err: &mut InvalidOptionsError)
|
||||
-> Option<api::Error> {
|
||||
let mut call = self.hub.user_usage_report().get(&self.opt.arg_user_key, &self.opt.arg_date);
|
||||
for parg in self.opt.arg_v.iter() {
|
||||
let (key, value) = parse_kv_arg(&*parg, err, false);
|
||||
match key {
|
||||
"parameters" => {
|
||||
call = call.parameters(value.unwrap_or(""));
|
||||
},
|
||||
"page-token" => {
|
||||
call = call.page_token(value.unwrap_or(""));
|
||||
},
|
||||
"max-results" => {
|
||||
call = call.max_results(arg_from_str(value.unwrap_or("-0"), err, "max-results", "integer"));
|
||||
},
|
||||
"filters" => {
|
||||
call = call.filters(value.unwrap_or(""));
|
||||
},
|
||||
"customer-id" => {
|
||||
call = call.customer_id(value.unwrap_or(""));
|
||||
},
|
||||
"alt"
|
||||
|"fields"
|
||||
|"key"
|
||||
|"oauth-token"
|
||||
|"pretty-print"
|
||||
|"quota-user"
|
||||
|"user-ip" => {
|
||||
let map = [
|
||||
("oauth-token", "oauth_token"),
|
||||
("pretty-print", "prettyPrint"),
|
||||
("quota-user", "quotaUser"),
|
||||
("user-ip", "userIp"),
|
||||
];
|
||||
call = call.param(map.iter().find(|t| t.0 == key).unwrap_or(&("", key)).1, value.unwrap_or("unset"))
|
||||
},
|
||||
_ => err.issues.push(CLIError::UnknownParameter(key.to_string())),
|
||||
}
|
||||
}
|
||||
let protocol = "standard-request";
|
||||
if dry_run {
|
||||
None
|
||||
} else {
|
||||
assert!(err.issues.len() == 0);
|
||||
let mut ostream = writer_from_opts(self.opt.flag_o, &self.opt.arg_out);
|
||||
match match protocol {
|
||||
"standard-request" => call.doit(),
|
||||
_ => unreachable!(),
|
||||
} {
|
||||
Err(api_err) => Some(api_err),
|
||||
Ok((mut response, output_schema)) => {
|
||||
serde::json::to_writer_pretty(&mut ostream, &output_schema).unwrap();
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn _doit(&self, dry_run: bool) -> (Option<api::Error>, Option<InvalidOptionsError>) {
|
||||
let mut err = InvalidOptionsError::new();
|
||||
let mut call_result: Option<api::Error>;
|
||||
let mut err_opt: Option<InvalidOptionsError> = None;
|
||||
|
||||
if self.opt.cmd_activities {
|
||||
if self.opt.cmd_list {
|
||||
call_result = self._activities_list(dry_run, &mut err);
|
||||
} else if self.opt.cmd_watch {
|
||||
call_result = self._activities_watch(dry_run, &mut err);
|
||||
} else {
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
else if self.opt.cmd_channels {
|
||||
if self.opt.cmd_stop {
|
||||
call_result = self._channels_stop(dry_run, &mut err);
|
||||
} else {
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
else if self.opt.cmd_customer_usage_reports {
|
||||
if self.opt.cmd_get {
|
||||
call_result = self._customer_usage_reports_get(dry_run, &mut err);
|
||||
} else {
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
else if self.opt.cmd_user_usage_report {
|
||||
if self.opt.cmd_get {
|
||||
call_result = self._user_usage_report_get(dry_run, &mut err);
|
||||
} else {
|
||||
unreachable!();
|
||||
}
|
||||
} else {
|
||||
unreachable!();
|
||||
}
|
||||
|
||||
if dry_run {
|
||||
if err.issues.len() > 0 {
|
||||
err_opt = Some(err);
|
||||
}
|
||||
}
|
||||
(call_result, err_opt)
|
||||
}
|
||||
|
||||
// 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(&opt.flag_config_dir) {
|
||||
Err(e) => return Err(InvalidOptionsError::single(e, 3)),
|
||||
Ok(p) => p,
|
||||
};
|
||||
|
||||
match cmn::application_secret_from_directory(&config_dir, "admin1-reports-secret.json",
|
||||
"{\"installed\":{\"auth_uri\":\"https://accounts.google.com/o/oauth2/auth\",\"client_secret\":\"hCsslbCUyfehWMmbkG8vTYxG\",\"token_uri\":\"https://accounts.google.com/o/oauth2/token\",\"client_email\":\"\",\"redirect_uris\":[\"urn:ietf:wg:oauth:2.0:oob\",\"oob\"],\"client_x509_cert_url\":\"\",\"client_id\":\"620010449518-9ngf7o4dhs0dka470npqvor6dc5lqb9b.apps.googleusercontent.com\",\"auth_provider_x509_cert_url\":\"https://www.googleapis.com/oauth2/v1/certs\"}}") {
|
||||
Ok(secret) => (config_dir, secret),
|
||||
Err(e) => return Err(InvalidOptionsError::single(e, 4))
|
||||
}
|
||||
};
|
||||
|
||||
let auth = Authenticator::new( &secret, DefaultAuthenticatorDelegate,
|
||||
if opt.flag_debug_auth {
|
||||
hyper::Client::with_connector(mock::TeeConnector {
|
||||
connector: hyper::net::HttpConnector(None)
|
||||
})
|
||||
} else {
|
||||
hyper::Client::new()
|
||||
},
|
||||
JsonTokenStorage {
|
||||
program_name: "admin1-reports",
|
||||
db_dir: config_dir.clone(),
|
||||
}, None);
|
||||
|
||||
let client =
|
||||
if opt.flag_debug {
|
||||
hyper::Client::with_connector(mock::TeeConnector {
|
||||
connector: hyper::net::HttpConnector(None)
|
||||
})
|
||||
} else {
|
||||
hyper::Client::new()
|
||||
};
|
||||
let engine = Engine {
|
||||
opt: opt,
|
||||
hub: api::Reports::new(client, auth),
|
||||
};
|
||||
|
||||
match engine._doit(true) {
|
||||
(_, Some(err)) => Err(err),
|
||||
_ => 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> {
|
||||
self._doit(false).0
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let opts: Options = Options::docopt().decode().unwrap_or_else(|e| e.exit());
|
||||
match Engine::new(opts) {
|
||||
Err(err) => {
|
||||
writeln!(io::stderr(), "{}", err).ok();
|
||||
env::set_exit_status(err.exit_code);
|
||||
},
|
||||
Ok(engine) => {
|
||||
if let Some(err) = engine.doit() {
|
||||
writeln!(io::stderr(), "{:?}", err).ok();
|
||||
writeln!(io::stderr(), "{}", err).ok();
|
||||
env::set_exit_status(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
31
gen/admin2_email_migration-cli/Cargo.toml
Normal file
31
gen/admin2_email_migration-cli/Cargo.toml
Normal file
@@ -0,0 +1,31 @@
|
||||
# DO NOT EDIT !
|
||||
# This file was generated automatically from 'src/mako/Cargo.toml.mako'
|
||||
# DO NOT EDIT !
|
||||
[package]
|
||||
|
||||
name = "google-admin2_email_migration-cli"
|
||||
version = "0.0.1+20150303"
|
||||
authors = ["Sebastian Thiel <byronimo@gmail.com>"]
|
||||
description = "A complete library to interact with admin (protocol email_migration_v2)"
|
||||
repository = "https://github.com/Byron/google-apis-rs/tree/master/gen/admin2_email_migration-cli"
|
||||
homepage = "https://developers.google.com/admin-sdk/email-migration/v2/"
|
||||
documentation = "http://byron.github.io/google-apis-rs/google_admin2_email_migration_cli"
|
||||
license = "MIT"
|
||||
keywords = ["admin", "google", "cli"]
|
||||
|
||||
[[bin]]
|
||||
name = "admin2-email-migration"
|
||||
|
||||
[dependencies]
|
||||
hyper = "*"
|
||||
mime = "*"
|
||||
yup-oauth2 = "*"
|
||||
docopt = "*"
|
||||
docopt_macros = "*"
|
||||
rustc-serialize = "*"
|
||||
yup-hyper-mock = "*"
|
||||
serde = ">= 0.3.0"
|
||||
serde_macros = "*"
|
||||
|
||||
[dependencies.google-admin2_email_migration]
|
||||
path = "../admin2_email_migration"
|
||||
30
gen/admin2_email_migration-cli/LICENSE.md
Normal file
30
gen/admin2_email_migration-cli/LICENSE.md
Normal file
@@ -0,0 +1,30 @@
|
||||
<!---
|
||||
DO NOT EDIT !
|
||||
This file was generated automatically from 'src/mako/LICENSE.md.mako'
|
||||
DO NOT EDIT !
|
||||
-->
|
||||
The MIT License (MIT)
|
||||
=====================
|
||||
|
||||
Copyright © `2015` `Sebastian Thiel`
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the “Software”), to deal in the Software without
|
||||
restriction, including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
4
gen/admin2_email_migration-cli/README.md
Normal file
4
gen/admin2_email_migration-cli/README.md
Normal file
@@ -0,0 +1,4 @@
|
||||
# HELLO ADMIN:EMAIL_MIGRATION_V2
|
||||
|
||||
|
||||
Include information about application secret files, and how we automatically write a default one.
|
||||
17
gen/admin2_email_migration-cli/mkdocs.yml
Normal file
17
gen/admin2_email_migration-cli/mkdocs.yml
Normal file
@@ -0,0 +1,17 @@
|
||||
site_name: admin v0.0.1+20150303
|
||||
site_url: http://byron.github.io/google-apis-rs/google-admin2_email_migration-cli
|
||||
site_description: Write integrating applications with bcore
|
||||
|
||||
repo_url: https://github.com/Byron/google-apis-rs/tree/master/gen/admin2_email_migration-cli
|
||||
|
||||
docs_dir: docs
|
||||
site_dir: build_html
|
||||
|
||||
pages:
|
||||
- ['index.md', 'Home']
|
||||
- ['mail_insert.md', 'Mail', 'Insert']
|
||||
|
||||
theme: readthedocs
|
||||
|
||||
copyright: Copyright © 2015, `Sebastian Thiel`
|
||||
|
||||
454
gen/admin2_email_migration-cli/src/cmn.rs
Normal file
454
gen/admin2_email_migration-cli/src/cmn.rs
Normal file
@@ -0,0 +1,454 @@
|
||||
// COPY OF 'src/rust/cli/cmn.rs'
|
||||
// DO NOT EDIT
|
||||
use oauth2::{ApplicationSecret, ConsoleApplicationSecret, TokenStorage, Token};
|
||||
use rustc_serialize::json;
|
||||
use mime::Mime;
|
||||
|
||||
use std::fs;
|
||||
use std::env;
|
||||
use std::io;
|
||||
use std::fmt;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::str::FromStr;
|
||||
use std::string::ToString;
|
||||
use std::io::{Write, Read, stdout};
|
||||
|
||||
use std::default::Default;
|
||||
|
||||
const FIELD_SEP: char = '.';
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct FieldCursor(Vec<String>);
|
||||
|
||||
impl ToString for FieldCursor {
|
||||
fn to_string(&self) -> String {
|
||||
self.0.connect(".")
|
||||
}
|
||||
}
|
||||
|
||||
impl FieldCursor {
|
||||
pub fn set(&mut self, value: &str) -> Result<(), CLIError> {
|
||||
if value.len() == 0 {
|
||||
return Err(CLIError::Field(FieldError::Empty))
|
||||
}
|
||||
|
||||
let mut first_is_field_sep = false;
|
||||
let mut char_count: usize = 0;
|
||||
let mut last_c = FIELD_SEP;
|
||||
let mut num_conscutive_field_seps = 0;
|
||||
|
||||
let mut field = String::new();
|
||||
let mut fields = self.0.clone();
|
||||
|
||||
let push_field = |fs: &mut Vec<String>, f: &mut String| {
|
||||
if f.len() > 0 {
|
||||
fs.push(f.clone());
|
||||
f.truncate(0);
|
||||
}
|
||||
};
|
||||
|
||||
for (cid, c) in value.chars().enumerate() {
|
||||
char_count += 1;
|
||||
|
||||
if c == FIELD_SEP {
|
||||
if cid == 0 {
|
||||
first_is_field_sep = true;
|
||||
}
|
||||
num_conscutive_field_seps += 1;
|
||||
if cid > 0 && last_c == FIELD_SEP {
|
||||
if fields.pop().is_none() {
|
||||
return Err(CLIError::Field(FieldError::PopOnEmpty(value.to_string())))
|
||||
}
|
||||
} else {
|
||||
push_field(&mut fields, &mut field);
|
||||
}
|
||||
} else {
|
||||
num_conscutive_field_seps = 0;
|
||||
if cid == 1 {
|
||||
if first_is_field_sep {
|
||||
fields.truncate(0);
|
||||
}
|
||||
}
|
||||
field.push(c);
|
||||
}
|
||||
|
||||
last_c = c;
|
||||
}
|
||||
|
||||
push_field(&mut fields, &mut field);
|
||||
|
||||
if char_count == 1 && first_is_field_sep {
|
||||
fields.truncate(0);
|
||||
}
|
||||
if char_count > 1 && num_conscutive_field_seps == 1 {
|
||||
return Err(CLIError::Field(FieldError::TrailingFieldSep(value.to_string())))
|
||||
}
|
||||
|
||||
self.0 = fields;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn num_fields(&self) -> usize {
|
||||
self.0.len()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_kv_arg<'a>(kv: &'a str, err: &mut InvalidOptionsError, for_hashmap: bool)
|
||||
-> (&'a str, Option<&'a str>) {
|
||||
let mut add_err = || err.issues.push(CLIError::InvalidKeyValueSyntax(kv.to_string(),for_hashmap));
|
||||
match kv.find('=') {
|
||||
None => {
|
||||
add_err();
|
||||
return (kv, None)
|
||||
},
|
||||
Some(pos) => {
|
||||
let key = &kv[..pos];
|
||||
if kv.len() <= pos + 1 {
|
||||
add_err();
|
||||
return (key, None)
|
||||
}
|
||||
(key, Some(&kv[pos+1..]))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn input_file_from_opts(file_path: &str, err: &mut InvalidOptionsError) -> Option<fs::File> {
|
||||
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<Mime> {
|
||||
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<Write> {
|
||||
if !flag || arg == "-" {
|
||||
Box::new(stdout())
|
||||
} else {
|
||||
Box::new(fs::OpenOptions::new().create(true).write(true).open(arg).unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn arg_from_str<T>(arg: &str, err: &mut InvalidOptionsError,
|
||||
arg_name: &'static str,
|
||||
arg_type: &'static str) -> T
|
||||
where T: FromStr + Default,
|
||||
<T as FromStr>::Err: fmt::Display {
|
||||
match FromStr::from_str(arg) {
|
||||
Err(perr) => {
|
||||
err.issues.push(
|
||||
CLIError::ParseError((arg_name, arg_type, arg.to_string(), format!("{}", perr)))
|
||||
);
|
||||
Default::default()
|
||||
},
|
||||
Ok(v) => v,
|
||||
}
|
||||
}
|
||||
|
||||
pub struct JsonTokenStorage {
|
||||
pub program_name: &'static str,
|
||||
pub db_dir: String,
|
||||
}
|
||||
|
||||
impl JsonTokenStorage {
|
||||
fn path(&self, scope_hash: u64) -> PathBuf {
|
||||
Path::new(&self.db_dir).join(&format!("{}-token-{}.json", self.program_name, scope_hash))
|
||||
}
|
||||
}
|
||||
|
||||
impl TokenStorage for JsonTokenStorage {
|
||||
type Error = io::Error;
|
||||
|
||||
// NOTE: logging might be interesting, currently we swallow all errors
|
||||
fn set(&mut self, scope_hash: u64, _: &Vec<&str>, token: Option<Token>) -> Option<io::Error> {
|
||||
match token {
|
||||
None => {
|
||||
match fs::remove_file(self.path(scope_hash)) {
|
||||
Err(err) =>
|
||||
match err.kind() {
|
||||
io::ErrorKind::NotFound => None,
|
||||
_ => Some(err)
|
||||
},
|
||||
Ok(_) => None
|
||||
}
|
||||
}
|
||||
Some(token) => {
|
||||
let json_token = json::encode(&token).unwrap();
|
||||
match fs::OpenOptions::new().create(true).write(true).open(&self.path(scope_hash)) {
|
||||
Ok(mut f) => {
|
||||
match f.write(json_token.as_bytes()) {
|
||||
Ok(_) => None,
|
||||
Err(io_err) => Some(io_err),
|
||||
}
|
||||
},
|
||||
Err(io_err) => Some(io_err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get(&self, scope_hash: u64, _: &Vec<&str>) -> Result<Option<Token>, io::Error> {
|
||||
match fs::File::open(&self.path(scope_hash)) {
|
||||
Ok(mut f) => {
|
||||
let mut json_string = String::new();
|
||||
match f.read_to_string(&mut json_string) {
|
||||
Ok(_) => Ok(Some(json::decode::<Token>(&json_string).unwrap())),
|
||||
Err(io_err) => Err(io_err),
|
||||
}
|
||||
},
|
||||
Err(io_err) => {
|
||||
match io_err.kind() {
|
||||
io::ErrorKind::NotFound => Ok(None),
|
||||
_ => Err(io_err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug)]
|
||||
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)),
|
||||
}
|
||||
|
||||
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 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 FieldError {
|
||||
PopOnEmpty(String),
|
||||
TrailingFieldSep(String),
|
||||
Unknown(String),
|
||||
Empty,
|
||||
}
|
||||
|
||||
|
||||
impl fmt::Display for FieldError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
match *self {
|
||||
FieldError::PopOnEmpty(ref field)
|
||||
=> writeln!(f, "'{}': Cannot move up on empty field cursor", field),
|
||||
FieldError::TrailingFieldSep(ref field)
|
||||
=> writeln!(f, "'{}': Single field separator may not be last character", field),
|
||||
FieldError::Unknown(ref field)
|
||||
=> writeln!(f, "Field '{}' does not exist", field),
|
||||
FieldError::Empty
|
||||
=> writeln!(f, "Field names must not be empty"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum CLIError {
|
||||
Configuration(ConfigurationError),
|
||||
ParseError((&'static str, &'static str, String, String)),
|
||||
UnknownParameter(String),
|
||||
InvalidKeyValueSyntax(String, bool),
|
||||
Input(InputError),
|
||||
Field(FieldError),
|
||||
}
|
||||
|
||||
impl fmt::Display for CLIError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
match *self {
|
||||
CLIError::Configuration(ref err) => write!(f, "Configuration -> {}", err),
|
||||
CLIError::Input(ref err) => write!(f, "Input -> {}", err),
|
||||
CLIError::Field(ref err) => write!(f, "Field -> {}", 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),
|
||||
CLIError::UnknownParameter(ref param_name)
|
||||
=> writeln!(f, "Parameter '{}' is unknown.", param_name),
|
||||
CLIError::InvalidKeyValueSyntax(ref kv, is_hashmap) => {
|
||||
let hashmap_info = if is_hashmap { "hashmap " } else { "" };
|
||||
writeln!(f, "'{}' does not match {}pattern <key>=<value>", kv, hashmap_info)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct InvalidOptionsError {
|
||||
pub issues: Vec<CLIError>,
|
||||
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: CLIError, exit_code: i32) -> InvalidOptionsError {
|
||||
InvalidOptionsError {
|
||||
issues: vec![err],
|
||||
exit_code: exit_code,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new() -> InvalidOptionsError {
|
||||
InvalidOptionsError {
|
||||
issues: Vec::new(),
|
||||
exit_code: 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn assure_config_dir_exists(dir: &str) -> Result<String, CLIError> {
|
||||
let trdir = dir.trim();
|
||||
if trdir.len() == 0 {
|
||||
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(CLIError::Configuration(ConfigurationError::HomeExpansionFailed(trdir.to_string()))),
|
||||
Some(mut user) => {
|
||||
user.push_str(&trdir[1..]);
|
||||
user
|
||||
}
|
||||
}
|
||||
} else {
|
||||
trdir.to_string()
|
||||
};
|
||||
|
||||
if let Err(err) = fs::create_dir(&expanded_config_dir) {
|
||||
if err.kind() != io::ErrorKind::AlreadyExists {
|
||||
return Err(CLIError::Configuration(
|
||||
ConfigurationError::DirectoryCreationFailed((expanded_config_dir, err))))
|
||||
}
|
||||
}
|
||||
|
||||
Ok(expanded_config_dir)
|
||||
}
|
||||
|
||||
pub fn application_secret_from_directory(dir: &str,
|
||||
secret_basename: &str,
|
||||
json_app_secret: &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(CLIError::Configuration(ConfigurationError::IOError(
|
||||
(secret_str(), io_err)
|
||||
)))
|
||||
};
|
||||
|
||||
for _ in 0..2 {
|
||||
match fs::File::open(&secret_path) {
|
||||
Err(mut err) => {
|
||||
if err.kind() == io::ErrorKind::NotFound {
|
||||
// Write our built-in one - user may adjust the written file at will
|
||||
|
||||
err = match fs::OpenOptions::new().create(true).write(true).open(&secret_path) {
|
||||
Err(cfe) => cfe,
|
||||
Ok(mut f) => {
|
||||
match f.write(json_app_secret.as_bytes()) {
|
||||
Err(io_err) => io_err,
|
||||
Ok(_) => continue,
|
||||
}
|
||||
}
|
||||
};
|
||||
// fall through to IO error handling
|
||||
}
|
||||
return secret_io_error(err)
|
||||
},
|
||||
Ok(mut f) => {
|
||||
let mut json_encoded_secret = String::new();
|
||||
if let Err(io_err) = f.read_to_string(&mut json_encoded_secret) {
|
||||
return secret_io_error(io_err)
|
||||
}
|
||||
match json::decode::<ConsoleApplicationSecret>(&json_encoded_secret) {
|
||||
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(
|
||||
CLIError::Configuration(
|
||||
ConfigurationError::Secret(
|
||||
ApplicationSecretError::FormatError(secret_str())
|
||||
)))
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
unreachable!();
|
||||
}
|
||||
248
gen/admin2_email_migration-cli/src/main.rs
Normal file
248
gen/admin2_email_migration-cli/src/main.rs
Normal file
@@ -0,0 +1,248 @@
|
||||
// DO NOT EDIT !
|
||||
// This file was generated automatically from 'src/mako/cli/main.rs.mako'
|
||||
// DO NOT EDIT !
|
||||
#![feature(plugin, exit_status)]
|
||||
#![plugin(docopt_macros)]
|
||||
#![allow(unused_variables, unused_imports, dead_code, unused_mut)]
|
||||
|
||||
extern crate docopt;
|
||||
extern crate yup_oauth2 as oauth2;
|
||||
extern crate yup_hyper_mock as mock;
|
||||
extern crate rustc_serialize;
|
||||
extern crate serde;
|
||||
extern crate hyper;
|
||||
extern crate mime;
|
||||
extern crate google_admin2_email_migration as api;
|
||||
|
||||
use std::env;
|
||||
use std::io::{self, Write};
|
||||
|
||||
docopt!(Options derive Debug, "
|
||||
Usage:
|
||||
admin2-email-migration [options] mail insert <user-key> -r <kv>... -u (simple|resumable) <file> <mime> [-p <v>]...
|
||||
admin2-email-migration --help
|
||||
|
||||
All documentation details can be found TODO: <URL to github.io docs here, see #51>
|
||||
|
||||
Configuration:
|
||||
--scope <url>
|
||||
Specify the authentication a method should be executed in. Each scope requires
|
||||
the user to grant this application permission to use it.
|
||||
If unset, it defaults to the shortest scope url for a particular method.
|
||||
--config-dir <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: ~/.google-service-cli]
|
||||
--debug
|
||||
Output all server communication to standard error. `tx` and `rx` are placed into
|
||||
the same stream.
|
||||
--debug-auth
|
||||
Output all communication related to authentication to standard error. `tx` and `rx` are placed into
|
||||
the same stream.
|
||||
");
|
||||
|
||||
mod cmn;
|
||||
use cmn::{InvalidOptionsError, CLIError, JsonTokenStorage, arg_from_str, writer_from_opts, parse_kv_arg,
|
||||
input_file_from_opts, input_mime_from_opts, FieldCursor, FieldError};
|
||||
|
||||
use std::default::Default;
|
||||
use std::str::FromStr;
|
||||
|
||||
use oauth2::{Authenticator, DefaultAuthenticatorDelegate};
|
||||
use rustc_serialize::json;
|
||||
|
||||
struct Engine {
|
||||
opt: Options,
|
||||
hub: api::Admin<hyper::Client, Authenticator<DefaultAuthenticatorDelegate, JsonTokenStorage, hyper::Client>>,
|
||||
}
|
||||
|
||||
|
||||
impl Engine {
|
||||
fn _mail_insert(&self, dry_run: bool, err: &mut InvalidOptionsError)
|
||||
-> Option<api::Error> {
|
||||
let mut request = api::MailItem::default();
|
||||
let mut call = self.hub.mail().insert(&request, &self.opt.arg_user_key);
|
||||
for parg in self.opt.arg_v.iter() {
|
||||
let (key, value) = parse_kv_arg(&*parg, err, false);
|
||||
match key {
|
||||
"alt"
|
||||
|"fields"
|
||||
|"key"
|
||||
|"oauth-token"
|
||||
|"pretty-print"
|
||||
|"quota-user"
|
||||
|"user-ip" => {
|
||||
let map = [
|
||||
("oauth-token", "oauth_token"),
|
||||
("pretty-print", "prettyPrint"),
|
||||
("quota-user", "quotaUser"),
|
||||
("user-ip", "userIp"),
|
||||
];
|
||||
call = call.param(map.iter().find(|t| t.0 == key).unwrap_or(&("", key)).1, value.unwrap_or("unset"))
|
||||
},
|
||||
_ => err.issues.push(CLIError::UnknownParameter(key.to_string())),
|
||||
}
|
||||
}
|
||||
|
||||
let mut field_name = FieldCursor::default();
|
||||
for kvarg in self.opt.arg_kv.iter() {
|
||||
let (key, value) = parse_kv_arg(&*kvarg, err, false);
|
||||
if let Err(field_err) = field_name.set(&*key) {
|
||||
err.issues.push(field_err);
|
||||
}
|
||||
match &field_name.to_string()[..] {
|
||||
"is-trash" => {
|
||||
request.is_trash = Some(arg_from_str(value.unwrap_or("false"), err, "is-trash", "boolean"));
|
||||
},
|
||||
"kind" => {
|
||||
request.kind = Some(value.unwrap_or("").to_string());
|
||||
},
|
||||
"labels" => {
|
||||
if request.labels.is_none() {
|
||||
request.labels = Some(Default::default());
|
||||
}
|
||||
request.labels.as_mut().unwrap().push(value.unwrap_or("").to_string());
|
||||
},
|
||||
"is-draft" => {
|
||||
request.is_draft = Some(arg_from_str(value.unwrap_or("false"), err, "is-draft", "boolean"));
|
||||
},
|
||||
"is-inbox" => {
|
||||
request.is_inbox = Some(arg_from_str(value.unwrap_or("false"), err, "is-inbox", "boolean"));
|
||||
},
|
||||
"is-sent" => {
|
||||
request.is_sent = Some(arg_from_str(value.unwrap_or("false"), err, "is-sent", "boolean"));
|
||||
},
|
||||
"is-starred" => {
|
||||
request.is_starred = Some(arg_from_str(value.unwrap_or("false"), err, "is-starred", "boolean"));
|
||||
},
|
||||
"is-unread" => {
|
||||
request.is_unread = Some(arg_from_str(value.unwrap_or("false"), err, "is-unread", "boolean"));
|
||||
},
|
||||
"is-deleted" => {
|
||||
request.is_deleted = Some(arg_from_str(value.unwrap_or("false"), err, "is-deleted", "boolean"));
|
||||
},
|
||||
_ => {
|
||||
err.issues.push(CLIError::Field(FieldError::Unknown(field_name.to_string())));
|
||||
}
|
||||
}
|
||||
}
|
||||
let protocol =
|
||||
if self.opt.cmd_simple {
|
||||
"simple"
|
||||
} else if self.opt.cmd_resumable {
|
||||
"resumable"
|
||||
} else {
|
||||
unreachable!()
|
||||
};
|
||||
let mut input_file = input_file_from_opts(&self.opt.arg_file, err);
|
||||
let mime_type = input_mime_from_opts(&self.opt.arg_mime, err);
|
||||
if dry_run {
|
||||
None
|
||||
} else {
|
||||
assert!(err.issues.len() == 0);
|
||||
match match protocol {
|
||||
"simple" => call.upload(input_file.unwrap(), mime_type.unwrap()),
|
||||
"resumable" => call.upload_resumable(input_file.unwrap(), mime_type.unwrap()),
|
||||
_ => unreachable!(),
|
||||
} {
|
||||
Err(api_err) => Some(api_err),
|
||||
Ok(mut response) => {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn _doit(&self, dry_run: bool) -> (Option<api::Error>, Option<InvalidOptionsError>) {
|
||||
let mut err = InvalidOptionsError::new();
|
||||
let mut call_result: Option<api::Error>;
|
||||
let mut err_opt: Option<InvalidOptionsError> = None;
|
||||
|
||||
if self.opt.cmd_mail {
|
||||
if self.opt.cmd_insert {
|
||||
call_result = self._mail_insert(dry_run, &mut err);
|
||||
} else {
|
||||
unreachable!();
|
||||
}
|
||||
} else {
|
||||
unreachable!();
|
||||
}
|
||||
|
||||
if dry_run {
|
||||
if err.issues.len() > 0 {
|
||||
err_opt = Some(err);
|
||||
}
|
||||
}
|
||||
(call_result, err_opt)
|
||||
}
|
||||
|
||||
// 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(&opt.flag_config_dir) {
|
||||
Err(e) => return Err(InvalidOptionsError::single(e, 3)),
|
||||
Ok(p) => p,
|
||||
};
|
||||
|
||||
match cmn::application_secret_from_directory(&config_dir, "admin2-email-migration-secret.json",
|
||||
"{\"installed\":{\"auth_uri\":\"https://accounts.google.com/o/oauth2/auth\",\"client_secret\":\"hCsslbCUyfehWMmbkG8vTYxG\",\"token_uri\":\"https://accounts.google.com/o/oauth2/token\",\"client_email\":\"\",\"redirect_uris\":[\"urn:ietf:wg:oauth:2.0:oob\",\"oob\"],\"client_x509_cert_url\":\"\",\"client_id\":\"620010449518-9ngf7o4dhs0dka470npqvor6dc5lqb9b.apps.googleusercontent.com\",\"auth_provider_x509_cert_url\":\"https://www.googleapis.com/oauth2/v1/certs\"}}") {
|
||||
Ok(secret) => (config_dir, secret),
|
||||
Err(e) => return Err(InvalidOptionsError::single(e, 4))
|
||||
}
|
||||
};
|
||||
|
||||
let auth = Authenticator::new( &secret, DefaultAuthenticatorDelegate,
|
||||
if opt.flag_debug_auth {
|
||||
hyper::Client::with_connector(mock::TeeConnector {
|
||||
connector: hyper::net::HttpConnector(None)
|
||||
})
|
||||
} else {
|
||||
hyper::Client::new()
|
||||
},
|
||||
JsonTokenStorage {
|
||||
program_name: "admin2-email-migration",
|
||||
db_dir: config_dir.clone(),
|
||||
}, None);
|
||||
|
||||
let client =
|
||||
if opt.flag_debug {
|
||||
hyper::Client::with_connector(mock::TeeConnector {
|
||||
connector: hyper::net::HttpConnector(None)
|
||||
})
|
||||
} else {
|
||||
hyper::Client::new()
|
||||
};
|
||||
let engine = Engine {
|
||||
opt: opt,
|
||||
hub: api::Admin::new(client, auth),
|
||||
};
|
||||
|
||||
match engine._doit(true) {
|
||||
(_, Some(err)) => Err(err),
|
||||
_ => 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> {
|
||||
self._doit(false).0
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let opts: Options = Options::docopt().decode().unwrap_or_else(|e| e.exit());
|
||||
match Engine::new(opts) {
|
||||
Err(err) => {
|
||||
writeln!(io::stderr(), "{}", err).ok();
|
||||
env::set_exit_status(err.exit_code);
|
||||
},
|
||||
Ok(engine) => {
|
||||
if let Some(err) = engine.doit() {
|
||||
writeln!(io::stderr(), "{:?}", err).ok();
|
||||
writeln!(io::stderr(), "{}", err).ok();
|
||||
env::set_exit_status(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
31
gen/adsensehost4d1-cli/Cargo.toml
Normal file
31
gen/adsensehost4d1-cli/Cargo.toml
Normal file
@@ -0,0 +1,31 @@
|
||||
# DO NOT EDIT !
|
||||
# This file was generated automatically from 'src/mako/Cargo.toml.mako'
|
||||
# DO NOT EDIT !
|
||||
[package]
|
||||
|
||||
name = "google-adsensehost4d1-cli"
|
||||
version = "0.0.1+20150307"
|
||||
authors = ["Sebastian Thiel <byronimo@gmail.com>"]
|
||||
description = "A complete library to interact with AdSense Host (protocol v4.1)"
|
||||
repository = "https://github.com/Byron/google-apis-rs/tree/master/gen/adsensehost4d1-cli"
|
||||
homepage = "https://developers.google.com/adsense/host/"
|
||||
documentation = "http://byron.github.io/google-apis-rs/google_adsensehost4d1_cli"
|
||||
license = "MIT"
|
||||
keywords = ["adsensehost", "google", "cli"]
|
||||
|
||||
[[bin]]
|
||||
name = "adsensehost4d1"
|
||||
|
||||
[dependencies]
|
||||
hyper = "*"
|
||||
mime = "*"
|
||||
yup-oauth2 = "*"
|
||||
docopt = "*"
|
||||
docopt_macros = "*"
|
||||
rustc-serialize = "*"
|
||||
yup-hyper-mock = "*"
|
||||
serde = ">= 0.3.0"
|
||||
serde_macros = "*"
|
||||
|
||||
[dependencies.google-adsensehost4d1]
|
||||
path = "../adsensehost4d1"
|
||||
30
gen/adsensehost4d1-cli/LICENSE.md
Normal file
30
gen/adsensehost4d1-cli/LICENSE.md
Normal file
@@ -0,0 +1,30 @@
|
||||
<!---
|
||||
DO NOT EDIT !
|
||||
This file was generated automatically from 'src/mako/LICENSE.md.mako'
|
||||
DO NOT EDIT !
|
||||
-->
|
||||
The MIT License (MIT)
|
||||
=====================
|
||||
|
||||
Copyright © `2015` `Sebastian Thiel`
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the “Software”), to deal in the Software without
|
||||
restriction, including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
4
gen/adsensehost4d1-cli/README.md
Normal file
4
gen/adsensehost4d1-cli/README.md
Normal file
@@ -0,0 +1,4 @@
|
||||
# HELLO ADSENSEHOST:V4.1
|
||||
|
||||
|
||||
Include information about application secret files, and how we automatically write a default one.
|
||||
42
gen/adsensehost4d1-cli/mkdocs.yml
Normal file
42
gen/adsensehost4d1-cli/mkdocs.yml
Normal file
@@ -0,0 +1,42 @@
|
||||
site_name: AdSense Host v0.0.1+20150307
|
||||
site_url: http://byron.github.io/google-apis-rs/google-adsensehost4d1-cli
|
||||
site_description: Write integrating applications with bcore
|
||||
|
||||
repo_url: https://github.com/Byron/google-apis-rs/tree/master/gen/adsensehost4d1-cli
|
||||
|
||||
docs_dir: docs
|
||||
site_dir: build_html
|
||||
|
||||
pages:
|
||||
- ['index.md', 'Home']
|
||||
- ['accounts_adclients-get.md', 'Accounts', 'Adclients Get']
|
||||
- ['accounts_adclients-list.md', 'Accounts', 'Adclients List']
|
||||
- ['accounts_adunits-delete.md', 'Accounts', 'Adunits Delete']
|
||||
- ['accounts_adunits-get.md', 'Accounts', 'Adunits Get']
|
||||
- ['accounts_adunits-get-ad-code.md', 'Accounts', 'Adunits Get Ad Code']
|
||||
- ['accounts_adunits-insert.md', 'Accounts', 'Adunits Insert']
|
||||
- ['accounts_adunits-list.md', 'Accounts', 'Adunits List']
|
||||
- ['accounts_adunits-patch.md', 'Accounts', 'Adunits Patch']
|
||||
- ['accounts_adunits-update.md', 'Accounts', 'Adunits Update']
|
||||
- ['accounts_get.md', 'Accounts', 'Get']
|
||||
- ['accounts_list.md', 'Accounts', 'List']
|
||||
- ['accounts_reports-generate.md', 'Accounts', 'Reports Generate']
|
||||
- ['adclients_get.md', 'Adclients', 'Get']
|
||||
- ['adclients_list.md', 'Adclients', 'List']
|
||||
- ['associationsessions_start.md', 'Associationsessions', 'Start']
|
||||
- ['associationsessions_verify.md', 'Associationsessions', 'Verify']
|
||||
- ['customchannels_delete.md', 'Customchannels', 'Delete']
|
||||
- ['customchannels_get.md', 'Customchannels', 'Get']
|
||||
- ['customchannels_insert.md', 'Customchannels', 'Insert']
|
||||
- ['customchannels_list.md', 'Customchannels', 'List']
|
||||
- ['customchannels_patch.md', 'Customchannels', 'Patch']
|
||||
- ['customchannels_update.md', 'Customchannels', 'Update']
|
||||
- ['reports_generate.md', 'Reports', 'Generate']
|
||||
- ['urlchannels_delete.md', 'Urlchannels', 'Delete']
|
||||
- ['urlchannels_insert.md', 'Urlchannels', 'Insert']
|
||||
- ['urlchannels_list.md', 'Urlchannels', 'List']
|
||||
|
||||
theme: readthedocs
|
||||
|
||||
copyright: Copyright © 2015, `Sebastian Thiel`
|
||||
|
||||
454
gen/adsensehost4d1-cli/src/cmn.rs
Normal file
454
gen/adsensehost4d1-cli/src/cmn.rs
Normal file
@@ -0,0 +1,454 @@
|
||||
// COPY OF 'src/rust/cli/cmn.rs'
|
||||
// DO NOT EDIT
|
||||
use oauth2::{ApplicationSecret, ConsoleApplicationSecret, TokenStorage, Token};
|
||||
use rustc_serialize::json;
|
||||
use mime::Mime;
|
||||
|
||||
use std::fs;
|
||||
use std::env;
|
||||
use std::io;
|
||||
use std::fmt;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::str::FromStr;
|
||||
use std::string::ToString;
|
||||
use std::io::{Write, Read, stdout};
|
||||
|
||||
use std::default::Default;
|
||||
|
||||
const FIELD_SEP: char = '.';
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct FieldCursor(Vec<String>);
|
||||
|
||||
impl ToString for FieldCursor {
|
||||
fn to_string(&self) -> String {
|
||||
self.0.connect(".")
|
||||
}
|
||||
}
|
||||
|
||||
impl FieldCursor {
|
||||
pub fn set(&mut self, value: &str) -> Result<(), CLIError> {
|
||||
if value.len() == 0 {
|
||||
return Err(CLIError::Field(FieldError::Empty))
|
||||
}
|
||||
|
||||
let mut first_is_field_sep = false;
|
||||
let mut char_count: usize = 0;
|
||||
let mut last_c = FIELD_SEP;
|
||||
let mut num_conscutive_field_seps = 0;
|
||||
|
||||
let mut field = String::new();
|
||||
let mut fields = self.0.clone();
|
||||
|
||||
let push_field = |fs: &mut Vec<String>, f: &mut String| {
|
||||
if f.len() > 0 {
|
||||
fs.push(f.clone());
|
||||
f.truncate(0);
|
||||
}
|
||||
};
|
||||
|
||||
for (cid, c) in value.chars().enumerate() {
|
||||
char_count += 1;
|
||||
|
||||
if c == FIELD_SEP {
|
||||
if cid == 0 {
|
||||
first_is_field_sep = true;
|
||||
}
|
||||
num_conscutive_field_seps += 1;
|
||||
if cid > 0 && last_c == FIELD_SEP {
|
||||
if fields.pop().is_none() {
|
||||
return Err(CLIError::Field(FieldError::PopOnEmpty(value.to_string())))
|
||||
}
|
||||
} else {
|
||||
push_field(&mut fields, &mut field);
|
||||
}
|
||||
} else {
|
||||
num_conscutive_field_seps = 0;
|
||||
if cid == 1 {
|
||||
if first_is_field_sep {
|
||||
fields.truncate(0);
|
||||
}
|
||||
}
|
||||
field.push(c);
|
||||
}
|
||||
|
||||
last_c = c;
|
||||
}
|
||||
|
||||
push_field(&mut fields, &mut field);
|
||||
|
||||
if char_count == 1 && first_is_field_sep {
|
||||
fields.truncate(0);
|
||||
}
|
||||
if char_count > 1 && num_conscutive_field_seps == 1 {
|
||||
return Err(CLIError::Field(FieldError::TrailingFieldSep(value.to_string())))
|
||||
}
|
||||
|
||||
self.0 = fields;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn num_fields(&self) -> usize {
|
||||
self.0.len()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_kv_arg<'a>(kv: &'a str, err: &mut InvalidOptionsError, for_hashmap: bool)
|
||||
-> (&'a str, Option<&'a str>) {
|
||||
let mut add_err = || err.issues.push(CLIError::InvalidKeyValueSyntax(kv.to_string(),for_hashmap));
|
||||
match kv.find('=') {
|
||||
None => {
|
||||
add_err();
|
||||
return (kv, None)
|
||||
},
|
||||
Some(pos) => {
|
||||
let key = &kv[..pos];
|
||||
if kv.len() <= pos + 1 {
|
||||
add_err();
|
||||
return (key, None)
|
||||
}
|
||||
(key, Some(&kv[pos+1..]))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn input_file_from_opts(file_path: &str, err: &mut InvalidOptionsError) -> Option<fs::File> {
|
||||
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<Mime> {
|
||||
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<Write> {
|
||||
if !flag || arg == "-" {
|
||||
Box::new(stdout())
|
||||
} else {
|
||||
Box::new(fs::OpenOptions::new().create(true).write(true).open(arg).unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn arg_from_str<T>(arg: &str, err: &mut InvalidOptionsError,
|
||||
arg_name: &'static str,
|
||||
arg_type: &'static str) -> T
|
||||
where T: FromStr + Default,
|
||||
<T as FromStr>::Err: fmt::Display {
|
||||
match FromStr::from_str(arg) {
|
||||
Err(perr) => {
|
||||
err.issues.push(
|
||||
CLIError::ParseError((arg_name, arg_type, arg.to_string(), format!("{}", perr)))
|
||||
);
|
||||
Default::default()
|
||||
},
|
||||
Ok(v) => v,
|
||||
}
|
||||
}
|
||||
|
||||
pub struct JsonTokenStorage {
|
||||
pub program_name: &'static str,
|
||||
pub db_dir: String,
|
||||
}
|
||||
|
||||
impl JsonTokenStorage {
|
||||
fn path(&self, scope_hash: u64) -> PathBuf {
|
||||
Path::new(&self.db_dir).join(&format!("{}-token-{}.json", self.program_name, scope_hash))
|
||||
}
|
||||
}
|
||||
|
||||
impl TokenStorage for JsonTokenStorage {
|
||||
type Error = io::Error;
|
||||
|
||||
// NOTE: logging might be interesting, currently we swallow all errors
|
||||
fn set(&mut self, scope_hash: u64, _: &Vec<&str>, token: Option<Token>) -> Option<io::Error> {
|
||||
match token {
|
||||
None => {
|
||||
match fs::remove_file(self.path(scope_hash)) {
|
||||
Err(err) =>
|
||||
match err.kind() {
|
||||
io::ErrorKind::NotFound => None,
|
||||
_ => Some(err)
|
||||
},
|
||||
Ok(_) => None
|
||||
}
|
||||
}
|
||||
Some(token) => {
|
||||
let json_token = json::encode(&token).unwrap();
|
||||
match fs::OpenOptions::new().create(true).write(true).open(&self.path(scope_hash)) {
|
||||
Ok(mut f) => {
|
||||
match f.write(json_token.as_bytes()) {
|
||||
Ok(_) => None,
|
||||
Err(io_err) => Some(io_err),
|
||||
}
|
||||
},
|
||||
Err(io_err) => Some(io_err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get(&self, scope_hash: u64, _: &Vec<&str>) -> Result<Option<Token>, io::Error> {
|
||||
match fs::File::open(&self.path(scope_hash)) {
|
||||
Ok(mut f) => {
|
||||
let mut json_string = String::new();
|
||||
match f.read_to_string(&mut json_string) {
|
||||
Ok(_) => Ok(Some(json::decode::<Token>(&json_string).unwrap())),
|
||||
Err(io_err) => Err(io_err),
|
||||
}
|
||||
},
|
||||
Err(io_err) => {
|
||||
match io_err.kind() {
|
||||
io::ErrorKind::NotFound => Ok(None),
|
||||
_ => Err(io_err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug)]
|
||||
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)),
|
||||
}
|
||||
|
||||
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 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 FieldError {
|
||||
PopOnEmpty(String),
|
||||
TrailingFieldSep(String),
|
||||
Unknown(String),
|
||||
Empty,
|
||||
}
|
||||
|
||||
|
||||
impl fmt::Display for FieldError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
match *self {
|
||||
FieldError::PopOnEmpty(ref field)
|
||||
=> writeln!(f, "'{}': Cannot move up on empty field cursor", field),
|
||||
FieldError::TrailingFieldSep(ref field)
|
||||
=> writeln!(f, "'{}': Single field separator may not be last character", field),
|
||||
FieldError::Unknown(ref field)
|
||||
=> writeln!(f, "Field '{}' does not exist", field),
|
||||
FieldError::Empty
|
||||
=> writeln!(f, "Field names must not be empty"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum CLIError {
|
||||
Configuration(ConfigurationError),
|
||||
ParseError((&'static str, &'static str, String, String)),
|
||||
UnknownParameter(String),
|
||||
InvalidKeyValueSyntax(String, bool),
|
||||
Input(InputError),
|
||||
Field(FieldError),
|
||||
}
|
||||
|
||||
impl fmt::Display for CLIError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
match *self {
|
||||
CLIError::Configuration(ref err) => write!(f, "Configuration -> {}", err),
|
||||
CLIError::Input(ref err) => write!(f, "Input -> {}", err),
|
||||
CLIError::Field(ref err) => write!(f, "Field -> {}", 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),
|
||||
CLIError::UnknownParameter(ref param_name)
|
||||
=> writeln!(f, "Parameter '{}' is unknown.", param_name),
|
||||
CLIError::InvalidKeyValueSyntax(ref kv, is_hashmap) => {
|
||||
let hashmap_info = if is_hashmap { "hashmap " } else { "" };
|
||||
writeln!(f, "'{}' does not match {}pattern <key>=<value>", kv, hashmap_info)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct InvalidOptionsError {
|
||||
pub issues: Vec<CLIError>,
|
||||
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: CLIError, exit_code: i32) -> InvalidOptionsError {
|
||||
InvalidOptionsError {
|
||||
issues: vec![err],
|
||||
exit_code: exit_code,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new() -> InvalidOptionsError {
|
||||
InvalidOptionsError {
|
||||
issues: Vec::new(),
|
||||
exit_code: 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn assure_config_dir_exists(dir: &str) -> Result<String, CLIError> {
|
||||
let trdir = dir.trim();
|
||||
if trdir.len() == 0 {
|
||||
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(CLIError::Configuration(ConfigurationError::HomeExpansionFailed(trdir.to_string()))),
|
||||
Some(mut user) => {
|
||||
user.push_str(&trdir[1..]);
|
||||
user
|
||||
}
|
||||
}
|
||||
} else {
|
||||
trdir.to_string()
|
||||
};
|
||||
|
||||
if let Err(err) = fs::create_dir(&expanded_config_dir) {
|
||||
if err.kind() != io::ErrorKind::AlreadyExists {
|
||||
return Err(CLIError::Configuration(
|
||||
ConfigurationError::DirectoryCreationFailed((expanded_config_dir, err))))
|
||||
}
|
||||
}
|
||||
|
||||
Ok(expanded_config_dir)
|
||||
}
|
||||
|
||||
pub fn application_secret_from_directory(dir: &str,
|
||||
secret_basename: &str,
|
||||
json_app_secret: &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(CLIError::Configuration(ConfigurationError::IOError(
|
||||
(secret_str(), io_err)
|
||||
)))
|
||||
};
|
||||
|
||||
for _ in 0..2 {
|
||||
match fs::File::open(&secret_path) {
|
||||
Err(mut err) => {
|
||||
if err.kind() == io::ErrorKind::NotFound {
|
||||
// Write our built-in one - user may adjust the written file at will
|
||||
|
||||
err = match fs::OpenOptions::new().create(true).write(true).open(&secret_path) {
|
||||
Err(cfe) => cfe,
|
||||
Ok(mut f) => {
|
||||
match f.write(json_app_secret.as_bytes()) {
|
||||
Err(io_err) => io_err,
|
||||
Ok(_) => continue,
|
||||
}
|
||||
}
|
||||
};
|
||||
// fall through to IO error handling
|
||||
}
|
||||
return secret_io_error(err)
|
||||
},
|
||||
Ok(mut f) => {
|
||||
let mut json_encoded_secret = String::new();
|
||||
if let Err(io_err) = f.read_to_string(&mut json_encoded_secret) {
|
||||
return secret_io_error(io_err)
|
||||
}
|
||||
match json::decode::<ConsoleApplicationSecret>(&json_encoded_secret) {
|
||||
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(
|
||||
CLIError::Configuration(
|
||||
ConfigurationError::Secret(
|
||||
ApplicationSecretError::FormatError(secret_str())
|
||||
)))
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
unreachable!();
|
||||
}
|
||||
1979
gen/adsensehost4d1-cli/src/main.rs
Normal file
1979
gen/adsensehost4d1-cli/src/main.rs
Normal file
File diff suppressed because it is too large
Load Diff
31
gen/audit1-cli/Cargo.toml
Normal file
31
gen/audit1-cli/Cargo.toml
Normal file
@@ -0,0 +1,31 @@
|
||||
# DO NOT EDIT !
|
||||
# This file was generated automatically from 'src/mako/Cargo.toml.mako'
|
||||
# DO NOT EDIT !
|
||||
[package]
|
||||
|
||||
name = "google-audit1-cli"
|
||||
version = "0.0.1+20150419"
|
||||
authors = ["Sebastian Thiel <byronimo@gmail.com>"]
|
||||
description = "A complete library to interact with audit (protocol v1)"
|
||||
repository = "https://github.com/Byron/google-apis-rs/tree/master/gen/audit1-cli"
|
||||
homepage = "https://developers.google.com/google-apps/admin-audit/get_started"
|
||||
documentation = "http://byron.github.io/google-apis-rs/google_audit1_cli"
|
||||
license = "MIT"
|
||||
keywords = ["audit", "google", "cli"]
|
||||
|
||||
[[bin]]
|
||||
name = "audit1"
|
||||
|
||||
[dependencies]
|
||||
hyper = "*"
|
||||
mime = "*"
|
||||
yup-oauth2 = "*"
|
||||
docopt = "*"
|
||||
docopt_macros = "*"
|
||||
rustc-serialize = "*"
|
||||
yup-hyper-mock = "*"
|
||||
serde = ">= 0.3.0"
|
||||
serde_macros = "*"
|
||||
|
||||
[dependencies.google-audit1]
|
||||
path = "../audit1"
|
||||
30
gen/audit1-cli/LICENSE.md
Normal file
30
gen/audit1-cli/LICENSE.md
Normal file
@@ -0,0 +1,30 @@
|
||||
<!---
|
||||
DO NOT EDIT !
|
||||
This file was generated automatically from 'src/mako/LICENSE.md.mako'
|
||||
DO NOT EDIT !
|
||||
-->
|
||||
The MIT License (MIT)
|
||||
=====================
|
||||
|
||||
Copyright © `2015` `Sebastian Thiel`
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the “Software”), to deal in the Software without
|
||||
restriction, including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
4
gen/audit1-cli/README.md
Normal file
4
gen/audit1-cli/README.md
Normal file
@@ -0,0 +1,4 @@
|
||||
# HELLO AUDIT:V1
|
||||
|
||||
|
||||
Include information about application secret files, and how we automatically write a default one.
|
||||
17
gen/audit1-cli/mkdocs.yml
Normal file
17
gen/audit1-cli/mkdocs.yml
Normal file
@@ -0,0 +1,17 @@
|
||||
site_name: audit v0.0.1+20150419
|
||||
site_url: http://byron.github.io/google-apis-rs/google-audit1-cli
|
||||
site_description: Write integrating applications with bcore
|
||||
|
||||
repo_url: https://github.com/Byron/google-apis-rs/tree/master/gen/audit1-cli
|
||||
|
||||
docs_dir: docs
|
||||
site_dir: build_html
|
||||
|
||||
pages:
|
||||
- ['index.md', 'Home']
|
||||
- ['activities_list.md', 'Activities', 'List']
|
||||
|
||||
theme: readthedocs
|
||||
|
||||
copyright: Copyright © 2015, `Sebastian Thiel`
|
||||
|
||||
454
gen/audit1-cli/src/cmn.rs
Normal file
454
gen/audit1-cli/src/cmn.rs
Normal file
@@ -0,0 +1,454 @@
|
||||
// COPY OF 'src/rust/cli/cmn.rs'
|
||||
// DO NOT EDIT
|
||||
use oauth2::{ApplicationSecret, ConsoleApplicationSecret, TokenStorage, Token};
|
||||
use rustc_serialize::json;
|
||||
use mime::Mime;
|
||||
|
||||
use std::fs;
|
||||
use std::env;
|
||||
use std::io;
|
||||
use std::fmt;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::str::FromStr;
|
||||
use std::string::ToString;
|
||||
use std::io::{Write, Read, stdout};
|
||||
|
||||
use std::default::Default;
|
||||
|
||||
const FIELD_SEP: char = '.';
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct FieldCursor(Vec<String>);
|
||||
|
||||
impl ToString for FieldCursor {
|
||||
fn to_string(&self) -> String {
|
||||
self.0.connect(".")
|
||||
}
|
||||
}
|
||||
|
||||
impl FieldCursor {
|
||||
pub fn set(&mut self, value: &str) -> Result<(), CLIError> {
|
||||
if value.len() == 0 {
|
||||
return Err(CLIError::Field(FieldError::Empty))
|
||||
}
|
||||
|
||||
let mut first_is_field_sep = false;
|
||||
let mut char_count: usize = 0;
|
||||
let mut last_c = FIELD_SEP;
|
||||
let mut num_conscutive_field_seps = 0;
|
||||
|
||||
let mut field = String::new();
|
||||
let mut fields = self.0.clone();
|
||||
|
||||
let push_field = |fs: &mut Vec<String>, f: &mut String| {
|
||||
if f.len() > 0 {
|
||||
fs.push(f.clone());
|
||||
f.truncate(0);
|
||||
}
|
||||
};
|
||||
|
||||
for (cid, c) in value.chars().enumerate() {
|
||||
char_count += 1;
|
||||
|
||||
if c == FIELD_SEP {
|
||||
if cid == 0 {
|
||||
first_is_field_sep = true;
|
||||
}
|
||||
num_conscutive_field_seps += 1;
|
||||
if cid > 0 && last_c == FIELD_SEP {
|
||||
if fields.pop().is_none() {
|
||||
return Err(CLIError::Field(FieldError::PopOnEmpty(value.to_string())))
|
||||
}
|
||||
} else {
|
||||
push_field(&mut fields, &mut field);
|
||||
}
|
||||
} else {
|
||||
num_conscutive_field_seps = 0;
|
||||
if cid == 1 {
|
||||
if first_is_field_sep {
|
||||
fields.truncate(0);
|
||||
}
|
||||
}
|
||||
field.push(c);
|
||||
}
|
||||
|
||||
last_c = c;
|
||||
}
|
||||
|
||||
push_field(&mut fields, &mut field);
|
||||
|
||||
if char_count == 1 && first_is_field_sep {
|
||||
fields.truncate(0);
|
||||
}
|
||||
if char_count > 1 && num_conscutive_field_seps == 1 {
|
||||
return Err(CLIError::Field(FieldError::TrailingFieldSep(value.to_string())))
|
||||
}
|
||||
|
||||
self.0 = fields;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn num_fields(&self) -> usize {
|
||||
self.0.len()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_kv_arg<'a>(kv: &'a str, err: &mut InvalidOptionsError, for_hashmap: bool)
|
||||
-> (&'a str, Option<&'a str>) {
|
||||
let mut add_err = || err.issues.push(CLIError::InvalidKeyValueSyntax(kv.to_string(),for_hashmap));
|
||||
match kv.find('=') {
|
||||
None => {
|
||||
add_err();
|
||||
return (kv, None)
|
||||
},
|
||||
Some(pos) => {
|
||||
let key = &kv[..pos];
|
||||
if kv.len() <= pos + 1 {
|
||||
add_err();
|
||||
return (key, None)
|
||||
}
|
||||
(key, Some(&kv[pos+1..]))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn input_file_from_opts(file_path: &str, err: &mut InvalidOptionsError) -> Option<fs::File> {
|
||||
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<Mime> {
|
||||
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<Write> {
|
||||
if !flag || arg == "-" {
|
||||
Box::new(stdout())
|
||||
} else {
|
||||
Box::new(fs::OpenOptions::new().create(true).write(true).open(arg).unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn arg_from_str<T>(arg: &str, err: &mut InvalidOptionsError,
|
||||
arg_name: &'static str,
|
||||
arg_type: &'static str) -> T
|
||||
where T: FromStr + Default,
|
||||
<T as FromStr>::Err: fmt::Display {
|
||||
match FromStr::from_str(arg) {
|
||||
Err(perr) => {
|
||||
err.issues.push(
|
||||
CLIError::ParseError((arg_name, arg_type, arg.to_string(), format!("{}", perr)))
|
||||
);
|
||||
Default::default()
|
||||
},
|
||||
Ok(v) => v,
|
||||
}
|
||||
}
|
||||
|
||||
pub struct JsonTokenStorage {
|
||||
pub program_name: &'static str,
|
||||
pub db_dir: String,
|
||||
}
|
||||
|
||||
impl JsonTokenStorage {
|
||||
fn path(&self, scope_hash: u64) -> PathBuf {
|
||||
Path::new(&self.db_dir).join(&format!("{}-token-{}.json", self.program_name, scope_hash))
|
||||
}
|
||||
}
|
||||
|
||||
impl TokenStorage for JsonTokenStorage {
|
||||
type Error = io::Error;
|
||||
|
||||
// NOTE: logging might be interesting, currently we swallow all errors
|
||||
fn set(&mut self, scope_hash: u64, _: &Vec<&str>, token: Option<Token>) -> Option<io::Error> {
|
||||
match token {
|
||||
None => {
|
||||
match fs::remove_file(self.path(scope_hash)) {
|
||||
Err(err) =>
|
||||
match err.kind() {
|
||||
io::ErrorKind::NotFound => None,
|
||||
_ => Some(err)
|
||||
},
|
||||
Ok(_) => None
|
||||
}
|
||||
}
|
||||
Some(token) => {
|
||||
let json_token = json::encode(&token).unwrap();
|
||||
match fs::OpenOptions::new().create(true).write(true).open(&self.path(scope_hash)) {
|
||||
Ok(mut f) => {
|
||||
match f.write(json_token.as_bytes()) {
|
||||
Ok(_) => None,
|
||||
Err(io_err) => Some(io_err),
|
||||
}
|
||||
},
|
||||
Err(io_err) => Some(io_err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get(&self, scope_hash: u64, _: &Vec<&str>) -> Result<Option<Token>, io::Error> {
|
||||
match fs::File::open(&self.path(scope_hash)) {
|
||||
Ok(mut f) => {
|
||||
let mut json_string = String::new();
|
||||
match f.read_to_string(&mut json_string) {
|
||||
Ok(_) => Ok(Some(json::decode::<Token>(&json_string).unwrap())),
|
||||
Err(io_err) => Err(io_err),
|
||||
}
|
||||
},
|
||||
Err(io_err) => {
|
||||
match io_err.kind() {
|
||||
io::ErrorKind::NotFound => Ok(None),
|
||||
_ => Err(io_err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug)]
|
||||
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)),
|
||||
}
|
||||
|
||||
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 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 FieldError {
|
||||
PopOnEmpty(String),
|
||||
TrailingFieldSep(String),
|
||||
Unknown(String),
|
||||
Empty,
|
||||
}
|
||||
|
||||
|
||||
impl fmt::Display for FieldError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
match *self {
|
||||
FieldError::PopOnEmpty(ref field)
|
||||
=> writeln!(f, "'{}': Cannot move up on empty field cursor", field),
|
||||
FieldError::TrailingFieldSep(ref field)
|
||||
=> writeln!(f, "'{}': Single field separator may not be last character", field),
|
||||
FieldError::Unknown(ref field)
|
||||
=> writeln!(f, "Field '{}' does not exist", field),
|
||||
FieldError::Empty
|
||||
=> writeln!(f, "Field names must not be empty"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum CLIError {
|
||||
Configuration(ConfigurationError),
|
||||
ParseError((&'static str, &'static str, String, String)),
|
||||
UnknownParameter(String),
|
||||
InvalidKeyValueSyntax(String, bool),
|
||||
Input(InputError),
|
||||
Field(FieldError),
|
||||
}
|
||||
|
||||
impl fmt::Display for CLIError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
match *self {
|
||||
CLIError::Configuration(ref err) => write!(f, "Configuration -> {}", err),
|
||||
CLIError::Input(ref err) => write!(f, "Input -> {}", err),
|
||||
CLIError::Field(ref err) => write!(f, "Field -> {}", 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),
|
||||
CLIError::UnknownParameter(ref param_name)
|
||||
=> writeln!(f, "Parameter '{}' is unknown.", param_name),
|
||||
CLIError::InvalidKeyValueSyntax(ref kv, is_hashmap) => {
|
||||
let hashmap_info = if is_hashmap { "hashmap " } else { "" };
|
||||
writeln!(f, "'{}' does not match {}pattern <key>=<value>", kv, hashmap_info)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct InvalidOptionsError {
|
||||
pub issues: Vec<CLIError>,
|
||||
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: CLIError, exit_code: i32) -> InvalidOptionsError {
|
||||
InvalidOptionsError {
|
||||
issues: vec![err],
|
||||
exit_code: exit_code,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new() -> InvalidOptionsError {
|
||||
InvalidOptionsError {
|
||||
issues: Vec::new(),
|
||||
exit_code: 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn assure_config_dir_exists(dir: &str) -> Result<String, CLIError> {
|
||||
let trdir = dir.trim();
|
||||
if trdir.len() == 0 {
|
||||
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(CLIError::Configuration(ConfigurationError::HomeExpansionFailed(trdir.to_string()))),
|
||||
Some(mut user) => {
|
||||
user.push_str(&trdir[1..]);
|
||||
user
|
||||
}
|
||||
}
|
||||
} else {
|
||||
trdir.to_string()
|
||||
};
|
||||
|
||||
if let Err(err) = fs::create_dir(&expanded_config_dir) {
|
||||
if err.kind() != io::ErrorKind::AlreadyExists {
|
||||
return Err(CLIError::Configuration(
|
||||
ConfigurationError::DirectoryCreationFailed((expanded_config_dir, err))))
|
||||
}
|
||||
}
|
||||
|
||||
Ok(expanded_config_dir)
|
||||
}
|
||||
|
||||
pub fn application_secret_from_directory(dir: &str,
|
||||
secret_basename: &str,
|
||||
json_app_secret: &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(CLIError::Configuration(ConfigurationError::IOError(
|
||||
(secret_str(), io_err)
|
||||
)))
|
||||
};
|
||||
|
||||
for _ in 0..2 {
|
||||
match fs::File::open(&secret_path) {
|
||||
Err(mut err) => {
|
||||
if err.kind() == io::ErrorKind::NotFound {
|
||||
// Write our built-in one - user may adjust the written file at will
|
||||
|
||||
err = match fs::OpenOptions::new().create(true).write(true).open(&secret_path) {
|
||||
Err(cfe) => cfe,
|
||||
Ok(mut f) => {
|
||||
match f.write(json_app_secret.as_bytes()) {
|
||||
Err(io_err) => io_err,
|
||||
Ok(_) => continue,
|
||||
}
|
||||
}
|
||||
};
|
||||
// fall through to IO error handling
|
||||
}
|
||||
return secret_io_error(err)
|
||||
},
|
||||
Ok(mut f) => {
|
||||
let mut json_encoded_secret = String::new();
|
||||
if let Err(io_err) = f.read_to_string(&mut json_encoded_secret) {
|
||||
return secret_io_error(io_err)
|
||||
}
|
||||
match json::decode::<ConsoleApplicationSecret>(&json_encoded_secret) {
|
||||
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(
|
||||
CLIError::Configuration(
|
||||
ConfigurationError::Secret(
|
||||
ApplicationSecretError::FormatError(secret_str())
|
||||
)))
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
unreachable!();
|
||||
}
|
||||
219
gen/audit1-cli/src/main.rs
Normal file
219
gen/audit1-cli/src/main.rs
Normal file
@@ -0,0 +1,219 @@
|
||||
// DO NOT EDIT !
|
||||
// This file was generated automatically from 'src/mako/cli/main.rs.mako'
|
||||
// DO NOT EDIT !
|
||||
#![feature(plugin, exit_status)]
|
||||
#![plugin(docopt_macros)]
|
||||
#![allow(unused_variables, unused_imports, dead_code, unused_mut)]
|
||||
|
||||
extern crate docopt;
|
||||
extern crate yup_oauth2 as oauth2;
|
||||
extern crate yup_hyper_mock as mock;
|
||||
extern crate rustc_serialize;
|
||||
extern crate serde;
|
||||
extern crate hyper;
|
||||
extern crate mime;
|
||||
extern crate google_audit1 as api;
|
||||
|
||||
use std::env;
|
||||
use std::io::{self, Write};
|
||||
|
||||
docopt!(Options derive Debug, "
|
||||
Usage:
|
||||
audit1 [options] activities list <customer-id> <application-id> [-p <v>]... [-o <out>]
|
||||
audit1 --help
|
||||
|
||||
All documentation details can be found TODO: <URL to github.io docs here, see #51>
|
||||
|
||||
Configuration:
|
||||
--config-dir <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: ~/.google-service-cli]
|
||||
--debug
|
||||
Output all server communication to standard error. `tx` and `rx` are placed into
|
||||
the same stream.
|
||||
--debug-auth
|
||||
Output all communication related to authentication to standard error. `tx` and `rx` are placed into
|
||||
the same stream.
|
||||
");
|
||||
|
||||
mod cmn;
|
||||
use cmn::{InvalidOptionsError, CLIError, JsonTokenStorage, arg_from_str, writer_from_opts, parse_kv_arg,
|
||||
input_file_from_opts, input_mime_from_opts, FieldCursor, FieldError};
|
||||
|
||||
use std::default::Default;
|
||||
use std::str::FromStr;
|
||||
|
||||
use oauth2::{Authenticator, DefaultAuthenticatorDelegate};
|
||||
use rustc_serialize::json;
|
||||
|
||||
struct Engine {
|
||||
opt: Options,
|
||||
hub: api::Audit<hyper::Client, Authenticator<DefaultAuthenticatorDelegate, JsonTokenStorage, hyper::Client>>,
|
||||
}
|
||||
|
||||
|
||||
impl Engine {
|
||||
fn _activities_list(&self, dry_run: bool, err: &mut InvalidOptionsError)
|
||||
-> Option<api::Error> {
|
||||
let mut call = self.hub.activities().list(&self.opt.arg_customer_id, &self.opt.arg_application_id);
|
||||
for parg in self.opt.arg_v.iter() {
|
||||
let (key, value) = parse_kv_arg(&*parg, err, false);
|
||||
match key {
|
||||
"start-time" => {
|
||||
call = call.start_time(value.unwrap_or(""));
|
||||
},
|
||||
"max-results" => {
|
||||
call = call.max_results(arg_from_str(value.unwrap_or("-0"), err, "max-results", "integer"));
|
||||
},
|
||||
"event-name" => {
|
||||
call = call.event_name(value.unwrap_or(""));
|
||||
},
|
||||
"end-time" => {
|
||||
call = call.end_time(value.unwrap_or(""));
|
||||
},
|
||||
"continuation-token" => {
|
||||
call = call.continuation_token(value.unwrap_or(""));
|
||||
},
|
||||
"caller" => {
|
||||
call = call.caller(value.unwrap_or(""));
|
||||
},
|
||||
"actor-ip-address" => {
|
||||
call = call.actor_ip_address(value.unwrap_or(""));
|
||||
},
|
||||
"actor-email" => {
|
||||
call = call.actor_email(value.unwrap_or(""));
|
||||
},
|
||||
"actor-application-id" => {
|
||||
call = call.actor_application_id(value.unwrap_or(""));
|
||||
},
|
||||
"alt"
|
||||
|"fields"
|
||||
|"key"
|
||||
|"oauth-token"
|
||||
|"pretty-print"
|
||||
|"quota-user"
|
||||
|"user-ip" => {
|
||||
let map = [
|
||||
("oauth-token", "oauth_token"),
|
||||
("pretty-print", "prettyPrint"),
|
||||
("quota-user", "quotaUser"),
|
||||
("user-ip", "userIp"),
|
||||
];
|
||||
call = call.param(map.iter().find(|t| t.0 == key).unwrap_or(&("", key)).1, value.unwrap_or("unset"))
|
||||
},
|
||||
_ => err.issues.push(CLIError::UnknownParameter(key.to_string())),
|
||||
}
|
||||
}
|
||||
let protocol = "standard-request";
|
||||
if dry_run {
|
||||
None
|
||||
} else {
|
||||
assert!(err.issues.len() == 0);
|
||||
let mut ostream = writer_from_opts(self.opt.flag_o, &self.opt.arg_out);
|
||||
match match protocol {
|
||||
"standard-request" => call.doit(),
|
||||
_ => unreachable!(),
|
||||
} {
|
||||
Err(api_err) => Some(api_err),
|
||||
Ok((mut response, output_schema)) => {
|
||||
serde::json::to_writer_pretty(&mut ostream, &output_schema).unwrap();
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn _doit(&self, dry_run: bool) -> (Option<api::Error>, Option<InvalidOptionsError>) {
|
||||
let mut err = InvalidOptionsError::new();
|
||||
let mut call_result: Option<api::Error>;
|
||||
let mut err_opt: Option<InvalidOptionsError> = None;
|
||||
|
||||
if self.opt.cmd_activities {
|
||||
if self.opt.cmd_list {
|
||||
call_result = self._activities_list(dry_run, &mut err);
|
||||
} else {
|
||||
unreachable!();
|
||||
}
|
||||
} else {
|
||||
unreachable!();
|
||||
}
|
||||
|
||||
if dry_run {
|
||||
if err.issues.len() > 0 {
|
||||
err_opt = Some(err);
|
||||
}
|
||||
}
|
||||
(call_result, err_opt)
|
||||
}
|
||||
|
||||
// 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(&opt.flag_config_dir) {
|
||||
Err(e) => return Err(InvalidOptionsError::single(e, 3)),
|
||||
Ok(p) => p,
|
||||
};
|
||||
|
||||
match cmn::application_secret_from_directory(&config_dir, "audit1-secret.json",
|
||||
"{\"installed\":{\"auth_uri\":\"https://accounts.google.com/o/oauth2/auth\",\"client_secret\":\"hCsslbCUyfehWMmbkG8vTYxG\",\"token_uri\":\"https://accounts.google.com/o/oauth2/token\",\"client_email\":\"\",\"redirect_uris\":[\"urn:ietf:wg:oauth:2.0:oob\",\"oob\"],\"client_x509_cert_url\":\"\",\"client_id\":\"620010449518-9ngf7o4dhs0dka470npqvor6dc5lqb9b.apps.googleusercontent.com\",\"auth_provider_x509_cert_url\":\"https://www.googleapis.com/oauth2/v1/certs\"}}") {
|
||||
Ok(secret) => (config_dir, secret),
|
||||
Err(e) => return Err(InvalidOptionsError::single(e, 4))
|
||||
}
|
||||
};
|
||||
|
||||
let auth = Authenticator::new( &secret, DefaultAuthenticatorDelegate,
|
||||
if opt.flag_debug_auth {
|
||||
hyper::Client::with_connector(mock::TeeConnector {
|
||||
connector: hyper::net::HttpConnector(None)
|
||||
})
|
||||
} else {
|
||||
hyper::Client::new()
|
||||
},
|
||||
JsonTokenStorage {
|
||||
program_name: "audit1",
|
||||
db_dir: config_dir.clone(),
|
||||
}, None);
|
||||
|
||||
let client =
|
||||
if opt.flag_debug {
|
||||
hyper::Client::with_connector(mock::TeeConnector {
|
||||
connector: hyper::net::HttpConnector(None)
|
||||
})
|
||||
} else {
|
||||
hyper::Client::new()
|
||||
};
|
||||
let engine = Engine {
|
||||
opt: opt,
|
||||
hub: api::Audit::new(client, auth),
|
||||
};
|
||||
|
||||
match engine._doit(true) {
|
||||
(_, Some(err)) => Err(err),
|
||||
_ => 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> {
|
||||
self._doit(false).0
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let opts: Options = Options::docopt().decode().unwrap_or_else(|e| e.exit());
|
||||
match Engine::new(opts) {
|
||||
Err(err) => {
|
||||
writeln!(io::stderr(), "{}", err).ok();
|
||||
env::set_exit_status(err.exit_code);
|
||||
},
|
||||
Ok(engine) => {
|
||||
if let Some(err) = engine.doit() {
|
||||
writeln!(io::stderr(), "{:?}", err).ok();
|
||||
writeln!(io::stderr(), "{}", err).ok();
|
||||
env::set_exit_status(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
31
gen/coordinate1-cli/Cargo.toml
Normal file
31
gen/coordinate1-cli/Cargo.toml
Normal file
@@ -0,0 +1,31 @@
|
||||
# DO NOT EDIT !
|
||||
# This file was generated automatically from 'src/mako/Cargo.toml.mako'
|
||||
# DO NOT EDIT !
|
||||
[package]
|
||||
|
||||
name = "google-coordinate1-cli"
|
||||
version = "0.0.1+20141215"
|
||||
authors = ["Sebastian Thiel <byronimo@gmail.com>"]
|
||||
description = "A complete library to interact with coordinate (protocol v1)"
|
||||
repository = "https://github.com/Byron/google-apis-rs/tree/master/gen/coordinate1-cli"
|
||||
homepage = "https://developers.google.com/coordinate/"
|
||||
documentation = "http://byron.github.io/google-apis-rs/google_coordinate1_cli"
|
||||
license = "MIT"
|
||||
keywords = ["coordinate", "google", "cli"]
|
||||
|
||||
[[bin]]
|
||||
name = "coordinate1"
|
||||
|
||||
[dependencies]
|
||||
hyper = "*"
|
||||
mime = "*"
|
||||
yup-oauth2 = "*"
|
||||
docopt = "*"
|
||||
docopt_macros = "*"
|
||||
rustc-serialize = "*"
|
||||
yup-hyper-mock = "*"
|
||||
serde = ">= 0.3.0"
|
||||
serde_macros = "*"
|
||||
|
||||
[dependencies.google-coordinate1]
|
||||
path = "../coordinate1"
|
||||
30
gen/coordinate1-cli/LICENSE.md
Normal file
30
gen/coordinate1-cli/LICENSE.md
Normal file
@@ -0,0 +1,30 @@
|
||||
<!---
|
||||
DO NOT EDIT !
|
||||
This file was generated automatically from 'src/mako/LICENSE.md.mako'
|
||||
DO NOT EDIT !
|
||||
-->
|
||||
The MIT License (MIT)
|
||||
=====================
|
||||
|
||||
Copyright © `2015` `Sebastian Thiel`
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the “Software”), to deal in the Software without
|
||||
restriction, including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
4
gen/coordinate1-cli/README.md
Normal file
4
gen/coordinate1-cli/README.md
Normal file
@@ -0,0 +1,4 @@
|
||||
# HELLO COORDINATE:V1
|
||||
|
||||
|
||||
Include information about application secret files, and how we automatically write a default one.
|
||||
28
gen/coordinate1-cli/mkdocs.yml
Normal file
28
gen/coordinate1-cli/mkdocs.yml
Normal file
@@ -0,0 +1,28 @@
|
||||
site_name: coordinate v0.0.1+20141215
|
||||
site_url: http://byron.github.io/google-apis-rs/google-coordinate1-cli
|
||||
site_description: Write integrating applications with bcore
|
||||
|
||||
repo_url: https://github.com/Byron/google-apis-rs/tree/master/gen/coordinate1-cli
|
||||
|
||||
docs_dir: docs
|
||||
site_dir: build_html
|
||||
|
||||
pages:
|
||||
- ['index.md', 'Home']
|
||||
- ['custom-field-def_list.md', 'Custom Field Def', 'List']
|
||||
- ['jobs_get.md', 'Jobs', 'Get']
|
||||
- ['jobs_insert.md', 'Jobs', 'Insert']
|
||||
- ['jobs_list.md', 'Jobs', 'List']
|
||||
- ['jobs_patch.md', 'Jobs', 'Patch']
|
||||
- ['jobs_update.md', 'Jobs', 'Update']
|
||||
- ['location_list.md', 'Location', 'List']
|
||||
- ['schedule_get.md', 'Schedule', 'Get']
|
||||
- ['schedule_patch.md', 'Schedule', 'Patch']
|
||||
- ['schedule_update.md', 'Schedule', 'Update']
|
||||
- ['team_list.md', 'Team', 'List']
|
||||
- ['worker_list.md', 'Worker', 'List']
|
||||
|
||||
theme: readthedocs
|
||||
|
||||
copyright: Copyright © 2015, `Sebastian Thiel`
|
||||
|
||||
454
gen/coordinate1-cli/src/cmn.rs
Normal file
454
gen/coordinate1-cli/src/cmn.rs
Normal file
@@ -0,0 +1,454 @@
|
||||
// COPY OF 'src/rust/cli/cmn.rs'
|
||||
// DO NOT EDIT
|
||||
use oauth2::{ApplicationSecret, ConsoleApplicationSecret, TokenStorage, Token};
|
||||
use rustc_serialize::json;
|
||||
use mime::Mime;
|
||||
|
||||
use std::fs;
|
||||
use std::env;
|
||||
use std::io;
|
||||
use std::fmt;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::str::FromStr;
|
||||
use std::string::ToString;
|
||||
use std::io::{Write, Read, stdout};
|
||||
|
||||
use std::default::Default;
|
||||
|
||||
const FIELD_SEP: char = '.';
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct FieldCursor(Vec<String>);
|
||||
|
||||
impl ToString for FieldCursor {
|
||||
fn to_string(&self) -> String {
|
||||
self.0.connect(".")
|
||||
}
|
||||
}
|
||||
|
||||
impl FieldCursor {
|
||||
pub fn set(&mut self, value: &str) -> Result<(), CLIError> {
|
||||
if value.len() == 0 {
|
||||
return Err(CLIError::Field(FieldError::Empty))
|
||||
}
|
||||
|
||||
let mut first_is_field_sep = false;
|
||||
let mut char_count: usize = 0;
|
||||
let mut last_c = FIELD_SEP;
|
||||
let mut num_conscutive_field_seps = 0;
|
||||
|
||||
let mut field = String::new();
|
||||
let mut fields = self.0.clone();
|
||||
|
||||
let push_field = |fs: &mut Vec<String>, f: &mut String| {
|
||||
if f.len() > 0 {
|
||||
fs.push(f.clone());
|
||||
f.truncate(0);
|
||||
}
|
||||
};
|
||||
|
||||
for (cid, c) in value.chars().enumerate() {
|
||||
char_count += 1;
|
||||
|
||||
if c == FIELD_SEP {
|
||||
if cid == 0 {
|
||||
first_is_field_sep = true;
|
||||
}
|
||||
num_conscutive_field_seps += 1;
|
||||
if cid > 0 && last_c == FIELD_SEP {
|
||||
if fields.pop().is_none() {
|
||||
return Err(CLIError::Field(FieldError::PopOnEmpty(value.to_string())))
|
||||
}
|
||||
} else {
|
||||
push_field(&mut fields, &mut field);
|
||||
}
|
||||
} else {
|
||||
num_conscutive_field_seps = 0;
|
||||
if cid == 1 {
|
||||
if first_is_field_sep {
|
||||
fields.truncate(0);
|
||||
}
|
||||
}
|
||||
field.push(c);
|
||||
}
|
||||
|
||||
last_c = c;
|
||||
}
|
||||
|
||||
push_field(&mut fields, &mut field);
|
||||
|
||||
if char_count == 1 && first_is_field_sep {
|
||||
fields.truncate(0);
|
||||
}
|
||||
if char_count > 1 && num_conscutive_field_seps == 1 {
|
||||
return Err(CLIError::Field(FieldError::TrailingFieldSep(value.to_string())))
|
||||
}
|
||||
|
||||
self.0 = fields;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn num_fields(&self) -> usize {
|
||||
self.0.len()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_kv_arg<'a>(kv: &'a str, err: &mut InvalidOptionsError, for_hashmap: bool)
|
||||
-> (&'a str, Option<&'a str>) {
|
||||
let mut add_err = || err.issues.push(CLIError::InvalidKeyValueSyntax(kv.to_string(),for_hashmap));
|
||||
match kv.find('=') {
|
||||
None => {
|
||||
add_err();
|
||||
return (kv, None)
|
||||
},
|
||||
Some(pos) => {
|
||||
let key = &kv[..pos];
|
||||
if kv.len() <= pos + 1 {
|
||||
add_err();
|
||||
return (key, None)
|
||||
}
|
||||
(key, Some(&kv[pos+1..]))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn input_file_from_opts(file_path: &str, err: &mut InvalidOptionsError) -> Option<fs::File> {
|
||||
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<Mime> {
|
||||
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<Write> {
|
||||
if !flag || arg == "-" {
|
||||
Box::new(stdout())
|
||||
} else {
|
||||
Box::new(fs::OpenOptions::new().create(true).write(true).open(arg).unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn arg_from_str<T>(arg: &str, err: &mut InvalidOptionsError,
|
||||
arg_name: &'static str,
|
||||
arg_type: &'static str) -> T
|
||||
where T: FromStr + Default,
|
||||
<T as FromStr>::Err: fmt::Display {
|
||||
match FromStr::from_str(arg) {
|
||||
Err(perr) => {
|
||||
err.issues.push(
|
||||
CLIError::ParseError((arg_name, arg_type, arg.to_string(), format!("{}", perr)))
|
||||
);
|
||||
Default::default()
|
||||
},
|
||||
Ok(v) => v,
|
||||
}
|
||||
}
|
||||
|
||||
pub struct JsonTokenStorage {
|
||||
pub program_name: &'static str,
|
||||
pub db_dir: String,
|
||||
}
|
||||
|
||||
impl JsonTokenStorage {
|
||||
fn path(&self, scope_hash: u64) -> PathBuf {
|
||||
Path::new(&self.db_dir).join(&format!("{}-token-{}.json", self.program_name, scope_hash))
|
||||
}
|
||||
}
|
||||
|
||||
impl TokenStorage for JsonTokenStorage {
|
||||
type Error = io::Error;
|
||||
|
||||
// NOTE: logging might be interesting, currently we swallow all errors
|
||||
fn set(&mut self, scope_hash: u64, _: &Vec<&str>, token: Option<Token>) -> Option<io::Error> {
|
||||
match token {
|
||||
None => {
|
||||
match fs::remove_file(self.path(scope_hash)) {
|
||||
Err(err) =>
|
||||
match err.kind() {
|
||||
io::ErrorKind::NotFound => None,
|
||||
_ => Some(err)
|
||||
},
|
||||
Ok(_) => None
|
||||
}
|
||||
}
|
||||
Some(token) => {
|
||||
let json_token = json::encode(&token).unwrap();
|
||||
match fs::OpenOptions::new().create(true).write(true).open(&self.path(scope_hash)) {
|
||||
Ok(mut f) => {
|
||||
match f.write(json_token.as_bytes()) {
|
||||
Ok(_) => None,
|
||||
Err(io_err) => Some(io_err),
|
||||
}
|
||||
},
|
||||
Err(io_err) => Some(io_err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get(&self, scope_hash: u64, _: &Vec<&str>) -> Result<Option<Token>, io::Error> {
|
||||
match fs::File::open(&self.path(scope_hash)) {
|
||||
Ok(mut f) => {
|
||||
let mut json_string = String::new();
|
||||
match f.read_to_string(&mut json_string) {
|
||||
Ok(_) => Ok(Some(json::decode::<Token>(&json_string).unwrap())),
|
||||
Err(io_err) => Err(io_err),
|
||||
}
|
||||
},
|
||||
Err(io_err) => {
|
||||
match io_err.kind() {
|
||||
io::ErrorKind::NotFound => Ok(None),
|
||||
_ => Err(io_err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug)]
|
||||
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)),
|
||||
}
|
||||
|
||||
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 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 FieldError {
|
||||
PopOnEmpty(String),
|
||||
TrailingFieldSep(String),
|
||||
Unknown(String),
|
||||
Empty,
|
||||
}
|
||||
|
||||
|
||||
impl fmt::Display for FieldError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
match *self {
|
||||
FieldError::PopOnEmpty(ref field)
|
||||
=> writeln!(f, "'{}': Cannot move up on empty field cursor", field),
|
||||
FieldError::TrailingFieldSep(ref field)
|
||||
=> writeln!(f, "'{}': Single field separator may not be last character", field),
|
||||
FieldError::Unknown(ref field)
|
||||
=> writeln!(f, "Field '{}' does not exist", field),
|
||||
FieldError::Empty
|
||||
=> writeln!(f, "Field names must not be empty"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum CLIError {
|
||||
Configuration(ConfigurationError),
|
||||
ParseError((&'static str, &'static str, String, String)),
|
||||
UnknownParameter(String),
|
||||
InvalidKeyValueSyntax(String, bool),
|
||||
Input(InputError),
|
||||
Field(FieldError),
|
||||
}
|
||||
|
||||
impl fmt::Display for CLIError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
match *self {
|
||||
CLIError::Configuration(ref err) => write!(f, "Configuration -> {}", err),
|
||||
CLIError::Input(ref err) => write!(f, "Input -> {}", err),
|
||||
CLIError::Field(ref err) => write!(f, "Field -> {}", 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),
|
||||
CLIError::UnknownParameter(ref param_name)
|
||||
=> writeln!(f, "Parameter '{}' is unknown.", param_name),
|
||||
CLIError::InvalidKeyValueSyntax(ref kv, is_hashmap) => {
|
||||
let hashmap_info = if is_hashmap { "hashmap " } else { "" };
|
||||
writeln!(f, "'{}' does not match {}pattern <key>=<value>", kv, hashmap_info)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct InvalidOptionsError {
|
||||
pub issues: Vec<CLIError>,
|
||||
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: CLIError, exit_code: i32) -> InvalidOptionsError {
|
||||
InvalidOptionsError {
|
||||
issues: vec![err],
|
||||
exit_code: exit_code,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new() -> InvalidOptionsError {
|
||||
InvalidOptionsError {
|
||||
issues: Vec::new(),
|
||||
exit_code: 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn assure_config_dir_exists(dir: &str) -> Result<String, CLIError> {
|
||||
let trdir = dir.trim();
|
||||
if trdir.len() == 0 {
|
||||
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(CLIError::Configuration(ConfigurationError::HomeExpansionFailed(trdir.to_string()))),
|
||||
Some(mut user) => {
|
||||
user.push_str(&trdir[1..]);
|
||||
user
|
||||
}
|
||||
}
|
||||
} else {
|
||||
trdir.to_string()
|
||||
};
|
||||
|
||||
if let Err(err) = fs::create_dir(&expanded_config_dir) {
|
||||
if err.kind() != io::ErrorKind::AlreadyExists {
|
||||
return Err(CLIError::Configuration(
|
||||
ConfigurationError::DirectoryCreationFailed((expanded_config_dir, err))))
|
||||
}
|
||||
}
|
||||
|
||||
Ok(expanded_config_dir)
|
||||
}
|
||||
|
||||
pub fn application_secret_from_directory(dir: &str,
|
||||
secret_basename: &str,
|
||||
json_app_secret: &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(CLIError::Configuration(ConfigurationError::IOError(
|
||||
(secret_str(), io_err)
|
||||
)))
|
||||
};
|
||||
|
||||
for _ in 0..2 {
|
||||
match fs::File::open(&secret_path) {
|
||||
Err(mut err) => {
|
||||
if err.kind() == io::ErrorKind::NotFound {
|
||||
// Write our built-in one - user may adjust the written file at will
|
||||
|
||||
err = match fs::OpenOptions::new().create(true).write(true).open(&secret_path) {
|
||||
Err(cfe) => cfe,
|
||||
Ok(mut f) => {
|
||||
match f.write(json_app_secret.as_bytes()) {
|
||||
Err(io_err) => io_err,
|
||||
Ok(_) => continue,
|
||||
}
|
||||
}
|
||||
};
|
||||
// fall through to IO error handling
|
||||
}
|
||||
return secret_io_error(err)
|
||||
},
|
||||
Ok(mut f) => {
|
||||
let mut json_encoded_secret = String::new();
|
||||
if let Err(io_err) = f.read_to_string(&mut json_encoded_secret) {
|
||||
return secret_io_error(io_err)
|
||||
}
|
||||
match json::decode::<ConsoleApplicationSecret>(&json_encoded_secret) {
|
||||
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(
|
||||
CLIError::Configuration(
|
||||
ConfigurationError::Secret(
|
||||
ApplicationSecretError::FormatError(secret_str())
|
||||
)))
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
unreachable!();
|
||||
}
|
||||
1192
gen/coordinate1-cli/src/main.rs
Normal file
1192
gen/coordinate1-cli/src/main.rs
Normal file
File diff suppressed because it is too large
Load Diff
31
gen/customsearch1-cli/Cargo.toml
Normal file
31
gen/customsearch1-cli/Cargo.toml
Normal file
@@ -0,0 +1,31 @@
|
||||
# DO NOT EDIT !
|
||||
# This file was generated automatically from 'src/mako/Cargo.toml.mako'
|
||||
# DO NOT EDIT !
|
||||
[package]
|
||||
|
||||
name = "google-customsearch1-cli"
|
||||
version = "0.0.1+20131205"
|
||||
authors = ["Sebastian Thiel <byronimo@gmail.com>"]
|
||||
description = "A complete library to interact with customsearch (protocol v1)"
|
||||
repository = "https://github.com/Byron/google-apis-rs/tree/master/gen/customsearch1-cli"
|
||||
homepage = "https://developers.google.com/custom-search/v1/using_rest"
|
||||
documentation = "http://byron.github.io/google-apis-rs/google_customsearch1_cli"
|
||||
license = "MIT"
|
||||
keywords = ["customsearch", "google", "cli"]
|
||||
|
||||
[[bin]]
|
||||
name = "customsearch1"
|
||||
|
||||
[dependencies]
|
||||
hyper = "*"
|
||||
mime = "*"
|
||||
yup-oauth2 = "*"
|
||||
docopt = "*"
|
||||
docopt_macros = "*"
|
||||
rustc-serialize = "*"
|
||||
yup-hyper-mock = "*"
|
||||
serde = ">= 0.3.0"
|
||||
serde_macros = "*"
|
||||
|
||||
[dependencies.google-customsearch1]
|
||||
path = "../customsearch1"
|
||||
30
gen/customsearch1-cli/LICENSE.md
Normal file
30
gen/customsearch1-cli/LICENSE.md
Normal file
@@ -0,0 +1,30 @@
|
||||
<!---
|
||||
DO NOT EDIT !
|
||||
This file was generated automatically from 'src/mako/LICENSE.md.mako'
|
||||
DO NOT EDIT !
|
||||
-->
|
||||
The MIT License (MIT)
|
||||
=====================
|
||||
|
||||
Copyright © `2015` `Sebastian Thiel`
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the “Software”), to deal in the Software without
|
||||
restriction, including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
4
gen/customsearch1-cli/README.md
Normal file
4
gen/customsearch1-cli/README.md
Normal file
@@ -0,0 +1,4 @@
|
||||
# HELLO CUSTOMSEARCH:V1
|
||||
|
||||
|
||||
Include information about application secret files, and how we automatically write a default one.
|
||||
17
gen/customsearch1-cli/mkdocs.yml
Normal file
17
gen/customsearch1-cli/mkdocs.yml
Normal file
@@ -0,0 +1,17 @@
|
||||
site_name: customsearch v0.0.1+20131205
|
||||
site_url: http://byron.github.io/google-apis-rs/google-customsearch1-cli
|
||||
site_description: Write integrating applications with bcore
|
||||
|
||||
repo_url: https://github.com/Byron/google-apis-rs/tree/master/gen/customsearch1-cli
|
||||
|
||||
docs_dir: docs
|
||||
site_dir: build_html
|
||||
|
||||
pages:
|
||||
- ['index.md', 'Home']
|
||||
- ['cse_list.md', 'Cse', 'List']
|
||||
|
||||
theme: readthedocs
|
||||
|
||||
copyright: Copyright © 2015, `Sebastian Thiel`
|
||||
|
||||
454
gen/customsearch1-cli/src/cmn.rs
Normal file
454
gen/customsearch1-cli/src/cmn.rs
Normal file
@@ -0,0 +1,454 @@
|
||||
// COPY OF 'src/rust/cli/cmn.rs'
|
||||
// DO NOT EDIT
|
||||
use oauth2::{ApplicationSecret, ConsoleApplicationSecret, TokenStorage, Token};
|
||||
use rustc_serialize::json;
|
||||
use mime::Mime;
|
||||
|
||||
use std::fs;
|
||||
use std::env;
|
||||
use std::io;
|
||||
use std::fmt;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::str::FromStr;
|
||||
use std::string::ToString;
|
||||
use std::io::{Write, Read, stdout};
|
||||
|
||||
use std::default::Default;
|
||||
|
||||
const FIELD_SEP: char = '.';
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct FieldCursor(Vec<String>);
|
||||
|
||||
impl ToString for FieldCursor {
|
||||
fn to_string(&self) -> String {
|
||||
self.0.connect(".")
|
||||
}
|
||||
}
|
||||
|
||||
impl FieldCursor {
|
||||
pub fn set(&mut self, value: &str) -> Result<(), CLIError> {
|
||||
if value.len() == 0 {
|
||||
return Err(CLIError::Field(FieldError::Empty))
|
||||
}
|
||||
|
||||
let mut first_is_field_sep = false;
|
||||
let mut char_count: usize = 0;
|
||||
let mut last_c = FIELD_SEP;
|
||||
let mut num_conscutive_field_seps = 0;
|
||||
|
||||
let mut field = String::new();
|
||||
let mut fields = self.0.clone();
|
||||
|
||||
let push_field = |fs: &mut Vec<String>, f: &mut String| {
|
||||
if f.len() > 0 {
|
||||
fs.push(f.clone());
|
||||
f.truncate(0);
|
||||
}
|
||||
};
|
||||
|
||||
for (cid, c) in value.chars().enumerate() {
|
||||
char_count += 1;
|
||||
|
||||
if c == FIELD_SEP {
|
||||
if cid == 0 {
|
||||
first_is_field_sep = true;
|
||||
}
|
||||
num_conscutive_field_seps += 1;
|
||||
if cid > 0 && last_c == FIELD_SEP {
|
||||
if fields.pop().is_none() {
|
||||
return Err(CLIError::Field(FieldError::PopOnEmpty(value.to_string())))
|
||||
}
|
||||
} else {
|
||||
push_field(&mut fields, &mut field);
|
||||
}
|
||||
} else {
|
||||
num_conscutive_field_seps = 0;
|
||||
if cid == 1 {
|
||||
if first_is_field_sep {
|
||||
fields.truncate(0);
|
||||
}
|
||||
}
|
||||
field.push(c);
|
||||
}
|
||||
|
||||
last_c = c;
|
||||
}
|
||||
|
||||
push_field(&mut fields, &mut field);
|
||||
|
||||
if char_count == 1 && first_is_field_sep {
|
||||
fields.truncate(0);
|
||||
}
|
||||
if char_count > 1 && num_conscutive_field_seps == 1 {
|
||||
return Err(CLIError::Field(FieldError::TrailingFieldSep(value.to_string())))
|
||||
}
|
||||
|
||||
self.0 = fields;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn num_fields(&self) -> usize {
|
||||
self.0.len()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_kv_arg<'a>(kv: &'a str, err: &mut InvalidOptionsError, for_hashmap: bool)
|
||||
-> (&'a str, Option<&'a str>) {
|
||||
let mut add_err = || err.issues.push(CLIError::InvalidKeyValueSyntax(kv.to_string(),for_hashmap));
|
||||
match kv.find('=') {
|
||||
None => {
|
||||
add_err();
|
||||
return (kv, None)
|
||||
},
|
||||
Some(pos) => {
|
||||
let key = &kv[..pos];
|
||||
if kv.len() <= pos + 1 {
|
||||
add_err();
|
||||
return (key, None)
|
||||
}
|
||||
(key, Some(&kv[pos+1..]))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn input_file_from_opts(file_path: &str, err: &mut InvalidOptionsError) -> Option<fs::File> {
|
||||
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<Mime> {
|
||||
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<Write> {
|
||||
if !flag || arg == "-" {
|
||||
Box::new(stdout())
|
||||
} else {
|
||||
Box::new(fs::OpenOptions::new().create(true).write(true).open(arg).unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn arg_from_str<T>(arg: &str, err: &mut InvalidOptionsError,
|
||||
arg_name: &'static str,
|
||||
arg_type: &'static str) -> T
|
||||
where T: FromStr + Default,
|
||||
<T as FromStr>::Err: fmt::Display {
|
||||
match FromStr::from_str(arg) {
|
||||
Err(perr) => {
|
||||
err.issues.push(
|
||||
CLIError::ParseError((arg_name, arg_type, arg.to_string(), format!("{}", perr)))
|
||||
);
|
||||
Default::default()
|
||||
},
|
||||
Ok(v) => v,
|
||||
}
|
||||
}
|
||||
|
||||
pub struct JsonTokenStorage {
|
||||
pub program_name: &'static str,
|
||||
pub db_dir: String,
|
||||
}
|
||||
|
||||
impl JsonTokenStorage {
|
||||
fn path(&self, scope_hash: u64) -> PathBuf {
|
||||
Path::new(&self.db_dir).join(&format!("{}-token-{}.json", self.program_name, scope_hash))
|
||||
}
|
||||
}
|
||||
|
||||
impl TokenStorage for JsonTokenStorage {
|
||||
type Error = io::Error;
|
||||
|
||||
// NOTE: logging might be interesting, currently we swallow all errors
|
||||
fn set(&mut self, scope_hash: u64, _: &Vec<&str>, token: Option<Token>) -> Option<io::Error> {
|
||||
match token {
|
||||
None => {
|
||||
match fs::remove_file(self.path(scope_hash)) {
|
||||
Err(err) =>
|
||||
match err.kind() {
|
||||
io::ErrorKind::NotFound => None,
|
||||
_ => Some(err)
|
||||
},
|
||||
Ok(_) => None
|
||||
}
|
||||
}
|
||||
Some(token) => {
|
||||
let json_token = json::encode(&token).unwrap();
|
||||
match fs::OpenOptions::new().create(true).write(true).open(&self.path(scope_hash)) {
|
||||
Ok(mut f) => {
|
||||
match f.write(json_token.as_bytes()) {
|
||||
Ok(_) => None,
|
||||
Err(io_err) => Some(io_err),
|
||||
}
|
||||
},
|
||||
Err(io_err) => Some(io_err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get(&self, scope_hash: u64, _: &Vec<&str>) -> Result<Option<Token>, io::Error> {
|
||||
match fs::File::open(&self.path(scope_hash)) {
|
||||
Ok(mut f) => {
|
||||
let mut json_string = String::new();
|
||||
match f.read_to_string(&mut json_string) {
|
||||
Ok(_) => Ok(Some(json::decode::<Token>(&json_string).unwrap())),
|
||||
Err(io_err) => Err(io_err),
|
||||
}
|
||||
},
|
||||
Err(io_err) => {
|
||||
match io_err.kind() {
|
||||
io::ErrorKind::NotFound => Ok(None),
|
||||
_ => Err(io_err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug)]
|
||||
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)),
|
||||
}
|
||||
|
||||
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 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 FieldError {
|
||||
PopOnEmpty(String),
|
||||
TrailingFieldSep(String),
|
||||
Unknown(String),
|
||||
Empty,
|
||||
}
|
||||
|
||||
|
||||
impl fmt::Display for FieldError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
match *self {
|
||||
FieldError::PopOnEmpty(ref field)
|
||||
=> writeln!(f, "'{}': Cannot move up on empty field cursor", field),
|
||||
FieldError::TrailingFieldSep(ref field)
|
||||
=> writeln!(f, "'{}': Single field separator may not be last character", field),
|
||||
FieldError::Unknown(ref field)
|
||||
=> writeln!(f, "Field '{}' does not exist", field),
|
||||
FieldError::Empty
|
||||
=> writeln!(f, "Field names must not be empty"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum CLIError {
|
||||
Configuration(ConfigurationError),
|
||||
ParseError((&'static str, &'static str, String, String)),
|
||||
UnknownParameter(String),
|
||||
InvalidKeyValueSyntax(String, bool),
|
||||
Input(InputError),
|
||||
Field(FieldError),
|
||||
}
|
||||
|
||||
impl fmt::Display for CLIError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
match *self {
|
||||
CLIError::Configuration(ref err) => write!(f, "Configuration -> {}", err),
|
||||
CLIError::Input(ref err) => write!(f, "Input -> {}", err),
|
||||
CLIError::Field(ref err) => write!(f, "Field -> {}", 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),
|
||||
CLIError::UnknownParameter(ref param_name)
|
||||
=> writeln!(f, "Parameter '{}' is unknown.", param_name),
|
||||
CLIError::InvalidKeyValueSyntax(ref kv, is_hashmap) => {
|
||||
let hashmap_info = if is_hashmap { "hashmap " } else { "" };
|
||||
writeln!(f, "'{}' does not match {}pattern <key>=<value>", kv, hashmap_info)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct InvalidOptionsError {
|
||||
pub issues: Vec<CLIError>,
|
||||
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: CLIError, exit_code: i32) -> InvalidOptionsError {
|
||||
InvalidOptionsError {
|
||||
issues: vec![err],
|
||||
exit_code: exit_code,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new() -> InvalidOptionsError {
|
||||
InvalidOptionsError {
|
||||
issues: Vec::new(),
|
||||
exit_code: 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn assure_config_dir_exists(dir: &str) -> Result<String, CLIError> {
|
||||
let trdir = dir.trim();
|
||||
if trdir.len() == 0 {
|
||||
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(CLIError::Configuration(ConfigurationError::HomeExpansionFailed(trdir.to_string()))),
|
||||
Some(mut user) => {
|
||||
user.push_str(&trdir[1..]);
|
||||
user
|
||||
}
|
||||
}
|
||||
} else {
|
||||
trdir.to_string()
|
||||
};
|
||||
|
||||
if let Err(err) = fs::create_dir(&expanded_config_dir) {
|
||||
if err.kind() != io::ErrorKind::AlreadyExists {
|
||||
return Err(CLIError::Configuration(
|
||||
ConfigurationError::DirectoryCreationFailed((expanded_config_dir, err))))
|
||||
}
|
||||
}
|
||||
|
||||
Ok(expanded_config_dir)
|
||||
}
|
||||
|
||||
pub fn application_secret_from_directory(dir: &str,
|
||||
secret_basename: &str,
|
||||
json_app_secret: &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(CLIError::Configuration(ConfigurationError::IOError(
|
||||
(secret_str(), io_err)
|
||||
)))
|
||||
};
|
||||
|
||||
for _ in 0..2 {
|
||||
match fs::File::open(&secret_path) {
|
||||
Err(mut err) => {
|
||||
if err.kind() == io::ErrorKind::NotFound {
|
||||
// Write our built-in one - user may adjust the written file at will
|
||||
|
||||
err = match fs::OpenOptions::new().create(true).write(true).open(&secret_path) {
|
||||
Err(cfe) => cfe,
|
||||
Ok(mut f) => {
|
||||
match f.write(json_app_secret.as_bytes()) {
|
||||
Err(io_err) => io_err,
|
||||
Ok(_) => continue,
|
||||
}
|
||||
}
|
||||
};
|
||||
// fall through to IO error handling
|
||||
}
|
||||
return secret_io_error(err)
|
||||
},
|
||||
Ok(mut f) => {
|
||||
let mut json_encoded_secret = String::new();
|
||||
if let Err(io_err) = f.read_to_string(&mut json_encoded_secret) {
|
||||
return secret_io_error(io_err)
|
||||
}
|
||||
match json::decode::<ConsoleApplicationSecret>(&json_encoded_secret) {
|
||||
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(
|
||||
CLIError::Configuration(
|
||||
ConfigurationError::Secret(
|
||||
ApplicationSecretError::FormatError(secret_str())
|
||||
)))
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
unreachable!();
|
||||
}
|
||||
285
gen/customsearch1-cli/src/main.rs
Normal file
285
gen/customsearch1-cli/src/main.rs
Normal file
@@ -0,0 +1,285 @@
|
||||
// DO NOT EDIT !
|
||||
// This file was generated automatically from 'src/mako/cli/main.rs.mako'
|
||||
// DO NOT EDIT !
|
||||
#![feature(plugin, exit_status)]
|
||||
#![plugin(docopt_macros)]
|
||||
#![allow(unused_variables, unused_imports, dead_code, unused_mut)]
|
||||
|
||||
extern crate docopt;
|
||||
extern crate yup_oauth2 as oauth2;
|
||||
extern crate yup_hyper_mock as mock;
|
||||
extern crate rustc_serialize;
|
||||
extern crate serde;
|
||||
extern crate hyper;
|
||||
extern crate mime;
|
||||
extern crate google_customsearch1 as api;
|
||||
|
||||
use std::env;
|
||||
use std::io::{self, Write};
|
||||
|
||||
docopt!(Options derive Debug, "
|
||||
Usage:
|
||||
customsearch1 [options] cse list <q> [-p <v>]... [-o <out>]
|
||||
customsearch1 --help
|
||||
|
||||
All documentation details can be found TODO: <URL to github.io docs here, see #51>
|
||||
|
||||
Configuration:
|
||||
--config-dir <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: ~/.google-service-cli]
|
||||
--debug
|
||||
Output all server communication to standard error. `tx` and `rx` are placed into
|
||||
the same stream.
|
||||
--debug-auth
|
||||
Output all communication related to authentication to standard error. `tx` and `rx` are placed into
|
||||
the same stream.
|
||||
");
|
||||
|
||||
mod cmn;
|
||||
use cmn::{InvalidOptionsError, CLIError, JsonTokenStorage, arg_from_str, writer_from_opts, parse_kv_arg,
|
||||
input_file_from_opts, input_mime_from_opts, FieldCursor, FieldError};
|
||||
|
||||
use std::default::Default;
|
||||
use std::str::FromStr;
|
||||
|
||||
use oauth2::{Authenticator, DefaultAuthenticatorDelegate};
|
||||
use rustc_serialize::json;
|
||||
|
||||
struct Engine {
|
||||
opt: Options,
|
||||
hub: api::Customsearch<hyper::Client, Authenticator<DefaultAuthenticatorDelegate, JsonTokenStorage, hyper::Client>>,
|
||||
}
|
||||
|
||||
|
||||
impl Engine {
|
||||
fn _cse_list(&self, dry_run: bool, err: &mut InvalidOptionsError)
|
||||
-> Option<api::Error> {
|
||||
let mut call = self.hub.cse().list(&self.opt.arg_q);
|
||||
for parg in self.opt.arg_v.iter() {
|
||||
let (key, value) = parse_kv_arg(&*parg, err, false);
|
||||
match key {
|
||||
"start" => {
|
||||
call = call.start(arg_from_str(value.unwrap_or("-0"), err, "start", "integer"));
|
||||
},
|
||||
"sort" => {
|
||||
call = call.sort(value.unwrap_or(""));
|
||||
},
|
||||
"site-search-filter" => {
|
||||
call = call.site_search_filter(value.unwrap_or(""));
|
||||
},
|
||||
"site-search" => {
|
||||
call = call.site_search(value.unwrap_or(""));
|
||||
},
|
||||
"search-type" => {
|
||||
call = call.search_type(value.unwrap_or(""));
|
||||
},
|
||||
"safe" => {
|
||||
call = call.safe(value.unwrap_or(""));
|
||||
},
|
||||
"rights" => {
|
||||
call = call.rights(value.unwrap_or(""));
|
||||
},
|
||||
"related-site" => {
|
||||
call = call.related_site(value.unwrap_or(""));
|
||||
},
|
||||
"or-terms" => {
|
||||
call = call.or_terms(value.unwrap_or(""));
|
||||
},
|
||||
"num" => {
|
||||
call = call.num(arg_from_str(value.unwrap_or("-0"), err, "num", "integer"));
|
||||
},
|
||||
"lr" => {
|
||||
call = call.lr(value.unwrap_or(""));
|
||||
},
|
||||
"low-range" => {
|
||||
call = call.low_range(value.unwrap_or(""));
|
||||
},
|
||||
"link-site" => {
|
||||
call = call.link_site(value.unwrap_or(""));
|
||||
},
|
||||
"img-type" => {
|
||||
call = call.img_type(value.unwrap_or(""));
|
||||
},
|
||||
"img-size" => {
|
||||
call = call.img_size(value.unwrap_or(""));
|
||||
},
|
||||
"img-dominant-color" => {
|
||||
call = call.img_dominant_color(value.unwrap_or(""));
|
||||
},
|
||||
"img-color-type" => {
|
||||
call = call.img_color_type(value.unwrap_or(""));
|
||||
},
|
||||
"hq" => {
|
||||
call = call.hq(value.unwrap_or(""));
|
||||
},
|
||||
"hl" => {
|
||||
call = call.hl(value.unwrap_or(""));
|
||||
},
|
||||
"high-range" => {
|
||||
call = call.high_range(value.unwrap_or(""));
|
||||
},
|
||||
"googlehost" => {
|
||||
call = call.googlehost(value.unwrap_or(""));
|
||||
},
|
||||
"gl" => {
|
||||
call = call.gl(value.unwrap_or(""));
|
||||
},
|
||||
"filter" => {
|
||||
call = call.filter(value.unwrap_or(""));
|
||||
},
|
||||
"file-type" => {
|
||||
call = call.file_type(value.unwrap_or(""));
|
||||
},
|
||||
"exclude-terms" => {
|
||||
call = call.exclude_terms(value.unwrap_or(""));
|
||||
},
|
||||
"exact-terms" => {
|
||||
call = call.exact_terms(value.unwrap_or(""));
|
||||
},
|
||||
"date-restrict" => {
|
||||
call = call.date_restrict(value.unwrap_or(""));
|
||||
},
|
||||
"cx" => {
|
||||
call = call.cx(value.unwrap_or(""));
|
||||
},
|
||||
"cref" => {
|
||||
call = call.cref(value.unwrap_or(""));
|
||||
},
|
||||
"cr" => {
|
||||
call = call.cr(value.unwrap_or(""));
|
||||
},
|
||||
"c2coff" => {
|
||||
call = call.c2coff(value.unwrap_or(""));
|
||||
},
|
||||
"alt"
|
||||
|"fields"
|
||||
|"key"
|
||||
|"oauth-token"
|
||||
|"pretty-print"
|
||||
|"quota-user"
|
||||
|"user-ip" => {
|
||||
let map = [
|
||||
("oauth-token", "oauth_token"),
|
||||
("pretty-print", "prettyPrint"),
|
||||
("quota-user", "quotaUser"),
|
||||
("user-ip", "userIp"),
|
||||
];
|
||||
call = call.param(map.iter().find(|t| t.0 == key).unwrap_or(&("", key)).1, value.unwrap_or("unset"))
|
||||
},
|
||||
_ => err.issues.push(CLIError::UnknownParameter(key.to_string())),
|
||||
}
|
||||
}
|
||||
let protocol = "standard-request";
|
||||
if dry_run {
|
||||
None
|
||||
} else {
|
||||
assert!(err.issues.len() == 0);
|
||||
let mut ostream = writer_from_opts(self.opt.flag_o, &self.opt.arg_out);
|
||||
match match protocol {
|
||||
"standard-request" => call.doit(),
|
||||
_ => unreachable!(),
|
||||
} {
|
||||
Err(api_err) => Some(api_err),
|
||||
Ok((mut response, output_schema)) => {
|
||||
serde::json::to_writer_pretty(&mut ostream, &output_schema).unwrap();
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn _doit(&self, dry_run: bool) -> (Option<api::Error>, Option<InvalidOptionsError>) {
|
||||
let mut err = InvalidOptionsError::new();
|
||||
let mut call_result: Option<api::Error>;
|
||||
let mut err_opt: Option<InvalidOptionsError> = None;
|
||||
|
||||
if self.opt.cmd_cse {
|
||||
if self.opt.cmd_list {
|
||||
call_result = self._cse_list(dry_run, &mut err);
|
||||
} else {
|
||||
unreachable!();
|
||||
}
|
||||
} else {
|
||||
unreachable!();
|
||||
}
|
||||
|
||||
if dry_run {
|
||||
if err.issues.len() > 0 {
|
||||
err_opt = Some(err);
|
||||
}
|
||||
}
|
||||
(call_result, err_opt)
|
||||
}
|
||||
|
||||
// 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(&opt.flag_config_dir) {
|
||||
Err(e) => return Err(InvalidOptionsError::single(e, 3)),
|
||||
Ok(p) => p,
|
||||
};
|
||||
|
||||
match cmn::application_secret_from_directory(&config_dir, "customsearch1-secret.json",
|
||||
"{\"installed\":{\"auth_uri\":\"https://accounts.google.com/o/oauth2/auth\",\"client_secret\":\"hCsslbCUyfehWMmbkG8vTYxG\",\"token_uri\":\"https://accounts.google.com/o/oauth2/token\",\"client_email\":\"\",\"redirect_uris\":[\"urn:ietf:wg:oauth:2.0:oob\",\"oob\"],\"client_x509_cert_url\":\"\",\"client_id\":\"620010449518-9ngf7o4dhs0dka470npqvor6dc5lqb9b.apps.googleusercontent.com\",\"auth_provider_x509_cert_url\":\"https://www.googleapis.com/oauth2/v1/certs\"}}") {
|
||||
Ok(secret) => (config_dir, secret),
|
||||
Err(e) => return Err(InvalidOptionsError::single(e, 4))
|
||||
}
|
||||
};
|
||||
|
||||
let auth = Authenticator::new( &secret, DefaultAuthenticatorDelegate,
|
||||
if opt.flag_debug_auth {
|
||||
hyper::Client::with_connector(mock::TeeConnector {
|
||||
connector: hyper::net::HttpConnector(None)
|
||||
})
|
||||
} else {
|
||||
hyper::Client::new()
|
||||
},
|
||||
JsonTokenStorage {
|
||||
program_name: "customsearch1",
|
||||
db_dir: config_dir.clone(),
|
||||
}, None);
|
||||
|
||||
let client =
|
||||
if opt.flag_debug {
|
||||
hyper::Client::with_connector(mock::TeeConnector {
|
||||
connector: hyper::net::HttpConnector(None)
|
||||
})
|
||||
} else {
|
||||
hyper::Client::new()
|
||||
};
|
||||
let engine = Engine {
|
||||
opt: opt,
|
||||
hub: api::Customsearch::new(client, auth),
|
||||
};
|
||||
|
||||
match engine._doit(true) {
|
||||
(_, Some(err)) => Err(err),
|
||||
_ => 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> {
|
||||
self._doit(false).0
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let opts: Options = Options::docopt().decode().unwrap_or_else(|e| e.exit());
|
||||
match Engine::new(opts) {
|
||||
Err(err) => {
|
||||
writeln!(io::stderr(), "{}", err).ok();
|
||||
env::set_exit_status(err.exit_code);
|
||||
},
|
||||
Ok(engine) => {
|
||||
if let Some(err) = engine.doit() {
|
||||
writeln!(io::stderr(), "{:?}", err).ok();
|
||||
writeln!(io::stderr(), "{}", err).ok();
|
||||
env::set_exit_status(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
31
gen/drive2-cli/Cargo.toml
Normal file
31
gen/drive2-cli/Cargo.toml
Normal file
@@ -0,0 +1,31 @@
|
||||
# DO NOT EDIT !
|
||||
# This file was generated automatically from 'src/mako/Cargo.toml.mako'
|
||||
# DO NOT EDIT !
|
||||
[package]
|
||||
|
||||
name = "google-drive2-cli"
|
||||
version = "0.0.1+20150326"
|
||||
authors = ["Sebastian Thiel <byronimo@gmail.com>"]
|
||||
description = "A complete library to interact with drive (protocol v2)"
|
||||
repository = "https://github.com/Byron/google-apis-rs/tree/master/gen/drive2-cli"
|
||||
homepage = "https://developers.google.com/drive/"
|
||||
documentation = "http://byron.github.io/google-apis-rs/google_drive2_cli"
|
||||
license = "MIT"
|
||||
keywords = ["drive", "google", "cli"]
|
||||
|
||||
[[bin]]
|
||||
name = "drive2"
|
||||
|
||||
[dependencies]
|
||||
hyper = "*"
|
||||
mime = "*"
|
||||
yup-oauth2 = "*"
|
||||
docopt = "*"
|
||||
docopt_macros = "*"
|
||||
rustc-serialize = "*"
|
||||
yup-hyper-mock = "*"
|
||||
serde = ">= 0.3.0"
|
||||
serde_macros = "*"
|
||||
|
||||
[dependencies.google-drive2]
|
||||
path = "../drive2"
|
||||
30
gen/drive2-cli/LICENSE.md
Normal file
30
gen/drive2-cli/LICENSE.md
Normal file
@@ -0,0 +1,30 @@
|
||||
<!---
|
||||
DO NOT EDIT !
|
||||
This file was generated automatically from 'src/mako/LICENSE.md.mako'
|
||||
DO NOT EDIT !
|
||||
-->
|
||||
The MIT License (MIT)
|
||||
=====================
|
||||
|
||||
Copyright © `2015` `Sebastian Thiel`
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the “Software”), to deal in the Software without
|
||||
restriction, including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
4
gen/drive2-cli/README.md
Normal file
4
gen/drive2-cli/README.md
Normal file
@@ -0,0 +1,4 @@
|
||||
# HELLO DRIVE:V2
|
||||
|
||||
|
||||
Include information about application secret files, and how we automatically write a default one.
|
||||
75
gen/drive2-cli/mkdocs.yml
Normal file
75
gen/drive2-cli/mkdocs.yml
Normal file
@@ -0,0 +1,75 @@
|
||||
site_name: drive v0.0.1+20150326
|
||||
site_url: http://byron.github.io/google-apis-rs/google-drive2-cli
|
||||
site_description: Write integrating applications with bcore
|
||||
|
||||
repo_url: https://github.com/Byron/google-apis-rs/tree/master/gen/drive2-cli
|
||||
|
||||
docs_dir: docs
|
||||
site_dir: build_html
|
||||
|
||||
pages:
|
||||
- ['index.md', 'Home']
|
||||
- ['about_get.md', 'About', 'Get']
|
||||
- ['apps_get.md', 'Apps', 'Get']
|
||||
- ['apps_list.md', 'Apps', 'List']
|
||||
- ['changes_get.md', 'Changes', 'Get']
|
||||
- ['changes_list.md', 'Changes', 'List']
|
||||
- ['changes_watch.md', 'Changes', 'Watch']
|
||||
- ['channels_stop.md', 'Channels', 'Stop']
|
||||
- ['children_delete.md', 'Children', 'Delete']
|
||||
- ['children_get.md', 'Children', 'Get']
|
||||
- ['children_insert.md', 'Children', 'Insert']
|
||||
- ['children_list.md', 'Children', 'List']
|
||||
- ['comments_delete.md', 'Comments', 'Delete']
|
||||
- ['comments_get.md', 'Comments', 'Get']
|
||||
- ['comments_insert.md', 'Comments', 'Insert']
|
||||
- ['comments_list.md', 'Comments', 'List']
|
||||
- ['comments_patch.md', 'Comments', 'Patch']
|
||||
- ['comments_update.md', 'Comments', 'Update']
|
||||
- ['files_copy.md', 'Files', 'Copy']
|
||||
- ['files_delete.md', 'Files', 'Delete']
|
||||
- ['files_empty-trash.md', 'Files', 'Empty Trash']
|
||||
- ['files_get.md', 'Files', 'Get']
|
||||
- ['files_insert.md', 'Files', 'Insert']
|
||||
- ['files_list.md', 'Files', 'List']
|
||||
- ['files_patch.md', 'Files', 'Patch']
|
||||
- ['files_touch.md', 'Files', 'Touch']
|
||||
- ['files_trash.md', 'Files', 'Trash']
|
||||
- ['files_untrash.md', 'Files', 'Untrash']
|
||||
- ['files_update.md', 'Files', 'Update']
|
||||
- ['files_watch.md', 'Files', 'Watch']
|
||||
- ['parents_delete.md', 'Parents', 'Delete']
|
||||
- ['parents_get.md', 'Parents', 'Get']
|
||||
- ['parents_insert.md', 'Parents', 'Insert']
|
||||
- ['parents_list.md', 'Parents', 'List']
|
||||
- ['permissions_delete.md', 'Permissions', 'Delete']
|
||||
- ['permissions_get.md', 'Permissions', 'Get']
|
||||
- ['permissions_get-id-for-email.md', 'Permissions', 'Get Id For Email']
|
||||
- ['permissions_insert.md', 'Permissions', 'Insert']
|
||||
- ['permissions_list.md', 'Permissions', 'List']
|
||||
- ['permissions_patch.md', 'Permissions', 'Patch']
|
||||
- ['permissions_update.md', 'Permissions', 'Update']
|
||||
- ['properties_delete.md', 'Properties', 'Delete']
|
||||
- ['properties_get.md', 'Properties', 'Get']
|
||||
- ['properties_insert.md', 'Properties', 'Insert']
|
||||
- ['properties_list.md', 'Properties', 'List']
|
||||
- ['properties_patch.md', 'Properties', 'Patch']
|
||||
- ['properties_update.md', 'Properties', 'Update']
|
||||
- ['realtime_get.md', 'Realtime', 'Get']
|
||||
- ['realtime_update.md', 'Realtime', 'Update']
|
||||
- ['replies_delete.md', 'Replies', 'Delete']
|
||||
- ['replies_get.md', 'Replies', 'Get']
|
||||
- ['replies_insert.md', 'Replies', 'Insert']
|
||||
- ['replies_list.md', 'Replies', 'List']
|
||||
- ['replies_patch.md', 'Replies', 'Patch']
|
||||
- ['replies_update.md', 'Replies', 'Update']
|
||||
- ['revisions_delete.md', 'Revisions', 'Delete']
|
||||
- ['revisions_get.md', 'Revisions', 'Get']
|
||||
- ['revisions_list.md', 'Revisions', 'List']
|
||||
- ['revisions_patch.md', 'Revisions', 'Patch']
|
||||
- ['revisions_update.md', 'Revisions', 'Update']
|
||||
|
||||
theme: readthedocs
|
||||
|
||||
copyright: Copyright © 2015, `Sebastian Thiel`
|
||||
|
||||
454
gen/drive2-cli/src/cmn.rs
Normal file
454
gen/drive2-cli/src/cmn.rs
Normal file
@@ -0,0 +1,454 @@
|
||||
// COPY OF 'src/rust/cli/cmn.rs'
|
||||
// DO NOT EDIT
|
||||
use oauth2::{ApplicationSecret, ConsoleApplicationSecret, TokenStorage, Token};
|
||||
use rustc_serialize::json;
|
||||
use mime::Mime;
|
||||
|
||||
use std::fs;
|
||||
use std::env;
|
||||
use std::io;
|
||||
use std::fmt;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::str::FromStr;
|
||||
use std::string::ToString;
|
||||
use std::io::{Write, Read, stdout};
|
||||
|
||||
use std::default::Default;
|
||||
|
||||
const FIELD_SEP: char = '.';
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct FieldCursor(Vec<String>);
|
||||
|
||||
impl ToString for FieldCursor {
|
||||
fn to_string(&self) -> String {
|
||||
self.0.connect(".")
|
||||
}
|
||||
}
|
||||
|
||||
impl FieldCursor {
|
||||
pub fn set(&mut self, value: &str) -> Result<(), CLIError> {
|
||||
if value.len() == 0 {
|
||||
return Err(CLIError::Field(FieldError::Empty))
|
||||
}
|
||||
|
||||
let mut first_is_field_sep = false;
|
||||
let mut char_count: usize = 0;
|
||||
let mut last_c = FIELD_SEP;
|
||||
let mut num_conscutive_field_seps = 0;
|
||||
|
||||
let mut field = String::new();
|
||||
let mut fields = self.0.clone();
|
||||
|
||||
let push_field = |fs: &mut Vec<String>, f: &mut String| {
|
||||
if f.len() > 0 {
|
||||
fs.push(f.clone());
|
||||
f.truncate(0);
|
||||
}
|
||||
};
|
||||
|
||||
for (cid, c) in value.chars().enumerate() {
|
||||
char_count += 1;
|
||||
|
||||
if c == FIELD_SEP {
|
||||
if cid == 0 {
|
||||
first_is_field_sep = true;
|
||||
}
|
||||
num_conscutive_field_seps += 1;
|
||||
if cid > 0 && last_c == FIELD_SEP {
|
||||
if fields.pop().is_none() {
|
||||
return Err(CLIError::Field(FieldError::PopOnEmpty(value.to_string())))
|
||||
}
|
||||
} else {
|
||||
push_field(&mut fields, &mut field);
|
||||
}
|
||||
} else {
|
||||
num_conscutive_field_seps = 0;
|
||||
if cid == 1 {
|
||||
if first_is_field_sep {
|
||||
fields.truncate(0);
|
||||
}
|
||||
}
|
||||
field.push(c);
|
||||
}
|
||||
|
||||
last_c = c;
|
||||
}
|
||||
|
||||
push_field(&mut fields, &mut field);
|
||||
|
||||
if char_count == 1 && first_is_field_sep {
|
||||
fields.truncate(0);
|
||||
}
|
||||
if char_count > 1 && num_conscutive_field_seps == 1 {
|
||||
return Err(CLIError::Field(FieldError::TrailingFieldSep(value.to_string())))
|
||||
}
|
||||
|
||||
self.0 = fields;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn num_fields(&self) -> usize {
|
||||
self.0.len()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_kv_arg<'a>(kv: &'a str, err: &mut InvalidOptionsError, for_hashmap: bool)
|
||||
-> (&'a str, Option<&'a str>) {
|
||||
let mut add_err = || err.issues.push(CLIError::InvalidKeyValueSyntax(kv.to_string(),for_hashmap));
|
||||
match kv.find('=') {
|
||||
None => {
|
||||
add_err();
|
||||
return (kv, None)
|
||||
},
|
||||
Some(pos) => {
|
||||
let key = &kv[..pos];
|
||||
if kv.len() <= pos + 1 {
|
||||
add_err();
|
||||
return (key, None)
|
||||
}
|
||||
(key, Some(&kv[pos+1..]))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn input_file_from_opts(file_path: &str, err: &mut InvalidOptionsError) -> Option<fs::File> {
|
||||
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<Mime> {
|
||||
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<Write> {
|
||||
if !flag || arg == "-" {
|
||||
Box::new(stdout())
|
||||
} else {
|
||||
Box::new(fs::OpenOptions::new().create(true).write(true).open(arg).unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn arg_from_str<T>(arg: &str, err: &mut InvalidOptionsError,
|
||||
arg_name: &'static str,
|
||||
arg_type: &'static str) -> T
|
||||
where T: FromStr + Default,
|
||||
<T as FromStr>::Err: fmt::Display {
|
||||
match FromStr::from_str(arg) {
|
||||
Err(perr) => {
|
||||
err.issues.push(
|
||||
CLIError::ParseError((arg_name, arg_type, arg.to_string(), format!("{}", perr)))
|
||||
);
|
||||
Default::default()
|
||||
},
|
||||
Ok(v) => v,
|
||||
}
|
||||
}
|
||||
|
||||
pub struct JsonTokenStorage {
|
||||
pub program_name: &'static str,
|
||||
pub db_dir: String,
|
||||
}
|
||||
|
||||
impl JsonTokenStorage {
|
||||
fn path(&self, scope_hash: u64) -> PathBuf {
|
||||
Path::new(&self.db_dir).join(&format!("{}-token-{}.json", self.program_name, scope_hash))
|
||||
}
|
||||
}
|
||||
|
||||
impl TokenStorage for JsonTokenStorage {
|
||||
type Error = io::Error;
|
||||
|
||||
// NOTE: logging might be interesting, currently we swallow all errors
|
||||
fn set(&mut self, scope_hash: u64, _: &Vec<&str>, token: Option<Token>) -> Option<io::Error> {
|
||||
match token {
|
||||
None => {
|
||||
match fs::remove_file(self.path(scope_hash)) {
|
||||
Err(err) =>
|
||||
match err.kind() {
|
||||
io::ErrorKind::NotFound => None,
|
||||
_ => Some(err)
|
||||
},
|
||||
Ok(_) => None
|
||||
}
|
||||
}
|
||||
Some(token) => {
|
||||
let json_token = json::encode(&token).unwrap();
|
||||
match fs::OpenOptions::new().create(true).write(true).open(&self.path(scope_hash)) {
|
||||
Ok(mut f) => {
|
||||
match f.write(json_token.as_bytes()) {
|
||||
Ok(_) => None,
|
||||
Err(io_err) => Some(io_err),
|
||||
}
|
||||
},
|
||||
Err(io_err) => Some(io_err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get(&self, scope_hash: u64, _: &Vec<&str>) -> Result<Option<Token>, io::Error> {
|
||||
match fs::File::open(&self.path(scope_hash)) {
|
||||
Ok(mut f) => {
|
||||
let mut json_string = String::new();
|
||||
match f.read_to_string(&mut json_string) {
|
||||
Ok(_) => Ok(Some(json::decode::<Token>(&json_string).unwrap())),
|
||||
Err(io_err) => Err(io_err),
|
||||
}
|
||||
},
|
||||
Err(io_err) => {
|
||||
match io_err.kind() {
|
||||
io::ErrorKind::NotFound => Ok(None),
|
||||
_ => Err(io_err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug)]
|
||||
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)),
|
||||
}
|
||||
|
||||
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 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 FieldError {
|
||||
PopOnEmpty(String),
|
||||
TrailingFieldSep(String),
|
||||
Unknown(String),
|
||||
Empty,
|
||||
}
|
||||
|
||||
|
||||
impl fmt::Display for FieldError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
match *self {
|
||||
FieldError::PopOnEmpty(ref field)
|
||||
=> writeln!(f, "'{}': Cannot move up on empty field cursor", field),
|
||||
FieldError::TrailingFieldSep(ref field)
|
||||
=> writeln!(f, "'{}': Single field separator may not be last character", field),
|
||||
FieldError::Unknown(ref field)
|
||||
=> writeln!(f, "Field '{}' does not exist", field),
|
||||
FieldError::Empty
|
||||
=> writeln!(f, "Field names must not be empty"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum CLIError {
|
||||
Configuration(ConfigurationError),
|
||||
ParseError((&'static str, &'static str, String, String)),
|
||||
UnknownParameter(String),
|
||||
InvalidKeyValueSyntax(String, bool),
|
||||
Input(InputError),
|
||||
Field(FieldError),
|
||||
}
|
||||
|
||||
impl fmt::Display for CLIError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
match *self {
|
||||
CLIError::Configuration(ref err) => write!(f, "Configuration -> {}", err),
|
||||
CLIError::Input(ref err) => write!(f, "Input -> {}", err),
|
||||
CLIError::Field(ref err) => write!(f, "Field -> {}", 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),
|
||||
CLIError::UnknownParameter(ref param_name)
|
||||
=> writeln!(f, "Parameter '{}' is unknown.", param_name),
|
||||
CLIError::InvalidKeyValueSyntax(ref kv, is_hashmap) => {
|
||||
let hashmap_info = if is_hashmap { "hashmap " } else { "" };
|
||||
writeln!(f, "'{}' does not match {}pattern <key>=<value>", kv, hashmap_info)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct InvalidOptionsError {
|
||||
pub issues: Vec<CLIError>,
|
||||
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: CLIError, exit_code: i32) -> InvalidOptionsError {
|
||||
InvalidOptionsError {
|
||||
issues: vec![err],
|
||||
exit_code: exit_code,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new() -> InvalidOptionsError {
|
||||
InvalidOptionsError {
|
||||
issues: Vec::new(),
|
||||
exit_code: 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn assure_config_dir_exists(dir: &str) -> Result<String, CLIError> {
|
||||
let trdir = dir.trim();
|
||||
if trdir.len() == 0 {
|
||||
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(CLIError::Configuration(ConfigurationError::HomeExpansionFailed(trdir.to_string()))),
|
||||
Some(mut user) => {
|
||||
user.push_str(&trdir[1..]);
|
||||
user
|
||||
}
|
||||
}
|
||||
} else {
|
||||
trdir.to_string()
|
||||
};
|
||||
|
||||
if let Err(err) = fs::create_dir(&expanded_config_dir) {
|
||||
if err.kind() != io::ErrorKind::AlreadyExists {
|
||||
return Err(CLIError::Configuration(
|
||||
ConfigurationError::DirectoryCreationFailed((expanded_config_dir, err))))
|
||||
}
|
||||
}
|
||||
|
||||
Ok(expanded_config_dir)
|
||||
}
|
||||
|
||||
pub fn application_secret_from_directory(dir: &str,
|
||||
secret_basename: &str,
|
||||
json_app_secret: &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(CLIError::Configuration(ConfigurationError::IOError(
|
||||
(secret_str(), io_err)
|
||||
)))
|
||||
};
|
||||
|
||||
for _ in 0..2 {
|
||||
match fs::File::open(&secret_path) {
|
||||
Err(mut err) => {
|
||||
if err.kind() == io::ErrorKind::NotFound {
|
||||
// Write our built-in one - user may adjust the written file at will
|
||||
|
||||
err = match fs::OpenOptions::new().create(true).write(true).open(&secret_path) {
|
||||
Err(cfe) => cfe,
|
||||
Ok(mut f) => {
|
||||
match f.write(json_app_secret.as_bytes()) {
|
||||
Err(io_err) => io_err,
|
||||
Ok(_) => continue,
|
||||
}
|
||||
}
|
||||
};
|
||||
// fall through to IO error handling
|
||||
}
|
||||
return secret_io_error(err)
|
||||
},
|
||||
Ok(mut f) => {
|
||||
let mut json_encoded_secret = String::new();
|
||||
if let Err(io_err) = f.read_to_string(&mut json_encoded_secret) {
|
||||
return secret_io_error(io_err)
|
||||
}
|
||||
match json::decode::<ConsoleApplicationSecret>(&json_encoded_secret) {
|
||||
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(
|
||||
CLIError::Configuration(
|
||||
ConfigurationError::Secret(
|
||||
ApplicationSecretError::FormatError(secret_str())
|
||||
)))
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
unreachable!();
|
||||
}
|
||||
6468
gen/drive2-cli/src/main.rs
Normal file
6468
gen/drive2-cli/src/main.rs
Normal file
File diff suppressed because it is too large
Load Diff
31
gen/games1-cli/Cargo.toml
Normal file
31
gen/games1-cli/Cargo.toml
Normal file
@@ -0,0 +1,31 @@
|
||||
# DO NOT EDIT !
|
||||
# This file was generated automatically from 'src/mako/Cargo.toml.mako'
|
||||
# DO NOT EDIT !
|
||||
[package]
|
||||
|
||||
name = "google-games1-cli"
|
||||
version = "0.0.1+20150413"
|
||||
authors = ["Sebastian Thiel <byronimo@gmail.com>"]
|
||||
description = "A complete library to interact with Games (protocol v1)"
|
||||
repository = "https://github.com/Byron/google-apis-rs/tree/master/gen/games1-cli"
|
||||
homepage = "https://developers.google.com/games/services/"
|
||||
documentation = "http://byron.github.io/google-apis-rs/google_games1_cli"
|
||||
license = "MIT"
|
||||
keywords = ["games", "google", "cli"]
|
||||
|
||||
[[bin]]
|
||||
name = "games1"
|
||||
|
||||
[dependencies]
|
||||
hyper = "*"
|
||||
mime = "*"
|
||||
yup-oauth2 = "*"
|
||||
docopt = "*"
|
||||
docopt_macros = "*"
|
||||
rustc-serialize = "*"
|
||||
yup-hyper-mock = "*"
|
||||
serde = ">= 0.3.0"
|
||||
serde_macros = "*"
|
||||
|
||||
[dependencies.google-games1]
|
||||
path = "../games1"
|
||||
30
gen/games1-cli/LICENSE.md
Normal file
30
gen/games1-cli/LICENSE.md
Normal file
@@ -0,0 +1,30 @@
|
||||
<!---
|
||||
DO NOT EDIT !
|
||||
This file was generated automatically from 'src/mako/LICENSE.md.mako'
|
||||
DO NOT EDIT !
|
||||
-->
|
||||
The MIT License (MIT)
|
||||
=====================
|
||||
|
||||
Copyright © `2015` `Sebastian Thiel`
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the “Software”), to deal in the Software without
|
||||
restriction, including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
4
gen/games1-cli/README.md
Normal file
4
gen/games1-cli/README.md
Normal file
@@ -0,0 +1,4 @@
|
||||
# HELLO GAMES:V1
|
||||
|
||||
|
||||
Include information about application secret files, and how we automatically write a default one.
|
||||
68
gen/games1-cli/mkdocs.yml
Normal file
68
gen/games1-cli/mkdocs.yml
Normal file
@@ -0,0 +1,68 @@
|
||||
site_name: Games v0.0.1+20150413
|
||||
site_url: http://byron.github.io/google-apis-rs/google-games1-cli
|
||||
site_description: Write integrating applications with bcore
|
||||
|
||||
repo_url: https://github.com/Byron/google-apis-rs/tree/master/gen/games1-cli
|
||||
|
||||
docs_dir: docs
|
||||
site_dir: build_html
|
||||
|
||||
pages:
|
||||
- ['index.md', 'Home']
|
||||
- ['achievement-definitions_list.md', 'Achievement Definitions', 'List']
|
||||
- ['achievements_increment.md', 'Achievements', 'Increment']
|
||||
- ['achievements_list.md', 'Achievements', 'List']
|
||||
- ['achievements_reveal.md', 'Achievements', 'Reveal']
|
||||
- ['achievements_set-steps-at-least.md', 'Achievements', 'Set Steps At Least']
|
||||
- ['achievements_unlock.md', 'Achievements', 'Unlock']
|
||||
- ['achievements_update-multiple.md', 'Achievements', 'Update Multiple']
|
||||
- ['applications_get.md', 'Applications', 'Get']
|
||||
- ['applications_played.md', 'Applications', 'Played']
|
||||
- ['events_list-by-player.md', 'Events', 'List By Player']
|
||||
- ['events_list-definitions.md', 'Events', 'List Definitions']
|
||||
- ['events_record.md', 'Events', 'Record']
|
||||
- ['leaderboards_get.md', 'Leaderboards', 'Get']
|
||||
- ['leaderboards_list.md', 'Leaderboards', 'List']
|
||||
- ['metagame_get-metagame-config.md', 'Metagame', 'Get Metagame Config']
|
||||
- ['metagame_list-categories-by-player.md', 'Metagame', 'List Categories By Player']
|
||||
- ['players_get.md', 'Players', 'Get']
|
||||
- ['players_list.md', 'Players', 'List']
|
||||
- ['pushtokens_remove.md', 'Pushtokens', 'Remove']
|
||||
- ['pushtokens_update.md', 'Pushtokens', 'Update']
|
||||
- ['quest-milestones_claim.md', 'Quest Milestones', 'Claim']
|
||||
- ['quests_accept.md', 'Quests', 'Accept']
|
||||
- ['quests_list.md', 'Quests', 'List']
|
||||
- ['revisions_check.md', 'Revisions', 'Check']
|
||||
- ['rooms_create.md', 'Rooms', 'Create']
|
||||
- ['rooms_decline.md', 'Rooms', 'Decline']
|
||||
- ['rooms_dismiss.md', 'Rooms', 'Dismiss']
|
||||
- ['rooms_get.md', 'Rooms', 'Get']
|
||||
- ['rooms_join.md', 'Rooms', 'Join']
|
||||
- ['rooms_leave.md', 'Rooms', 'Leave']
|
||||
- ['rooms_list.md', 'Rooms', 'List']
|
||||
- ['rooms_report-status.md', 'Rooms', 'Report Status']
|
||||
- ['scores_get.md', 'Scores', 'Get']
|
||||
- ['scores_list.md', 'Scores', 'List']
|
||||
- ['scores_list-window.md', 'Scores', 'List Window']
|
||||
- ['scores_submit.md', 'Scores', 'Submit']
|
||||
- ['scores_submit-multiple.md', 'Scores', 'Submit Multiple']
|
||||
- ['snapshots_get.md', 'Snapshots', 'Get']
|
||||
- ['snapshots_list.md', 'Snapshots', 'List']
|
||||
- ['turn-based-matches_cancel.md', 'Turn Based Matches', 'Cancel']
|
||||
- ['turn-based-matches_create.md', 'Turn Based Matches', 'Create']
|
||||
- ['turn-based-matches_decline.md', 'Turn Based Matches', 'Decline']
|
||||
- ['turn-based-matches_dismiss.md', 'Turn Based Matches', 'Dismiss']
|
||||
- ['turn-based-matches_finish.md', 'Turn Based Matches', 'Finish']
|
||||
- ['turn-based-matches_get.md', 'Turn Based Matches', 'Get']
|
||||
- ['turn-based-matches_join.md', 'Turn Based Matches', 'Join']
|
||||
- ['turn-based-matches_leave.md', 'Turn Based Matches', 'Leave']
|
||||
- ['turn-based-matches_leave-turn.md', 'Turn Based Matches', 'Leave Turn']
|
||||
- ['turn-based-matches_list.md', 'Turn Based Matches', 'List']
|
||||
- ['turn-based-matches_rematch.md', 'Turn Based Matches', 'Rematch']
|
||||
- ['turn-based-matches_sync.md', 'Turn Based Matches', 'Sync']
|
||||
- ['turn-based-matches_take-turn.md', 'Turn Based Matches', 'Take Turn']
|
||||
|
||||
theme: readthedocs
|
||||
|
||||
copyright: Copyright © 2015, `Sebastian Thiel`
|
||||
|
||||
454
gen/games1-cli/src/cmn.rs
Normal file
454
gen/games1-cli/src/cmn.rs
Normal file
@@ -0,0 +1,454 @@
|
||||
// COPY OF 'src/rust/cli/cmn.rs'
|
||||
// DO NOT EDIT
|
||||
use oauth2::{ApplicationSecret, ConsoleApplicationSecret, TokenStorage, Token};
|
||||
use rustc_serialize::json;
|
||||
use mime::Mime;
|
||||
|
||||
use std::fs;
|
||||
use std::env;
|
||||
use std::io;
|
||||
use std::fmt;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::str::FromStr;
|
||||
use std::string::ToString;
|
||||
use std::io::{Write, Read, stdout};
|
||||
|
||||
use std::default::Default;
|
||||
|
||||
const FIELD_SEP: char = '.';
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct FieldCursor(Vec<String>);
|
||||
|
||||
impl ToString for FieldCursor {
|
||||
fn to_string(&self) -> String {
|
||||
self.0.connect(".")
|
||||
}
|
||||
}
|
||||
|
||||
impl FieldCursor {
|
||||
pub fn set(&mut self, value: &str) -> Result<(), CLIError> {
|
||||
if value.len() == 0 {
|
||||
return Err(CLIError::Field(FieldError::Empty))
|
||||
}
|
||||
|
||||
let mut first_is_field_sep = false;
|
||||
let mut char_count: usize = 0;
|
||||
let mut last_c = FIELD_SEP;
|
||||
let mut num_conscutive_field_seps = 0;
|
||||
|
||||
let mut field = String::new();
|
||||
let mut fields = self.0.clone();
|
||||
|
||||
let push_field = |fs: &mut Vec<String>, f: &mut String| {
|
||||
if f.len() > 0 {
|
||||
fs.push(f.clone());
|
||||
f.truncate(0);
|
||||
}
|
||||
};
|
||||
|
||||
for (cid, c) in value.chars().enumerate() {
|
||||
char_count += 1;
|
||||
|
||||
if c == FIELD_SEP {
|
||||
if cid == 0 {
|
||||
first_is_field_sep = true;
|
||||
}
|
||||
num_conscutive_field_seps += 1;
|
||||
if cid > 0 && last_c == FIELD_SEP {
|
||||
if fields.pop().is_none() {
|
||||
return Err(CLIError::Field(FieldError::PopOnEmpty(value.to_string())))
|
||||
}
|
||||
} else {
|
||||
push_field(&mut fields, &mut field);
|
||||
}
|
||||
} else {
|
||||
num_conscutive_field_seps = 0;
|
||||
if cid == 1 {
|
||||
if first_is_field_sep {
|
||||
fields.truncate(0);
|
||||
}
|
||||
}
|
||||
field.push(c);
|
||||
}
|
||||
|
||||
last_c = c;
|
||||
}
|
||||
|
||||
push_field(&mut fields, &mut field);
|
||||
|
||||
if char_count == 1 && first_is_field_sep {
|
||||
fields.truncate(0);
|
||||
}
|
||||
if char_count > 1 && num_conscutive_field_seps == 1 {
|
||||
return Err(CLIError::Field(FieldError::TrailingFieldSep(value.to_string())))
|
||||
}
|
||||
|
||||
self.0 = fields;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn num_fields(&self) -> usize {
|
||||
self.0.len()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_kv_arg<'a>(kv: &'a str, err: &mut InvalidOptionsError, for_hashmap: bool)
|
||||
-> (&'a str, Option<&'a str>) {
|
||||
let mut add_err = || err.issues.push(CLIError::InvalidKeyValueSyntax(kv.to_string(),for_hashmap));
|
||||
match kv.find('=') {
|
||||
None => {
|
||||
add_err();
|
||||
return (kv, None)
|
||||
},
|
||||
Some(pos) => {
|
||||
let key = &kv[..pos];
|
||||
if kv.len() <= pos + 1 {
|
||||
add_err();
|
||||
return (key, None)
|
||||
}
|
||||
(key, Some(&kv[pos+1..]))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn input_file_from_opts(file_path: &str, err: &mut InvalidOptionsError) -> Option<fs::File> {
|
||||
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<Mime> {
|
||||
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<Write> {
|
||||
if !flag || arg == "-" {
|
||||
Box::new(stdout())
|
||||
} else {
|
||||
Box::new(fs::OpenOptions::new().create(true).write(true).open(arg).unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn arg_from_str<T>(arg: &str, err: &mut InvalidOptionsError,
|
||||
arg_name: &'static str,
|
||||
arg_type: &'static str) -> T
|
||||
where T: FromStr + Default,
|
||||
<T as FromStr>::Err: fmt::Display {
|
||||
match FromStr::from_str(arg) {
|
||||
Err(perr) => {
|
||||
err.issues.push(
|
||||
CLIError::ParseError((arg_name, arg_type, arg.to_string(), format!("{}", perr)))
|
||||
);
|
||||
Default::default()
|
||||
},
|
||||
Ok(v) => v,
|
||||
}
|
||||
}
|
||||
|
||||
pub struct JsonTokenStorage {
|
||||
pub program_name: &'static str,
|
||||
pub db_dir: String,
|
||||
}
|
||||
|
||||
impl JsonTokenStorage {
|
||||
fn path(&self, scope_hash: u64) -> PathBuf {
|
||||
Path::new(&self.db_dir).join(&format!("{}-token-{}.json", self.program_name, scope_hash))
|
||||
}
|
||||
}
|
||||
|
||||
impl TokenStorage for JsonTokenStorage {
|
||||
type Error = io::Error;
|
||||
|
||||
// NOTE: logging might be interesting, currently we swallow all errors
|
||||
fn set(&mut self, scope_hash: u64, _: &Vec<&str>, token: Option<Token>) -> Option<io::Error> {
|
||||
match token {
|
||||
None => {
|
||||
match fs::remove_file(self.path(scope_hash)) {
|
||||
Err(err) =>
|
||||
match err.kind() {
|
||||
io::ErrorKind::NotFound => None,
|
||||
_ => Some(err)
|
||||
},
|
||||
Ok(_) => None
|
||||
}
|
||||
}
|
||||
Some(token) => {
|
||||
let json_token = json::encode(&token).unwrap();
|
||||
match fs::OpenOptions::new().create(true).write(true).open(&self.path(scope_hash)) {
|
||||
Ok(mut f) => {
|
||||
match f.write(json_token.as_bytes()) {
|
||||
Ok(_) => None,
|
||||
Err(io_err) => Some(io_err),
|
||||
}
|
||||
},
|
||||
Err(io_err) => Some(io_err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get(&self, scope_hash: u64, _: &Vec<&str>) -> Result<Option<Token>, io::Error> {
|
||||
match fs::File::open(&self.path(scope_hash)) {
|
||||
Ok(mut f) => {
|
||||
let mut json_string = String::new();
|
||||
match f.read_to_string(&mut json_string) {
|
||||
Ok(_) => Ok(Some(json::decode::<Token>(&json_string).unwrap())),
|
||||
Err(io_err) => Err(io_err),
|
||||
}
|
||||
},
|
||||
Err(io_err) => {
|
||||
match io_err.kind() {
|
||||
io::ErrorKind::NotFound => Ok(None),
|
||||
_ => Err(io_err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug)]
|
||||
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)),
|
||||
}
|
||||
|
||||
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 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 FieldError {
|
||||
PopOnEmpty(String),
|
||||
TrailingFieldSep(String),
|
||||
Unknown(String),
|
||||
Empty,
|
||||
}
|
||||
|
||||
|
||||
impl fmt::Display for FieldError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
match *self {
|
||||
FieldError::PopOnEmpty(ref field)
|
||||
=> writeln!(f, "'{}': Cannot move up on empty field cursor", field),
|
||||
FieldError::TrailingFieldSep(ref field)
|
||||
=> writeln!(f, "'{}': Single field separator may not be last character", field),
|
||||
FieldError::Unknown(ref field)
|
||||
=> writeln!(f, "Field '{}' does not exist", field),
|
||||
FieldError::Empty
|
||||
=> writeln!(f, "Field names must not be empty"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum CLIError {
|
||||
Configuration(ConfigurationError),
|
||||
ParseError((&'static str, &'static str, String, String)),
|
||||
UnknownParameter(String),
|
||||
InvalidKeyValueSyntax(String, bool),
|
||||
Input(InputError),
|
||||
Field(FieldError),
|
||||
}
|
||||
|
||||
impl fmt::Display for CLIError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
match *self {
|
||||
CLIError::Configuration(ref err) => write!(f, "Configuration -> {}", err),
|
||||
CLIError::Input(ref err) => write!(f, "Input -> {}", err),
|
||||
CLIError::Field(ref err) => write!(f, "Field -> {}", 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),
|
||||
CLIError::UnknownParameter(ref param_name)
|
||||
=> writeln!(f, "Parameter '{}' is unknown.", param_name),
|
||||
CLIError::InvalidKeyValueSyntax(ref kv, is_hashmap) => {
|
||||
let hashmap_info = if is_hashmap { "hashmap " } else { "" };
|
||||
writeln!(f, "'{}' does not match {}pattern <key>=<value>", kv, hashmap_info)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct InvalidOptionsError {
|
||||
pub issues: Vec<CLIError>,
|
||||
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: CLIError, exit_code: i32) -> InvalidOptionsError {
|
||||
InvalidOptionsError {
|
||||
issues: vec![err],
|
||||
exit_code: exit_code,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new() -> InvalidOptionsError {
|
||||
InvalidOptionsError {
|
||||
issues: Vec::new(),
|
||||
exit_code: 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn assure_config_dir_exists(dir: &str) -> Result<String, CLIError> {
|
||||
let trdir = dir.trim();
|
||||
if trdir.len() == 0 {
|
||||
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(CLIError::Configuration(ConfigurationError::HomeExpansionFailed(trdir.to_string()))),
|
||||
Some(mut user) => {
|
||||
user.push_str(&trdir[1..]);
|
||||
user
|
||||
}
|
||||
}
|
||||
} else {
|
||||
trdir.to_string()
|
||||
};
|
||||
|
||||
if let Err(err) = fs::create_dir(&expanded_config_dir) {
|
||||
if err.kind() != io::ErrorKind::AlreadyExists {
|
||||
return Err(CLIError::Configuration(
|
||||
ConfigurationError::DirectoryCreationFailed((expanded_config_dir, err))))
|
||||
}
|
||||
}
|
||||
|
||||
Ok(expanded_config_dir)
|
||||
}
|
||||
|
||||
pub fn application_secret_from_directory(dir: &str,
|
||||
secret_basename: &str,
|
||||
json_app_secret: &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(CLIError::Configuration(ConfigurationError::IOError(
|
||||
(secret_str(), io_err)
|
||||
)))
|
||||
};
|
||||
|
||||
for _ in 0..2 {
|
||||
match fs::File::open(&secret_path) {
|
||||
Err(mut err) => {
|
||||
if err.kind() == io::ErrorKind::NotFound {
|
||||
// Write our built-in one - user may adjust the written file at will
|
||||
|
||||
err = match fs::OpenOptions::new().create(true).write(true).open(&secret_path) {
|
||||
Err(cfe) => cfe,
|
||||
Ok(mut f) => {
|
||||
match f.write(json_app_secret.as_bytes()) {
|
||||
Err(io_err) => io_err,
|
||||
Ok(_) => continue,
|
||||
}
|
||||
}
|
||||
};
|
||||
// fall through to IO error handling
|
||||
}
|
||||
return secret_io_error(err)
|
||||
},
|
||||
Ok(mut f) => {
|
||||
let mut json_encoded_secret = String::new();
|
||||
if let Err(io_err) = f.read_to_string(&mut json_encoded_secret) {
|
||||
return secret_io_error(io_err)
|
||||
}
|
||||
match json::decode::<ConsoleApplicationSecret>(&json_encoded_secret) {
|
||||
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(
|
||||
CLIError::Configuration(
|
||||
ConfigurationError::Secret(
|
||||
ApplicationSecretError::FormatError(secret_str())
|
||||
)))
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
unreachable!();
|
||||
}
|
||||
3364
gen/games1-cli/src/main.rs
Normal file
3364
gen/games1-cli/src/main.rs
Normal file
File diff suppressed because it is too large
Load Diff
31
gen/identitytoolkit3-cli/Cargo.toml
Normal file
31
gen/identitytoolkit3-cli/Cargo.toml
Normal file
@@ -0,0 +1,31 @@
|
||||
# DO NOT EDIT !
|
||||
# This file was generated automatically from 'src/mako/Cargo.toml.mako'
|
||||
# DO NOT EDIT !
|
||||
[package]
|
||||
|
||||
name = "google-identitytoolkit3-cli"
|
||||
version = "0.0.1+20150406"
|
||||
authors = ["Sebastian Thiel <byronimo@gmail.com>"]
|
||||
description = "A complete library to interact with Identity Toolkit (protocol v3)"
|
||||
repository = "https://github.com/Byron/google-apis-rs/tree/master/gen/identitytoolkit3-cli"
|
||||
homepage = "https://developers.google.com/identity-toolkit/v3/"
|
||||
documentation = "http://byron.github.io/google-apis-rs/google_identitytoolkit3_cli"
|
||||
license = "MIT"
|
||||
keywords = ["identitytoolkit", "google", "cli"]
|
||||
|
||||
[[bin]]
|
||||
name = "identitytoolkit3"
|
||||
|
||||
[dependencies]
|
||||
hyper = "*"
|
||||
mime = "*"
|
||||
yup-oauth2 = "*"
|
||||
docopt = "*"
|
||||
docopt_macros = "*"
|
||||
rustc-serialize = "*"
|
||||
yup-hyper-mock = "*"
|
||||
serde = ">= 0.3.0"
|
||||
serde_macros = "*"
|
||||
|
||||
[dependencies.google-identitytoolkit3]
|
||||
path = "../identitytoolkit3"
|
||||
30
gen/identitytoolkit3-cli/LICENSE.md
Normal file
30
gen/identitytoolkit3-cli/LICENSE.md
Normal file
@@ -0,0 +1,30 @@
|
||||
<!---
|
||||
DO NOT EDIT !
|
||||
This file was generated automatically from 'src/mako/LICENSE.md.mako'
|
||||
DO NOT EDIT !
|
||||
-->
|
||||
The MIT License (MIT)
|
||||
=====================
|
||||
|
||||
Copyright © `2015` `Sebastian Thiel`
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the “Software”), to deal in the Software without
|
||||
restriction, including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
4
gen/identitytoolkit3-cli/README.md
Normal file
4
gen/identitytoolkit3-cli/README.md
Normal file
@@ -0,0 +1,4 @@
|
||||
# HELLO IDENTITYTOOLKIT:V3
|
||||
|
||||
|
||||
Include information about application secret files, and how we automatically write a default one.
|
||||
28
gen/identitytoolkit3-cli/mkdocs.yml
Normal file
28
gen/identitytoolkit3-cli/mkdocs.yml
Normal file
@@ -0,0 +1,28 @@
|
||||
site_name: Identity Toolkit v0.0.1+20150406
|
||||
site_url: http://byron.github.io/google-apis-rs/google-identitytoolkit3-cli
|
||||
site_description: Write integrating applications with bcore
|
||||
|
||||
repo_url: https://github.com/Byron/google-apis-rs/tree/master/gen/identitytoolkit3-cli
|
||||
|
||||
docs_dir: docs
|
||||
site_dir: build_html
|
||||
|
||||
pages:
|
||||
- ['index.md', 'Home']
|
||||
- ['relyingparty_create-auth-uri.md', 'Relyingparty', 'Create Auth Uri']
|
||||
- ['relyingparty_delete-account.md', 'Relyingparty', 'Delete Account']
|
||||
- ['relyingparty_download-account.md', 'Relyingparty', 'Download Account']
|
||||
- ['relyingparty_get-account-info.md', 'Relyingparty', 'Get Account Info']
|
||||
- ['relyingparty_get-oob-confirmation-code.md', 'Relyingparty', 'Get Oob Confirmation Code']
|
||||
- ['relyingparty_get-public-keys.md', 'Relyingparty', 'Get Public Keys']
|
||||
- ['relyingparty_get-recaptcha-param.md', 'Relyingparty', 'Get Recaptcha Param']
|
||||
- ['relyingparty_reset-password.md', 'Relyingparty', 'Reset Password']
|
||||
- ['relyingparty_set-account-info.md', 'Relyingparty', 'Set Account Info']
|
||||
- ['relyingparty_upload-account.md', 'Relyingparty', 'Upload Account']
|
||||
- ['relyingparty_verify-assertion.md', 'Relyingparty', 'Verify Assertion']
|
||||
- ['relyingparty_verify-password.md', 'Relyingparty', 'Verify Password']
|
||||
|
||||
theme: readthedocs
|
||||
|
||||
copyright: Copyright © 2015, `Sebastian Thiel`
|
||||
|
||||
454
gen/identitytoolkit3-cli/src/cmn.rs
Normal file
454
gen/identitytoolkit3-cli/src/cmn.rs
Normal file
@@ -0,0 +1,454 @@
|
||||
// COPY OF 'src/rust/cli/cmn.rs'
|
||||
// DO NOT EDIT
|
||||
use oauth2::{ApplicationSecret, ConsoleApplicationSecret, TokenStorage, Token};
|
||||
use rustc_serialize::json;
|
||||
use mime::Mime;
|
||||
|
||||
use std::fs;
|
||||
use std::env;
|
||||
use std::io;
|
||||
use std::fmt;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::str::FromStr;
|
||||
use std::string::ToString;
|
||||
use std::io::{Write, Read, stdout};
|
||||
|
||||
use std::default::Default;
|
||||
|
||||
const FIELD_SEP: char = '.';
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct FieldCursor(Vec<String>);
|
||||
|
||||
impl ToString for FieldCursor {
|
||||
fn to_string(&self) -> String {
|
||||
self.0.connect(".")
|
||||
}
|
||||
}
|
||||
|
||||
impl FieldCursor {
|
||||
pub fn set(&mut self, value: &str) -> Result<(), CLIError> {
|
||||
if value.len() == 0 {
|
||||
return Err(CLIError::Field(FieldError::Empty))
|
||||
}
|
||||
|
||||
let mut first_is_field_sep = false;
|
||||
let mut char_count: usize = 0;
|
||||
let mut last_c = FIELD_SEP;
|
||||
let mut num_conscutive_field_seps = 0;
|
||||
|
||||
let mut field = String::new();
|
||||
let mut fields = self.0.clone();
|
||||
|
||||
let push_field = |fs: &mut Vec<String>, f: &mut String| {
|
||||
if f.len() > 0 {
|
||||
fs.push(f.clone());
|
||||
f.truncate(0);
|
||||
}
|
||||
};
|
||||
|
||||
for (cid, c) in value.chars().enumerate() {
|
||||
char_count += 1;
|
||||
|
||||
if c == FIELD_SEP {
|
||||
if cid == 0 {
|
||||
first_is_field_sep = true;
|
||||
}
|
||||
num_conscutive_field_seps += 1;
|
||||
if cid > 0 && last_c == FIELD_SEP {
|
||||
if fields.pop().is_none() {
|
||||
return Err(CLIError::Field(FieldError::PopOnEmpty(value.to_string())))
|
||||
}
|
||||
} else {
|
||||
push_field(&mut fields, &mut field);
|
||||
}
|
||||
} else {
|
||||
num_conscutive_field_seps = 0;
|
||||
if cid == 1 {
|
||||
if first_is_field_sep {
|
||||
fields.truncate(0);
|
||||
}
|
||||
}
|
||||
field.push(c);
|
||||
}
|
||||
|
||||
last_c = c;
|
||||
}
|
||||
|
||||
push_field(&mut fields, &mut field);
|
||||
|
||||
if char_count == 1 && first_is_field_sep {
|
||||
fields.truncate(0);
|
||||
}
|
||||
if char_count > 1 && num_conscutive_field_seps == 1 {
|
||||
return Err(CLIError::Field(FieldError::TrailingFieldSep(value.to_string())))
|
||||
}
|
||||
|
||||
self.0 = fields;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn num_fields(&self) -> usize {
|
||||
self.0.len()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_kv_arg<'a>(kv: &'a str, err: &mut InvalidOptionsError, for_hashmap: bool)
|
||||
-> (&'a str, Option<&'a str>) {
|
||||
let mut add_err = || err.issues.push(CLIError::InvalidKeyValueSyntax(kv.to_string(),for_hashmap));
|
||||
match kv.find('=') {
|
||||
None => {
|
||||
add_err();
|
||||
return (kv, None)
|
||||
},
|
||||
Some(pos) => {
|
||||
let key = &kv[..pos];
|
||||
if kv.len() <= pos + 1 {
|
||||
add_err();
|
||||
return (key, None)
|
||||
}
|
||||
(key, Some(&kv[pos+1..]))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn input_file_from_opts(file_path: &str, err: &mut InvalidOptionsError) -> Option<fs::File> {
|
||||
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<Mime> {
|
||||
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<Write> {
|
||||
if !flag || arg == "-" {
|
||||
Box::new(stdout())
|
||||
} else {
|
||||
Box::new(fs::OpenOptions::new().create(true).write(true).open(arg).unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn arg_from_str<T>(arg: &str, err: &mut InvalidOptionsError,
|
||||
arg_name: &'static str,
|
||||
arg_type: &'static str) -> T
|
||||
where T: FromStr + Default,
|
||||
<T as FromStr>::Err: fmt::Display {
|
||||
match FromStr::from_str(arg) {
|
||||
Err(perr) => {
|
||||
err.issues.push(
|
||||
CLIError::ParseError((arg_name, arg_type, arg.to_string(), format!("{}", perr)))
|
||||
);
|
||||
Default::default()
|
||||
},
|
||||
Ok(v) => v,
|
||||
}
|
||||
}
|
||||
|
||||
pub struct JsonTokenStorage {
|
||||
pub program_name: &'static str,
|
||||
pub db_dir: String,
|
||||
}
|
||||
|
||||
impl JsonTokenStorage {
|
||||
fn path(&self, scope_hash: u64) -> PathBuf {
|
||||
Path::new(&self.db_dir).join(&format!("{}-token-{}.json", self.program_name, scope_hash))
|
||||
}
|
||||
}
|
||||
|
||||
impl TokenStorage for JsonTokenStorage {
|
||||
type Error = io::Error;
|
||||
|
||||
// NOTE: logging might be interesting, currently we swallow all errors
|
||||
fn set(&mut self, scope_hash: u64, _: &Vec<&str>, token: Option<Token>) -> Option<io::Error> {
|
||||
match token {
|
||||
None => {
|
||||
match fs::remove_file(self.path(scope_hash)) {
|
||||
Err(err) =>
|
||||
match err.kind() {
|
||||
io::ErrorKind::NotFound => None,
|
||||
_ => Some(err)
|
||||
},
|
||||
Ok(_) => None
|
||||
}
|
||||
}
|
||||
Some(token) => {
|
||||
let json_token = json::encode(&token).unwrap();
|
||||
match fs::OpenOptions::new().create(true).write(true).open(&self.path(scope_hash)) {
|
||||
Ok(mut f) => {
|
||||
match f.write(json_token.as_bytes()) {
|
||||
Ok(_) => None,
|
||||
Err(io_err) => Some(io_err),
|
||||
}
|
||||
},
|
||||
Err(io_err) => Some(io_err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get(&self, scope_hash: u64, _: &Vec<&str>) -> Result<Option<Token>, io::Error> {
|
||||
match fs::File::open(&self.path(scope_hash)) {
|
||||
Ok(mut f) => {
|
||||
let mut json_string = String::new();
|
||||
match f.read_to_string(&mut json_string) {
|
||||
Ok(_) => Ok(Some(json::decode::<Token>(&json_string).unwrap())),
|
||||
Err(io_err) => Err(io_err),
|
||||
}
|
||||
},
|
||||
Err(io_err) => {
|
||||
match io_err.kind() {
|
||||
io::ErrorKind::NotFound => Ok(None),
|
||||
_ => Err(io_err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug)]
|
||||
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)),
|
||||
}
|
||||
|
||||
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 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 FieldError {
|
||||
PopOnEmpty(String),
|
||||
TrailingFieldSep(String),
|
||||
Unknown(String),
|
||||
Empty,
|
||||
}
|
||||
|
||||
|
||||
impl fmt::Display for FieldError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
match *self {
|
||||
FieldError::PopOnEmpty(ref field)
|
||||
=> writeln!(f, "'{}': Cannot move up on empty field cursor", field),
|
||||
FieldError::TrailingFieldSep(ref field)
|
||||
=> writeln!(f, "'{}': Single field separator may not be last character", field),
|
||||
FieldError::Unknown(ref field)
|
||||
=> writeln!(f, "Field '{}' does not exist", field),
|
||||
FieldError::Empty
|
||||
=> writeln!(f, "Field names must not be empty"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum CLIError {
|
||||
Configuration(ConfigurationError),
|
||||
ParseError((&'static str, &'static str, String, String)),
|
||||
UnknownParameter(String),
|
||||
InvalidKeyValueSyntax(String, bool),
|
||||
Input(InputError),
|
||||
Field(FieldError),
|
||||
}
|
||||
|
||||
impl fmt::Display for CLIError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
match *self {
|
||||
CLIError::Configuration(ref err) => write!(f, "Configuration -> {}", err),
|
||||
CLIError::Input(ref err) => write!(f, "Input -> {}", err),
|
||||
CLIError::Field(ref err) => write!(f, "Field -> {}", 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),
|
||||
CLIError::UnknownParameter(ref param_name)
|
||||
=> writeln!(f, "Parameter '{}' is unknown.", param_name),
|
||||
CLIError::InvalidKeyValueSyntax(ref kv, is_hashmap) => {
|
||||
let hashmap_info = if is_hashmap { "hashmap " } else { "" };
|
||||
writeln!(f, "'{}' does not match {}pattern <key>=<value>", kv, hashmap_info)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct InvalidOptionsError {
|
||||
pub issues: Vec<CLIError>,
|
||||
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: CLIError, exit_code: i32) -> InvalidOptionsError {
|
||||
InvalidOptionsError {
|
||||
issues: vec![err],
|
||||
exit_code: exit_code,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new() -> InvalidOptionsError {
|
||||
InvalidOptionsError {
|
||||
issues: Vec::new(),
|
||||
exit_code: 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn assure_config_dir_exists(dir: &str) -> Result<String, CLIError> {
|
||||
let trdir = dir.trim();
|
||||
if trdir.len() == 0 {
|
||||
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(CLIError::Configuration(ConfigurationError::HomeExpansionFailed(trdir.to_string()))),
|
||||
Some(mut user) => {
|
||||
user.push_str(&trdir[1..]);
|
||||
user
|
||||
}
|
||||
}
|
||||
} else {
|
||||
trdir.to_string()
|
||||
};
|
||||
|
||||
if let Err(err) = fs::create_dir(&expanded_config_dir) {
|
||||
if err.kind() != io::ErrorKind::AlreadyExists {
|
||||
return Err(CLIError::Configuration(
|
||||
ConfigurationError::DirectoryCreationFailed((expanded_config_dir, err))))
|
||||
}
|
||||
}
|
||||
|
||||
Ok(expanded_config_dir)
|
||||
}
|
||||
|
||||
pub fn application_secret_from_directory(dir: &str,
|
||||
secret_basename: &str,
|
||||
json_app_secret: &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(CLIError::Configuration(ConfigurationError::IOError(
|
||||
(secret_str(), io_err)
|
||||
)))
|
||||
};
|
||||
|
||||
for _ in 0..2 {
|
||||
match fs::File::open(&secret_path) {
|
||||
Err(mut err) => {
|
||||
if err.kind() == io::ErrorKind::NotFound {
|
||||
// Write our built-in one - user may adjust the written file at will
|
||||
|
||||
err = match fs::OpenOptions::new().create(true).write(true).open(&secret_path) {
|
||||
Err(cfe) => cfe,
|
||||
Ok(mut f) => {
|
||||
match f.write(json_app_secret.as_bytes()) {
|
||||
Err(io_err) => io_err,
|
||||
Ok(_) => continue,
|
||||
}
|
||||
}
|
||||
};
|
||||
// fall through to IO error handling
|
||||
}
|
||||
return secret_io_error(err)
|
||||
},
|
||||
Ok(mut f) => {
|
||||
let mut json_encoded_secret = String::new();
|
||||
if let Err(io_err) = f.read_to_string(&mut json_encoded_secret) {
|
||||
return secret_io_error(io_err)
|
||||
}
|
||||
match json::decode::<ConsoleApplicationSecret>(&json_encoded_secret) {
|
||||
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(
|
||||
CLIError::Configuration(
|
||||
ConfigurationError::Secret(
|
||||
ApplicationSecretError::FormatError(secret_str())
|
||||
)))
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
unreachable!();
|
||||
}
|
||||
1006
gen/identitytoolkit3-cli/src/main.rs
Normal file
1006
gen/identitytoolkit3-cli/src/main.rs
Normal file
File diff suppressed because it is too large
Load Diff
30
gen/logging1_beta3-cli/Cargo.toml
Normal file
30
gen/logging1_beta3-cli/Cargo.toml
Normal file
@@ -0,0 +1,30 @@
|
||||
# DO NOT EDIT !
|
||||
# This file was generated automatically from 'src/mako/Cargo.toml.mako'
|
||||
# DO NOT EDIT !
|
||||
[package]
|
||||
|
||||
name = "google-logging1_beta3-cli"
|
||||
version = "0.0.1+20150326"
|
||||
authors = ["Sebastian Thiel <byronimo@gmail.com>"]
|
||||
description = "A complete library to interact with logging (protocol v1beta3)"
|
||||
repository = "https://github.com/Byron/google-apis-rs/tree/master/gen/logging1_beta3-cli"
|
||||
documentation = "http://byron.github.io/google-apis-rs/google_logging1_beta3_cli"
|
||||
license = "MIT"
|
||||
keywords = ["logging", "google", "cli"]
|
||||
|
||||
[[bin]]
|
||||
name = "logging1-beta3"
|
||||
|
||||
[dependencies]
|
||||
hyper = "*"
|
||||
mime = "*"
|
||||
yup-oauth2 = "*"
|
||||
docopt = "*"
|
||||
docopt_macros = "*"
|
||||
rustc-serialize = "*"
|
||||
yup-hyper-mock = "*"
|
||||
serde = ">= 0.3.0"
|
||||
serde_macros = "*"
|
||||
|
||||
[dependencies.google-logging1_beta3]
|
||||
path = "../logging1_beta3"
|
||||
30
gen/logging1_beta3-cli/LICENSE.md
Normal file
30
gen/logging1_beta3-cli/LICENSE.md
Normal file
@@ -0,0 +1,30 @@
|
||||
<!---
|
||||
DO NOT EDIT !
|
||||
This file was generated automatically from 'src/mako/LICENSE.md.mako'
|
||||
DO NOT EDIT !
|
||||
-->
|
||||
The MIT License (MIT)
|
||||
=====================
|
||||
|
||||
Copyright © `2015` `Sebastian Thiel`
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the “Software”), to deal in the Software without
|
||||
restriction, including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
4
gen/logging1_beta3-cli/README.md
Normal file
4
gen/logging1_beta3-cli/README.md
Normal file
@@ -0,0 +1,4 @@
|
||||
# HELLO LOGGING:V1BETA3
|
||||
|
||||
|
||||
Include information about application secret files, and how we automatically write a default one.
|
||||
31
gen/logging1_beta3-cli/mkdocs.yml
Normal file
31
gen/logging1_beta3-cli/mkdocs.yml
Normal file
@@ -0,0 +1,31 @@
|
||||
site_name: logging v0.0.1+20150326
|
||||
site_url: http://byron.github.io/google-apis-rs/google-logging1_beta3-cli
|
||||
site_description: Write integrating applications with bcore
|
||||
|
||||
repo_url: https://github.com/Byron/google-apis-rs/tree/master/gen/logging1_beta3-cli
|
||||
|
||||
docs_dir: docs
|
||||
site_dir: build_html
|
||||
|
||||
pages:
|
||||
- ['index.md', 'Home']
|
||||
- ['projects_log-services-indexes-list.md', 'Projects', 'Log Services Indexes List']
|
||||
- ['projects_log-services-list.md', 'Projects', 'Log Services List']
|
||||
- ['projects_log-services-sinks-create.md', 'Projects', 'Log Services Sinks Create']
|
||||
- ['projects_log-services-sinks-delete.md', 'Projects', 'Log Services Sinks Delete']
|
||||
- ['projects_log-services-sinks-get.md', 'Projects', 'Log Services Sinks Get']
|
||||
- ['projects_log-services-sinks-list.md', 'Projects', 'Log Services Sinks List']
|
||||
- ['projects_log-services-sinks-update.md', 'Projects', 'Log Services Sinks Update']
|
||||
- ['projects_logs-delete.md', 'Projects', 'Logs Delete']
|
||||
- ['projects_logs-entries-write.md', 'Projects', 'Logs Entries Write']
|
||||
- ['projects_logs-list.md', 'Projects', 'Logs List']
|
||||
- ['projects_logs-sinks-create.md', 'Projects', 'Logs Sinks Create']
|
||||
- ['projects_logs-sinks-delete.md', 'Projects', 'Logs Sinks Delete']
|
||||
- ['projects_logs-sinks-get.md', 'Projects', 'Logs Sinks Get']
|
||||
- ['projects_logs-sinks-list.md', 'Projects', 'Logs Sinks List']
|
||||
- ['projects_logs-sinks-update.md', 'Projects', 'Logs Sinks Update']
|
||||
|
||||
theme: readthedocs
|
||||
|
||||
copyright: Copyright © 2015, `Sebastian Thiel`
|
||||
|
||||
454
gen/logging1_beta3-cli/src/cmn.rs
Normal file
454
gen/logging1_beta3-cli/src/cmn.rs
Normal file
@@ -0,0 +1,454 @@
|
||||
// COPY OF 'src/rust/cli/cmn.rs'
|
||||
// DO NOT EDIT
|
||||
use oauth2::{ApplicationSecret, ConsoleApplicationSecret, TokenStorage, Token};
|
||||
use rustc_serialize::json;
|
||||
use mime::Mime;
|
||||
|
||||
use std::fs;
|
||||
use std::env;
|
||||
use std::io;
|
||||
use std::fmt;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::str::FromStr;
|
||||
use std::string::ToString;
|
||||
use std::io::{Write, Read, stdout};
|
||||
|
||||
use std::default::Default;
|
||||
|
||||
const FIELD_SEP: char = '.';
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct FieldCursor(Vec<String>);
|
||||
|
||||
impl ToString for FieldCursor {
|
||||
fn to_string(&self) -> String {
|
||||
self.0.connect(".")
|
||||
}
|
||||
}
|
||||
|
||||
impl FieldCursor {
|
||||
pub fn set(&mut self, value: &str) -> Result<(), CLIError> {
|
||||
if value.len() == 0 {
|
||||
return Err(CLIError::Field(FieldError::Empty))
|
||||
}
|
||||
|
||||
let mut first_is_field_sep = false;
|
||||
let mut char_count: usize = 0;
|
||||
let mut last_c = FIELD_SEP;
|
||||
let mut num_conscutive_field_seps = 0;
|
||||
|
||||
let mut field = String::new();
|
||||
let mut fields = self.0.clone();
|
||||
|
||||
let push_field = |fs: &mut Vec<String>, f: &mut String| {
|
||||
if f.len() > 0 {
|
||||
fs.push(f.clone());
|
||||
f.truncate(0);
|
||||
}
|
||||
};
|
||||
|
||||
for (cid, c) in value.chars().enumerate() {
|
||||
char_count += 1;
|
||||
|
||||
if c == FIELD_SEP {
|
||||
if cid == 0 {
|
||||
first_is_field_sep = true;
|
||||
}
|
||||
num_conscutive_field_seps += 1;
|
||||
if cid > 0 && last_c == FIELD_SEP {
|
||||
if fields.pop().is_none() {
|
||||
return Err(CLIError::Field(FieldError::PopOnEmpty(value.to_string())))
|
||||
}
|
||||
} else {
|
||||
push_field(&mut fields, &mut field);
|
||||
}
|
||||
} else {
|
||||
num_conscutive_field_seps = 0;
|
||||
if cid == 1 {
|
||||
if first_is_field_sep {
|
||||
fields.truncate(0);
|
||||
}
|
||||
}
|
||||
field.push(c);
|
||||
}
|
||||
|
||||
last_c = c;
|
||||
}
|
||||
|
||||
push_field(&mut fields, &mut field);
|
||||
|
||||
if char_count == 1 && first_is_field_sep {
|
||||
fields.truncate(0);
|
||||
}
|
||||
if char_count > 1 && num_conscutive_field_seps == 1 {
|
||||
return Err(CLIError::Field(FieldError::TrailingFieldSep(value.to_string())))
|
||||
}
|
||||
|
||||
self.0 = fields;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn num_fields(&self) -> usize {
|
||||
self.0.len()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_kv_arg<'a>(kv: &'a str, err: &mut InvalidOptionsError, for_hashmap: bool)
|
||||
-> (&'a str, Option<&'a str>) {
|
||||
let mut add_err = || err.issues.push(CLIError::InvalidKeyValueSyntax(kv.to_string(),for_hashmap));
|
||||
match kv.find('=') {
|
||||
None => {
|
||||
add_err();
|
||||
return (kv, None)
|
||||
},
|
||||
Some(pos) => {
|
||||
let key = &kv[..pos];
|
||||
if kv.len() <= pos + 1 {
|
||||
add_err();
|
||||
return (key, None)
|
||||
}
|
||||
(key, Some(&kv[pos+1..]))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn input_file_from_opts(file_path: &str, err: &mut InvalidOptionsError) -> Option<fs::File> {
|
||||
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<Mime> {
|
||||
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<Write> {
|
||||
if !flag || arg == "-" {
|
||||
Box::new(stdout())
|
||||
} else {
|
||||
Box::new(fs::OpenOptions::new().create(true).write(true).open(arg).unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn arg_from_str<T>(arg: &str, err: &mut InvalidOptionsError,
|
||||
arg_name: &'static str,
|
||||
arg_type: &'static str) -> T
|
||||
where T: FromStr + Default,
|
||||
<T as FromStr>::Err: fmt::Display {
|
||||
match FromStr::from_str(arg) {
|
||||
Err(perr) => {
|
||||
err.issues.push(
|
||||
CLIError::ParseError((arg_name, arg_type, arg.to_string(), format!("{}", perr)))
|
||||
);
|
||||
Default::default()
|
||||
},
|
||||
Ok(v) => v,
|
||||
}
|
||||
}
|
||||
|
||||
pub struct JsonTokenStorage {
|
||||
pub program_name: &'static str,
|
||||
pub db_dir: String,
|
||||
}
|
||||
|
||||
impl JsonTokenStorage {
|
||||
fn path(&self, scope_hash: u64) -> PathBuf {
|
||||
Path::new(&self.db_dir).join(&format!("{}-token-{}.json", self.program_name, scope_hash))
|
||||
}
|
||||
}
|
||||
|
||||
impl TokenStorage for JsonTokenStorage {
|
||||
type Error = io::Error;
|
||||
|
||||
// NOTE: logging might be interesting, currently we swallow all errors
|
||||
fn set(&mut self, scope_hash: u64, _: &Vec<&str>, token: Option<Token>) -> Option<io::Error> {
|
||||
match token {
|
||||
None => {
|
||||
match fs::remove_file(self.path(scope_hash)) {
|
||||
Err(err) =>
|
||||
match err.kind() {
|
||||
io::ErrorKind::NotFound => None,
|
||||
_ => Some(err)
|
||||
},
|
||||
Ok(_) => None
|
||||
}
|
||||
}
|
||||
Some(token) => {
|
||||
let json_token = json::encode(&token).unwrap();
|
||||
match fs::OpenOptions::new().create(true).write(true).open(&self.path(scope_hash)) {
|
||||
Ok(mut f) => {
|
||||
match f.write(json_token.as_bytes()) {
|
||||
Ok(_) => None,
|
||||
Err(io_err) => Some(io_err),
|
||||
}
|
||||
},
|
||||
Err(io_err) => Some(io_err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get(&self, scope_hash: u64, _: &Vec<&str>) -> Result<Option<Token>, io::Error> {
|
||||
match fs::File::open(&self.path(scope_hash)) {
|
||||
Ok(mut f) => {
|
||||
let mut json_string = String::new();
|
||||
match f.read_to_string(&mut json_string) {
|
||||
Ok(_) => Ok(Some(json::decode::<Token>(&json_string).unwrap())),
|
||||
Err(io_err) => Err(io_err),
|
||||
}
|
||||
},
|
||||
Err(io_err) => {
|
||||
match io_err.kind() {
|
||||
io::ErrorKind::NotFound => Ok(None),
|
||||
_ => Err(io_err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug)]
|
||||
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)),
|
||||
}
|
||||
|
||||
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 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 FieldError {
|
||||
PopOnEmpty(String),
|
||||
TrailingFieldSep(String),
|
||||
Unknown(String),
|
||||
Empty,
|
||||
}
|
||||
|
||||
|
||||
impl fmt::Display for FieldError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
match *self {
|
||||
FieldError::PopOnEmpty(ref field)
|
||||
=> writeln!(f, "'{}': Cannot move up on empty field cursor", field),
|
||||
FieldError::TrailingFieldSep(ref field)
|
||||
=> writeln!(f, "'{}': Single field separator may not be last character", field),
|
||||
FieldError::Unknown(ref field)
|
||||
=> writeln!(f, "Field '{}' does not exist", field),
|
||||
FieldError::Empty
|
||||
=> writeln!(f, "Field names must not be empty"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum CLIError {
|
||||
Configuration(ConfigurationError),
|
||||
ParseError((&'static str, &'static str, String, String)),
|
||||
UnknownParameter(String),
|
||||
InvalidKeyValueSyntax(String, bool),
|
||||
Input(InputError),
|
||||
Field(FieldError),
|
||||
}
|
||||
|
||||
impl fmt::Display for CLIError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
match *self {
|
||||
CLIError::Configuration(ref err) => write!(f, "Configuration -> {}", err),
|
||||
CLIError::Input(ref err) => write!(f, "Input -> {}", err),
|
||||
CLIError::Field(ref err) => write!(f, "Field -> {}", 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),
|
||||
CLIError::UnknownParameter(ref param_name)
|
||||
=> writeln!(f, "Parameter '{}' is unknown.", param_name),
|
||||
CLIError::InvalidKeyValueSyntax(ref kv, is_hashmap) => {
|
||||
let hashmap_info = if is_hashmap { "hashmap " } else { "" };
|
||||
writeln!(f, "'{}' does not match {}pattern <key>=<value>", kv, hashmap_info)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct InvalidOptionsError {
|
||||
pub issues: Vec<CLIError>,
|
||||
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: CLIError, exit_code: i32) -> InvalidOptionsError {
|
||||
InvalidOptionsError {
|
||||
issues: vec![err],
|
||||
exit_code: exit_code,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new() -> InvalidOptionsError {
|
||||
InvalidOptionsError {
|
||||
issues: Vec::new(),
|
||||
exit_code: 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn assure_config_dir_exists(dir: &str) -> Result<String, CLIError> {
|
||||
let trdir = dir.trim();
|
||||
if trdir.len() == 0 {
|
||||
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(CLIError::Configuration(ConfigurationError::HomeExpansionFailed(trdir.to_string()))),
|
||||
Some(mut user) => {
|
||||
user.push_str(&trdir[1..]);
|
||||
user
|
||||
}
|
||||
}
|
||||
} else {
|
||||
trdir.to_string()
|
||||
};
|
||||
|
||||
if let Err(err) = fs::create_dir(&expanded_config_dir) {
|
||||
if err.kind() != io::ErrorKind::AlreadyExists {
|
||||
return Err(CLIError::Configuration(
|
||||
ConfigurationError::DirectoryCreationFailed((expanded_config_dir, err))))
|
||||
}
|
||||
}
|
||||
|
||||
Ok(expanded_config_dir)
|
||||
}
|
||||
|
||||
pub fn application_secret_from_directory(dir: &str,
|
||||
secret_basename: &str,
|
||||
json_app_secret: &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(CLIError::Configuration(ConfigurationError::IOError(
|
||||
(secret_str(), io_err)
|
||||
)))
|
||||
};
|
||||
|
||||
for _ in 0..2 {
|
||||
match fs::File::open(&secret_path) {
|
||||
Err(mut err) => {
|
||||
if err.kind() == io::ErrorKind::NotFound {
|
||||
// Write our built-in one - user may adjust the written file at will
|
||||
|
||||
err = match fs::OpenOptions::new().create(true).write(true).open(&secret_path) {
|
||||
Err(cfe) => cfe,
|
||||
Ok(mut f) => {
|
||||
match f.write(json_app_secret.as_bytes()) {
|
||||
Err(io_err) => io_err,
|
||||
Ok(_) => continue,
|
||||
}
|
||||
}
|
||||
};
|
||||
// fall through to IO error handling
|
||||
}
|
||||
return secret_io_error(err)
|
||||
},
|
||||
Ok(mut f) => {
|
||||
let mut json_encoded_secret = String::new();
|
||||
if let Err(io_err) = f.read_to_string(&mut json_encoded_secret) {
|
||||
return secret_io_error(io_err)
|
||||
}
|
||||
match json::decode::<ConsoleApplicationSecret>(&json_encoded_secret) {
|
||||
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(
|
||||
CLIError::Configuration(
|
||||
ConfigurationError::Secret(
|
||||
ApplicationSecretError::FormatError(secret_str())
|
||||
)))
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
unreachable!();
|
||||
}
|
||||
1067
gen/logging1_beta3-cli/src/main.rs
Normal file
1067
gen/logging1_beta3-cli/src/main.rs
Normal file
File diff suppressed because it is too large
Load Diff
31
gen/reseller1_sandbox-cli/Cargo.toml
Normal file
31
gen/reseller1_sandbox-cli/Cargo.toml
Normal file
@@ -0,0 +1,31 @@
|
||||
# DO NOT EDIT !
|
||||
# This file was generated automatically from 'src/mako/Cargo.toml.mako'
|
||||
# DO NOT EDIT !
|
||||
[package]
|
||||
|
||||
name = "google-reseller1_sandbox-cli"
|
||||
version = "0.0.1+20141112"
|
||||
authors = ["Sebastian Thiel <byronimo@gmail.com>"]
|
||||
description = "A complete library to interact with reseller (protocol v1sandbox)"
|
||||
repository = "https://github.com/Byron/google-apis-rs/tree/master/gen/reseller1_sandbox-cli"
|
||||
homepage = "https://developers.google.com/google-apps/reseller/"
|
||||
documentation = "http://byron.github.io/google-apis-rs/google_reseller1_sandbox_cli"
|
||||
license = "MIT"
|
||||
keywords = ["reseller", "google", "cli"]
|
||||
|
||||
[[bin]]
|
||||
name = "reseller1-sandbox"
|
||||
|
||||
[dependencies]
|
||||
hyper = "*"
|
||||
mime = "*"
|
||||
yup-oauth2 = "*"
|
||||
docopt = "*"
|
||||
docopt_macros = "*"
|
||||
rustc-serialize = "*"
|
||||
yup-hyper-mock = "*"
|
||||
serde = ">= 0.3.0"
|
||||
serde_macros = "*"
|
||||
|
||||
[dependencies.google-reseller1_sandbox]
|
||||
path = "../reseller1_sandbox"
|
||||
30
gen/reseller1_sandbox-cli/LICENSE.md
Normal file
30
gen/reseller1_sandbox-cli/LICENSE.md
Normal file
@@ -0,0 +1,30 @@
|
||||
<!---
|
||||
DO NOT EDIT !
|
||||
This file was generated automatically from 'src/mako/LICENSE.md.mako'
|
||||
DO NOT EDIT !
|
||||
-->
|
||||
The MIT License (MIT)
|
||||
=====================
|
||||
|
||||
Copyright © `2015` `Sebastian Thiel`
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the “Software”), to deal in the Software without
|
||||
restriction, including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
4
gen/reseller1_sandbox-cli/README.md
Normal file
4
gen/reseller1_sandbox-cli/README.md
Normal file
@@ -0,0 +1,4 @@
|
||||
# HELLO RESELLER:V1SANDBOX
|
||||
|
||||
|
||||
Include information about application secret files, and how we automatically write a default one.
|
||||
30
gen/reseller1_sandbox-cli/mkdocs.yml
Normal file
30
gen/reseller1_sandbox-cli/mkdocs.yml
Normal file
@@ -0,0 +1,30 @@
|
||||
site_name: reseller v0.0.1+20141112
|
||||
site_url: http://byron.github.io/google-apis-rs/google-reseller1_sandbox-cli
|
||||
site_description: Write integrating applications with bcore
|
||||
|
||||
repo_url: https://github.com/Byron/google-apis-rs/tree/master/gen/reseller1_sandbox-cli
|
||||
|
||||
docs_dir: docs
|
||||
site_dir: build_html
|
||||
|
||||
pages:
|
||||
- ['index.md', 'Home']
|
||||
- ['customers_get.md', 'Customers', 'Get']
|
||||
- ['customers_insert.md', 'Customers', 'Insert']
|
||||
- ['customers_patch.md', 'Customers', 'Patch']
|
||||
- ['customers_update.md', 'Customers', 'Update']
|
||||
- ['subscriptions_activate.md', 'Subscriptions', 'Activate']
|
||||
- ['subscriptions_change-plan.md', 'Subscriptions', 'Change Plan']
|
||||
- ['subscriptions_change-renewal-settings.md', 'Subscriptions', 'Change Renewal Settings']
|
||||
- ['subscriptions_change-seats.md', 'Subscriptions', 'Change Seats']
|
||||
- ['subscriptions_delete.md', 'Subscriptions', 'Delete']
|
||||
- ['subscriptions_get.md', 'Subscriptions', 'Get']
|
||||
- ['subscriptions_insert.md', 'Subscriptions', 'Insert']
|
||||
- ['subscriptions_list.md', 'Subscriptions', 'List']
|
||||
- ['subscriptions_start-paid-service.md', 'Subscriptions', 'Start Paid Service']
|
||||
- ['subscriptions_suspend.md', 'Subscriptions', 'Suspend']
|
||||
|
||||
theme: readthedocs
|
||||
|
||||
copyright: Copyright © 2015, `Sebastian Thiel`
|
||||
|
||||
454
gen/reseller1_sandbox-cli/src/cmn.rs
Normal file
454
gen/reseller1_sandbox-cli/src/cmn.rs
Normal file
@@ -0,0 +1,454 @@
|
||||
// COPY OF 'src/rust/cli/cmn.rs'
|
||||
// DO NOT EDIT
|
||||
use oauth2::{ApplicationSecret, ConsoleApplicationSecret, TokenStorage, Token};
|
||||
use rustc_serialize::json;
|
||||
use mime::Mime;
|
||||
|
||||
use std::fs;
|
||||
use std::env;
|
||||
use std::io;
|
||||
use std::fmt;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::str::FromStr;
|
||||
use std::string::ToString;
|
||||
use std::io::{Write, Read, stdout};
|
||||
|
||||
use std::default::Default;
|
||||
|
||||
const FIELD_SEP: char = '.';
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct FieldCursor(Vec<String>);
|
||||
|
||||
impl ToString for FieldCursor {
|
||||
fn to_string(&self) -> String {
|
||||
self.0.connect(".")
|
||||
}
|
||||
}
|
||||
|
||||
impl FieldCursor {
|
||||
pub fn set(&mut self, value: &str) -> Result<(), CLIError> {
|
||||
if value.len() == 0 {
|
||||
return Err(CLIError::Field(FieldError::Empty))
|
||||
}
|
||||
|
||||
let mut first_is_field_sep = false;
|
||||
let mut char_count: usize = 0;
|
||||
let mut last_c = FIELD_SEP;
|
||||
let mut num_conscutive_field_seps = 0;
|
||||
|
||||
let mut field = String::new();
|
||||
let mut fields = self.0.clone();
|
||||
|
||||
let push_field = |fs: &mut Vec<String>, f: &mut String| {
|
||||
if f.len() > 0 {
|
||||
fs.push(f.clone());
|
||||
f.truncate(0);
|
||||
}
|
||||
};
|
||||
|
||||
for (cid, c) in value.chars().enumerate() {
|
||||
char_count += 1;
|
||||
|
||||
if c == FIELD_SEP {
|
||||
if cid == 0 {
|
||||
first_is_field_sep = true;
|
||||
}
|
||||
num_conscutive_field_seps += 1;
|
||||
if cid > 0 && last_c == FIELD_SEP {
|
||||
if fields.pop().is_none() {
|
||||
return Err(CLIError::Field(FieldError::PopOnEmpty(value.to_string())))
|
||||
}
|
||||
} else {
|
||||
push_field(&mut fields, &mut field);
|
||||
}
|
||||
} else {
|
||||
num_conscutive_field_seps = 0;
|
||||
if cid == 1 {
|
||||
if first_is_field_sep {
|
||||
fields.truncate(0);
|
||||
}
|
||||
}
|
||||
field.push(c);
|
||||
}
|
||||
|
||||
last_c = c;
|
||||
}
|
||||
|
||||
push_field(&mut fields, &mut field);
|
||||
|
||||
if char_count == 1 && first_is_field_sep {
|
||||
fields.truncate(0);
|
||||
}
|
||||
if char_count > 1 && num_conscutive_field_seps == 1 {
|
||||
return Err(CLIError::Field(FieldError::TrailingFieldSep(value.to_string())))
|
||||
}
|
||||
|
||||
self.0 = fields;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn num_fields(&self) -> usize {
|
||||
self.0.len()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_kv_arg<'a>(kv: &'a str, err: &mut InvalidOptionsError, for_hashmap: bool)
|
||||
-> (&'a str, Option<&'a str>) {
|
||||
let mut add_err = || err.issues.push(CLIError::InvalidKeyValueSyntax(kv.to_string(),for_hashmap));
|
||||
match kv.find('=') {
|
||||
None => {
|
||||
add_err();
|
||||
return (kv, None)
|
||||
},
|
||||
Some(pos) => {
|
||||
let key = &kv[..pos];
|
||||
if kv.len() <= pos + 1 {
|
||||
add_err();
|
||||
return (key, None)
|
||||
}
|
||||
(key, Some(&kv[pos+1..]))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn input_file_from_opts(file_path: &str, err: &mut InvalidOptionsError) -> Option<fs::File> {
|
||||
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<Mime> {
|
||||
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<Write> {
|
||||
if !flag || arg == "-" {
|
||||
Box::new(stdout())
|
||||
} else {
|
||||
Box::new(fs::OpenOptions::new().create(true).write(true).open(arg).unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn arg_from_str<T>(arg: &str, err: &mut InvalidOptionsError,
|
||||
arg_name: &'static str,
|
||||
arg_type: &'static str) -> T
|
||||
where T: FromStr + Default,
|
||||
<T as FromStr>::Err: fmt::Display {
|
||||
match FromStr::from_str(arg) {
|
||||
Err(perr) => {
|
||||
err.issues.push(
|
||||
CLIError::ParseError((arg_name, arg_type, arg.to_string(), format!("{}", perr)))
|
||||
);
|
||||
Default::default()
|
||||
},
|
||||
Ok(v) => v,
|
||||
}
|
||||
}
|
||||
|
||||
pub struct JsonTokenStorage {
|
||||
pub program_name: &'static str,
|
||||
pub db_dir: String,
|
||||
}
|
||||
|
||||
impl JsonTokenStorage {
|
||||
fn path(&self, scope_hash: u64) -> PathBuf {
|
||||
Path::new(&self.db_dir).join(&format!("{}-token-{}.json", self.program_name, scope_hash))
|
||||
}
|
||||
}
|
||||
|
||||
impl TokenStorage for JsonTokenStorage {
|
||||
type Error = io::Error;
|
||||
|
||||
// NOTE: logging might be interesting, currently we swallow all errors
|
||||
fn set(&mut self, scope_hash: u64, _: &Vec<&str>, token: Option<Token>) -> Option<io::Error> {
|
||||
match token {
|
||||
None => {
|
||||
match fs::remove_file(self.path(scope_hash)) {
|
||||
Err(err) =>
|
||||
match err.kind() {
|
||||
io::ErrorKind::NotFound => None,
|
||||
_ => Some(err)
|
||||
},
|
||||
Ok(_) => None
|
||||
}
|
||||
}
|
||||
Some(token) => {
|
||||
let json_token = json::encode(&token).unwrap();
|
||||
match fs::OpenOptions::new().create(true).write(true).open(&self.path(scope_hash)) {
|
||||
Ok(mut f) => {
|
||||
match f.write(json_token.as_bytes()) {
|
||||
Ok(_) => None,
|
||||
Err(io_err) => Some(io_err),
|
||||
}
|
||||
},
|
||||
Err(io_err) => Some(io_err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get(&self, scope_hash: u64, _: &Vec<&str>) -> Result<Option<Token>, io::Error> {
|
||||
match fs::File::open(&self.path(scope_hash)) {
|
||||
Ok(mut f) => {
|
||||
let mut json_string = String::new();
|
||||
match f.read_to_string(&mut json_string) {
|
||||
Ok(_) => Ok(Some(json::decode::<Token>(&json_string).unwrap())),
|
||||
Err(io_err) => Err(io_err),
|
||||
}
|
||||
},
|
||||
Err(io_err) => {
|
||||
match io_err.kind() {
|
||||
io::ErrorKind::NotFound => Ok(None),
|
||||
_ => Err(io_err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug)]
|
||||
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)),
|
||||
}
|
||||
|
||||
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 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 FieldError {
|
||||
PopOnEmpty(String),
|
||||
TrailingFieldSep(String),
|
||||
Unknown(String),
|
||||
Empty,
|
||||
}
|
||||
|
||||
|
||||
impl fmt::Display for FieldError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
match *self {
|
||||
FieldError::PopOnEmpty(ref field)
|
||||
=> writeln!(f, "'{}': Cannot move up on empty field cursor", field),
|
||||
FieldError::TrailingFieldSep(ref field)
|
||||
=> writeln!(f, "'{}': Single field separator may not be last character", field),
|
||||
FieldError::Unknown(ref field)
|
||||
=> writeln!(f, "Field '{}' does not exist", field),
|
||||
FieldError::Empty
|
||||
=> writeln!(f, "Field names must not be empty"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum CLIError {
|
||||
Configuration(ConfigurationError),
|
||||
ParseError((&'static str, &'static str, String, String)),
|
||||
UnknownParameter(String),
|
||||
InvalidKeyValueSyntax(String, bool),
|
||||
Input(InputError),
|
||||
Field(FieldError),
|
||||
}
|
||||
|
||||
impl fmt::Display for CLIError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
match *self {
|
||||
CLIError::Configuration(ref err) => write!(f, "Configuration -> {}", err),
|
||||
CLIError::Input(ref err) => write!(f, "Input -> {}", err),
|
||||
CLIError::Field(ref err) => write!(f, "Field -> {}", 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),
|
||||
CLIError::UnknownParameter(ref param_name)
|
||||
=> writeln!(f, "Parameter '{}' is unknown.", param_name),
|
||||
CLIError::InvalidKeyValueSyntax(ref kv, is_hashmap) => {
|
||||
let hashmap_info = if is_hashmap { "hashmap " } else { "" };
|
||||
writeln!(f, "'{}' does not match {}pattern <key>=<value>", kv, hashmap_info)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct InvalidOptionsError {
|
||||
pub issues: Vec<CLIError>,
|
||||
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: CLIError, exit_code: i32) -> InvalidOptionsError {
|
||||
InvalidOptionsError {
|
||||
issues: vec![err],
|
||||
exit_code: exit_code,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new() -> InvalidOptionsError {
|
||||
InvalidOptionsError {
|
||||
issues: Vec::new(),
|
||||
exit_code: 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn assure_config_dir_exists(dir: &str) -> Result<String, CLIError> {
|
||||
let trdir = dir.trim();
|
||||
if trdir.len() == 0 {
|
||||
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(CLIError::Configuration(ConfigurationError::HomeExpansionFailed(trdir.to_string()))),
|
||||
Some(mut user) => {
|
||||
user.push_str(&trdir[1..]);
|
||||
user
|
||||
}
|
||||
}
|
||||
} else {
|
||||
trdir.to_string()
|
||||
};
|
||||
|
||||
if let Err(err) = fs::create_dir(&expanded_config_dir) {
|
||||
if err.kind() != io::ErrorKind::AlreadyExists {
|
||||
return Err(CLIError::Configuration(
|
||||
ConfigurationError::DirectoryCreationFailed((expanded_config_dir, err))))
|
||||
}
|
||||
}
|
||||
|
||||
Ok(expanded_config_dir)
|
||||
}
|
||||
|
||||
pub fn application_secret_from_directory(dir: &str,
|
||||
secret_basename: &str,
|
||||
json_app_secret: &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(CLIError::Configuration(ConfigurationError::IOError(
|
||||
(secret_str(), io_err)
|
||||
)))
|
||||
};
|
||||
|
||||
for _ in 0..2 {
|
||||
match fs::File::open(&secret_path) {
|
||||
Err(mut err) => {
|
||||
if err.kind() == io::ErrorKind::NotFound {
|
||||
// Write our built-in one - user may adjust the written file at will
|
||||
|
||||
err = match fs::OpenOptions::new().create(true).write(true).open(&secret_path) {
|
||||
Err(cfe) => cfe,
|
||||
Ok(mut f) => {
|
||||
match f.write(json_app_secret.as_bytes()) {
|
||||
Err(io_err) => io_err,
|
||||
Ok(_) => continue,
|
||||
}
|
||||
}
|
||||
};
|
||||
// fall through to IO error handling
|
||||
}
|
||||
return secret_io_error(err)
|
||||
},
|
||||
Ok(mut f) => {
|
||||
let mut json_encoded_secret = String::new();
|
||||
if let Err(io_err) = f.read_to_string(&mut json_encoded_secret) {
|
||||
return secret_io_error(io_err)
|
||||
}
|
||||
match json::decode::<ConsoleApplicationSecret>(&json_encoded_secret) {
|
||||
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(
|
||||
CLIError::Configuration(
|
||||
ConfigurationError::Secret(
|
||||
ApplicationSecretError::FormatError(secret_str())
|
||||
)))
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
unreachable!();
|
||||
}
|
||||
1290
gen/reseller1_sandbox-cli/src/main.rs
Normal file
1290
gen/reseller1_sandbox-cli/src/main.rs
Normal file
File diff suppressed because it is too large
Load Diff
31
gen/tagmanager1-cli/Cargo.toml
Normal file
31
gen/tagmanager1-cli/Cargo.toml
Normal file
@@ -0,0 +1,31 @@
|
||||
# DO NOT EDIT !
|
||||
# This file was generated automatically from 'src/mako/Cargo.toml.mako'
|
||||
# DO NOT EDIT !
|
||||
[package]
|
||||
|
||||
name = "google-tagmanager1-cli"
|
||||
version = "0.0.1+20150121"
|
||||
authors = ["Sebastian Thiel <byronimo@gmail.com>"]
|
||||
description = "A complete library to interact with Tag Manager (protocol v1)"
|
||||
repository = "https://github.com/Byron/google-apis-rs/tree/master/gen/tagmanager1-cli"
|
||||
homepage = "https://developers.google.com/tag-manager/api/v1/"
|
||||
documentation = "http://byron.github.io/google-apis-rs/google_tagmanager1_cli"
|
||||
license = "MIT"
|
||||
keywords = ["tagmanager", "google", "cli"]
|
||||
|
||||
[[bin]]
|
||||
name = "tagmanager1"
|
||||
|
||||
[dependencies]
|
||||
hyper = "*"
|
||||
mime = "*"
|
||||
yup-oauth2 = "*"
|
||||
docopt = "*"
|
||||
docopt_macros = "*"
|
||||
rustc-serialize = "*"
|
||||
yup-hyper-mock = "*"
|
||||
serde = ">= 0.3.0"
|
||||
serde_macros = "*"
|
||||
|
||||
[dependencies.google-tagmanager1]
|
||||
path = "../tagmanager1"
|
||||
30
gen/tagmanager1-cli/LICENSE.md
Normal file
30
gen/tagmanager1-cli/LICENSE.md
Normal file
@@ -0,0 +1,30 @@
|
||||
<!---
|
||||
DO NOT EDIT !
|
||||
This file was generated automatically from 'src/mako/LICENSE.md.mako'
|
||||
DO NOT EDIT !
|
||||
-->
|
||||
The MIT License (MIT)
|
||||
=====================
|
||||
|
||||
Copyright © `2015` `Sebastian Thiel`
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the “Software”), to deal in the Software without
|
||||
restriction, including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
4
gen/tagmanager1-cli/README.md
Normal file
4
gen/tagmanager1-cli/README.md
Normal file
@@ -0,0 +1,4 @@
|
||||
# HELLO TAGMANAGER:V1
|
||||
|
||||
|
||||
Include information about application secret files, and how we automatically write a default one.
|
||||
62
gen/tagmanager1-cli/mkdocs.yml
Normal file
62
gen/tagmanager1-cli/mkdocs.yml
Normal file
@@ -0,0 +1,62 @@
|
||||
site_name: Tag Manager v0.0.1+20150121
|
||||
site_url: http://byron.github.io/google-apis-rs/google-tagmanager1-cli
|
||||
site_description: Write integrating applications with bcore
|
||||
|
||||
repo_url: https://github.com/Byron/google-apis-rs/tree/master/gen/tagmanager1-cli
|
||||
|
||||
docs_dir: docs
|
||||
site_dir: build_html
|
||||
|
||||
pages:
|
||||
- ['index.md', 'Home']
|
||||
- ['accounts_containers-create.md', 'Accounts', 'Containers Create']
|
||||
- ['accounts_containers-delete.md', 'Accounts', 'Containers Delete']
|
||||
- ['accounts_containers-get.md', 'Accounts', 'Containers Get']
|
||||
- ['accounts_containers-list.md', 'Accounts', 'Containers List']
|
||||
- ['accounts_containers-macros-create.md', 'Accounts', 'Containers Macros Create']
|
||||
- ['accounts_containers-macros-delete.md', 'Accounts', 'Containers Macros Delete']
|
||||
- ['accounts_containers-macros-get.md', 'Accounts', 'Containers Macros Get']
|
||||
- ['accounts_containers-macros-list.md', 'Accounts', 'Containers Macros List']
|
||||
- ['accounts_containers-macros-update.md', 'Accounts', 'Containers Macros Update']
|
||||
- ['accounts_containers-rules-create.md', 'Accounts', 'Containers Rules Create']
|
||||
- ['accounts_containers-rules-delete.md', 'Accounts', 'Containers Rules Delete']
|
||||
- ['accounts_containers-rules-get.md', 'Accounts', 'Containers Rules Get']
|
||||
- ['accounts_containers-rules-list.md', 'Accounts', 'Containers Rules List']
|
||||
- ['accounts_containers-rules-update.md', 'Accounts', 'Containers Rules Update']
|
||||
- ['accounts_containers-tags-create.md', 'Accounts', 'Containers Tags Create']
|
||||
- ['accounts_containers-tags-delete.md', 'Accounts', 'Containers Tags Delete']
|
||||
- ['accounts_containers-tags-get.md', 'Accounts', 'Containers Tags Get']
|
||||
- ['accounts_containers-tags-list.md', 'Accounts', 'Containers Tags List']
|
||||
- ['accounts_containers-tags-update.md', 'Accounts', 'Containers Tags Update']
|
||||
- ['accounts_containers-triggers-create.md', 'Accounts', 'Containers Triggers Create']
|
||||
- ['accounts_containers-triggers-delete.md', 'Accounts', 'Containers Triggers Delete']
|
||||
- ['accounts_containers-triggers-get.md', 'Accounts', 'Containers Triggers Get']
|
||||
- ['accounts_containers-triggers-list.md', 'Accounts', 'Containers Triggers List']
|
||||
- ['accounts_containers-triggers-update.md', 'Accounts', 'Containers Triggers Update']
|
||||
- ['accounts_containers-update.md', 'Accounts', 'Containers Update']
|
||||
- ['accounts_containers-variables-create.md', 'Accounts', 'Containers Variables Create']
|
||||
- ['accounts_containers-variables-delete.md', 'Accounts', 'Containers Variables Delete']
|
||||
- ['accounts_containers-variables-get.md', 'Accounts', 'Containers Variables Get']
|
||||
- ['accounts_containers-variables-list.md', 'Accounts', 'Containers Variables List']
|
||||
- ['accounts_containers-variables-update.md', 'Accounts', 'Containers Variables Update']
|
||||
- ['accounts_containers-versions-create.md', 'Accounts', 'Containers Versions Create']
|
||||
- ['accounts_containers-versions-delete.md', 'Accounts', 'Containers Versions Delete']
|
||||
- ['accounts_containers-versions-get.md', 'Accounts', 'Containers Versions Get']
|
||||
- ['accounts_containers-versions-list.md', 'Accounts', 'Containers Versions List']
|
||||
- ['accounts_containers-versions-publish.md', 'Accounts', 'Containers Versions Publish']
|
||||
- ['accounts_containers-versions-restore.md', 'Accounts', 'Containers Versions Restore']
|
||||
- ['accounts_containers-versions-undelete.md', 'Accounts', 'Containers Versions Undelete']
|
||||
- ['accounts_containers-versions-update.md', 'Accounts', 'Containers Versions Update']
|
||||
- ['accounts_get.md', 'Accounts', 'Get']
|
||||
- ['accounts_list.md', 'Accounts', 'List']
|
||||
- ['accounts_permissions-create.md', 'Accounts', 'Permissions Create']
|
||||
- ['accounts_permissions-delete.md', 'Accounts', 'Permissions Delete']
|
||||
- ['accounts_permissions-get.md', 'Accounts', 'Permissions Get']
|
||||
- ['accounts_permissions-list.md', 'Accounts', 'Permissions List']
|
||||
- ['accounts_permissions-update.md', 'Accounts', 'Permissions Update']
|
||||
- ['accounts_update.md', 'Accounts', 'Update']
|
||||
|
||||
theme: readthedocs
|
||||
|
||||
copyright: Copyright © 2015, `Sebastian Thiel`
|
||||
|
||||
454
gen/tagmanager1-cli/src/cmn.rs
Normal file
454
gen/tagmanager1-cli/src/cmn.rs
Normal file
@@ -0,0 +1,454 @@
|
||||
// COPY OF 'src/rust/cli/cmn.rs'
|
||||
// DO NOT EDIT
|
||||
use oauth2::{ApplicationSecret, ConsoleApplicationSecret, TokenStorage, Token};
|
||||
use rustc_serialize::json;
|
||||
use mime::Mime;
|
||||
|
||||
use std::fs;
|
||||
use std::env;
|
||||
use std::io;
|
||||
use std::fmt;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::str::FromStr;
|
||||
use std::string::ToString;
|
||||
use std::io::{Write, Read, stdout};
|
||||
|
||||
use std::default::Default;
|
||||
|
||||
const FIELD_SEP: char = '.';
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct FieldCursor(Vec<String>);
|
||||
|
||||
impl ToString for FieldCursor {
|
||||
fn to_string(&self) -> String {
|
||||
self.0.connect(".")
|
||||
}
|
||||
}
|
||||
|
||||
impl FieldCursor {
|
||||
pub fn set(&mut self, value: &str) -> Result<(), CLIError> {
|
||||
if value.len() == 0 {
|
||||
return Err(CLIError::Field(FieldError::Empty))
|
||||
}
|
||||
|
||||
let mut first_is_field_sep = false;
|
||||
let mut char_count: usize = 0;
|
||||
let mut last_c = FIELD_SEP;
|
||||
let mut num_conscutive_field_seps = 0;
|
||||
|
||||
let mut field = String::new();
|
||||
let mut fields = self.0.clone();
|
||||
|
||||
let push_field = |fs: &mut Vec<String>, f: &mut String| {
|
||||
if f.len() > 0 {
|
||||
fs.push(f.clone());
|
||||
f.truncate(0);
|
||||
}
|
||||
};
|
||||
|
||||
for (cid, c) in value.chars().enumerate() {
|
||||
char_count += 1;
|
||||
|
||||
if c == FIELD_SEP {
|
||||
if cid == 0 {
|
||||
first_is_field_sep = true;
|
||||
}
|
||||
num_conscutive_field_seps += 1;
|
||||
if cid > 0 && last_c == FIELD_SEP {
|
||||
if fields.pop().is_none() {
|
||||
return Err(CLIError::Field(FieldError::PopOnEmpty(value.to_string())))
|
||||
}
|
||||
} else {
|
||||
push_field(&mut fields, &mut field);
|
||||
}
|
||||
} else {
|
||||
num_conscutive_field_seps = 0;
|
||||
if cid == 1 {
|
||||
if first_is_field_sep {
|
||||
fields.truncate(0);
|
||||
}
|
||||
}
|
||||
field.push(c);
|
||||
}
|
||||
|
||||
last_c = c;
|
||||
}
|
||||
|
||||
push_field(&mut fields, &mut field);
|
||||
|
||||
if char_count == 1 && first_is_field_sep {
|
||||
fields.truncate(0);
|
||||
}
|
||||
if char_count > 1 && num_conscutive_field_seps == 1 {
|
||||
return Err(CLIError::Field(FieldError::TrailingFieldSep(value.to_string())))
|
||||
}
|
||||
|
||||
self.0 = fields;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn num_fields(&self) -> usize {
|
||||
self.0.len()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_kv_arg<'a>(kv: &'a str, err: &mut InvalidOptionsError, for_hashmap: bool)
|
||||
-> (&'a str, Option<&'a str>) {
|
||||
let mut add_err = || err.issues.push(CLIError::InvalidKeyValueSyntax(kv.to_string(),for_hashmap));
|
||||
match kv.find('=') {
|
||||
None => {
|
||||
add_err();
|
||||
return (kv, None)
|
||||
},
|
||||
Some(pos) => {
|
||||
let key = &kv[..pos];
|
||||
if kv.len() <= pos + 1 {
|
||||
add_err();
|
||||
return (key, None)
|
||||
}
|
||||
(key, Some(&kv[pos+1..]))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn input_file_from_opts(file_path: &str, err: &mut InvalidOptionsError) -> Option<fs::File> {
|
||||
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<Mime> {
|
||||
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<Write> {
|
||||
if !flag || arg == "-" {
|
||||
Box::new(stdout())
|
||||
} else {
|
||||
Box::new(fs::OpenOptions::new().create(true).write(true).open(arg).unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn arg_from_str<T>(arg: &str, err: &mut InvalidOptionsError,
|
||||
arg_name: &'static str,
|
||||
arg_type: &'static str) -> T
|
||||
where T: FromStr + Default,
|
||||
<T as FromStr>::Err: fmt::Display {
|
||||
match FromStr::from_str(arg) {
|
||||
Err(perr) => {
|
||||
err.issues.push(
|
||||
CLIError::ParseError((arg_name, arg_type, arg.to_string(), format!("{}", perr)))
|
||||
);
|
||||
Default::default()
|
||||
},
|
||||
Ok(v) => v,
|
||||
}
|
||||
}
|
||||
|
||||
pub struct JsonTokenStorage {
|
||||
pub program_name: &'static str,
|
||||
pub db_dir: String,
|
||||
}
|
||||
|
||||
impl JsonTokenStorage {
|
||||
fn path(&self, scope_hash: u64) -> PathBuf {
|
||||
Path::new(&self.db_dir).join(&format!("{}-token-{}.json", self.program_name, scope_hash))
|
||||
}
|
||||
}
|
||||
|
||||
impl TokenStorage for JsonTokenStorage {
|
||||
type Error = io::Error;
|
||||
|
||||
// NOTE: logging might be interesting, currently we swallow all errors
|
||||
fn set(&mut self, scope_hash: u64, _: &Vec<&str>, token: Option<Token>) -> Option<io::Error> {
|
||||
match token {
|
||||
None => {
|
||||
match fs::remove_file(self.path(scope_hash)) {
|
||||
Err(err) =>
|
||||
match err.kind() {
|
||||
io::ErrorKind::NotFound => None,
|
||||
_ => Some(err)
|
||||
},
|
||||
Ok(_) => None
|
||||
}
|
||||
}
|
||||
Some(token) => {
|
||||
let json_token = json::encode(&token).unwrap();
|
||||
match fs::OpenOptions::new().create(true).write(true).open(&self.path(scope_hash)) {
|
||||
Ok(mut f) => {
|
||||
match f.write(json_token.as_bytes()) {
|
||||
Ok(_) => None,
|
||||
Err(io_err) => Some(io_err),
|
||||
}
|
||||
},
|
||||
Err(io_err) => Some(io_err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get(&self, scope_hash: u64, _: &Vec<&str>) -> Result<Option<Token>, io::Error> {
|
||||
match fs::File::open(&self.path(scope_hash)) {
|
||||
Ok(mut f) => {
|
||||
let mut json_string = String::new();
|
||||
match f.read_to_string(&mut json_string) {
|
||||
Ok(_) => Ok(Some(json::decode::<Token>(&json_string).unwrap())),
|
||||
Err(io_err) => Err(io_err),
|
||||
}
|
||||
},
|
||||
Err(io_err) => {
|
||||
match io_err.kind() {
|
||||
io::ErrorKind::NotFound => Ok(None),
|
||||
_ => Err(io_err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug)]
|
||||
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)),
|
||||
}
|
||||
|
||||
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 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 FieldError {
|
||||
PopOnEmpty(String),
|
||||
TrailingFieldSep(String),
|
||||
Unknown(String),
|
||||
Empty,
|
||||
}
|
||||
|
||||
|
||||
impl fmt::Display for FieldError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
match *self {
|
||||
FieldError::PopOnEmpty(ref field)
|
||||
=> writeln!(f, "'{}': Cannot move up on empty field cursor", field),
|
||||
FieldError::TrailingFieldSep(ref field)
|
||||
=> writeln!(f, "'{}': Single field separator may not be last character", field),
|
||||
FieldError::Unknown(ref field)
|
||||
=> writeln!(f, "Field '{}' does not exist", field),
|
||||
FieldError::Empty
|
||||
=> writeln!(f, "Field names must not be empty"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum CLIError {
|
||||
Configuration(ConfigurationError),
|
||||
ParseError((&'static str, &'static str, String, String)),
|
||||
UnknownParameter(String),
|
||||
InvalidKeyValueSyntax(String, bool),
|
||||
Input(InputError),
|
||||
Field(FieldError),
|
||||
}
|
||||
|
||||
impl fmt::Display for CLIError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
match *self {
|
||||
CLIError::Configuration(ref err) => write!(f, "Configuration -> {}", err),
|
||||
CLIError::Input(ref err) => write!(f, "Input -> {}", err),
|
||||
CLIError::Field(ref err) => write!(f, "Field -> {}", 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),
|
||||
CLIError::UnknownParameter(ref param_name)
|
||||
=> writeln!(f, "Parameter '{}' is unknown.", param_name),
|
||||
CLIError::InvalidKeyValueSyntax(ref kv, is_hashmap) => {
|
||||
let hashmap_info = if is_hashmap { "hashmap " } else { "" };
|
||||
writeln!(f, "'{}' does not match {}pattern <key>=<value>", kv, hashmap_info)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct InvalidOptionsError {
|
||||
pub issues: Vec<CLIError>,
|
||||
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: CLIError, exit_code: i32) -> InvalidOptionsError {
|
||||
InvalidOptionsError {
|
||||
issues: vec![err],
|
||||
exit_code: exit_code,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new() -> InvalidOptionsError {
|
||||
InvalidOptionsError {
|
||||
issues: Vec::new(),
|
||||
exit_code: 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn assure_config_dir_exists(dir: &str) -> Result<String, CLIError> {
|
||||
let trdir = dir.trim();
|
||||
if trdir.len() == 0 {
|
||||
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(CLIError::Configuration(ConfigurationError::HomeExpansionFailed(trdir.to_string()))),
|
||||
Some(mut user) => {
|
||||
user.push_str(&trdir[1..]);
|
||||
user
|
||||
}
|
||||
}
|
||||
} else {
|
||||
trdir.to_string()
|
||||
};
|
||||
|
||||
if let Err(err) = fs::create_dir(&expanded_config_dir) {
|
||||
if err.kind() != io::ErrorKind::AlreadyExists {
|
||||
return Err(CLIError::Configuration(
|
||||
ConfigurationError::DirectoryCreationFailed((expanded_config_dir, err))))
|
||||
}
|
||||
}
|
||||
|
||||
Ok(expanded_config_dir)
|
||||
}
|
||||
|
||||
pub fn application_secret_from_directory(dir: &str,
|
||||
secret_basename: &str,
|
||||
json_app_secret: &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(CLIError::Configuration(ConfigurationError::IOError(
|
||||
(secret_str(), io_err)
|
||||
)))
|
||||
};
|
||||
|
||||
for _ in 0..2 {
|
||||
match fs::File::open(&secret_path) {
|
||||
Err(mut err) => {
|
||||
if err.kind() == io::ErrorKind::NotFound {
|
||||
// Write our built-in one - user may adjust the written file at will
|
||||
|
||||
err = match fs::OpenOptions::new().create(true).write(true).open(&secret_path) {
|
||||
Err(cfe) => cfe,
|
||||
Ok(mut f) => {
|
||||
match f.write(json_app_secret.as_bytes()) {
|
||||
Err(io_err) => io_err,
|
||||
Ok(_) => continue,
|
||||
}
|
||||
}
|
||||
};
|
||||
// fall through to IO error handling
|
||||
}
|
||||
return secret_io_error(err)
|
||||
},
|
||||
Ok(mut f) => {
|
||||
let mut json_encoded_secret = String::new();
|
||||
if let Err(io_err) = f.read_to_string(&mut json_encoded_secret) {
|
||||
return secret_io_error(io_err)
|
||||
}
|
||||
match json::decode::<ConsoleApplicationSecret>(&json_encoded_secret) {
|
||||
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(
|
||||
CLIError::Configuration(
|
||||
ConfigurationError::Secret(
|
||||
ApplicationSecretError::FormatError(secret_str())
|
||||
)))
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
unreachable!();
|
||||
}
|
||||
3484
gen/tagmanager1-cli/src/main.rs
Normal file
3484
gen/tagmanager1-cli/src/main.rs
Normal file
File diff suppressed because it is too large
Load Diff
31
gen/taskqueue1_beta2-cli/Cargo.toml
Normal file
31
gen/taskqueue1_beta2-cli/Cargo.toml
Normal file
@@ -0,0 +1,31 @@
|
||||
# DO NOT EDIT !
|
||||
# This file was generated automatically from 'src/mako/Cargo.toml.mako'
|
||||
# DO NOT EDIT !
|
||||
[package]
|
||||
|
||||
name = "google-taskqueue1_beta2-cli"
|
||||
version = "0.0.1+20141111"
|
||||
authors = ["Sebastian Thiel <byronimo@gmail.com>"]
|
||||
description = "A complete library to interact with taskqueue (protocol v1beta2)"
|
||||
repository = "https://github.com/Byron/google-apis-rs/tree/master/gen/taskqueue1_beta2-cli"
|
||||
homepage = "https://developers.google.com/appengine/docs/python/taskqueue/rest"
|
||||
documentation = "http://byron.github.io/google-apis-rs/google_taskqueue1_beta2_cli"
|
||||
license = "MIT"
|
||||
keywords = ["taskqueue", "google", "cli"]
|
||||
|
||||
[[bin]]
|
||||
name = "taskqueue1-beta2"
|
||||
|
||||
[dependencies]
|
||||
hyper = "*"
|
||||
mime = "*"
|
||||
yup-oauth2 = "*"
|
||||
docopt = "*"
|
||||
docopt_macros = "*"
|
||||
rustc-serialize = "*"
|
||||
yup-hyper-mock = "*"
|
||||
serde = ">= 0.3.0"
|
||||
serde_macros = "*"
|
||||
|
||||
[dependencies.google-taskqueue1_beta2]
|
||||
path = "../taskqueue1_beta2"
|
||||
30
gen/taskqueue1_beta2-cli/LICENSE.md
Normal file
30
gen/taskqueue1_beta2-cli/LICENSE.md
Normal file
@@ -0,0 +1,30 @@
|
||||
<!---
|
||||
DO NOT EDIT !
|
||||
This file was generated automatically from 'src/mako/LICENSE.md.mako'
|
||||
DO NOT EDIT !
|
||||
-->
|
||||
The MIT License (MIT)
|
||||
=====================
|
||||
|
||||
Copyright © `2015` `Sebastian Thiel`
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the “Software”), to deal in the Software without
|
||||
restriction, including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
4
gen/taskqueue1_beta2-cli/README.md
Normal file
4
gen/taskqueue1_beta2-cli/README.md
Normal file
@@ -0,0 +1,4 @@
|
||||
# HELLO TASKQUEUE:V1BETA2
|
||||
|
||||
|
||||
Include information about application secret files, and how we automatically write a default one.
|
||||
24
gen/taskqueue1_beta2-cli/mkdocs.yml
Normal file
24
gen/taskqueue1_beta2-cli/mkdocs.yml
Normal file
@@ -0,0 +1,24 @@
|
||||
site_name: taskqueue v0.0.1+20141111
|
||||
site_url: http://byron.github.io/google-apis-rs/google-taskqueue1_beta2-cli
|
||||
site_description: Write integrating applications with bcore
|
||||
|
||||
repo_url: https://github.com/Byron/google-apis-rs/tree/master/gen/taskqueue1_beta2-cli
|
||||
|
||||
docs_dir: docs
|
||||
site_dir: build_html
|
||||
|
||||
pages:
|
||||
- ['index.md', 'Home']
|
||||
- ['taskqueues_get.md', 'Taskqueues', 'Get']
|
||||
- ['tasks_delete.md', 'Tasks', 'Delete']
|
||||
- ['tasks_get.md', 'Tasks', 'Get']
|
||||
- ['tasks_insert.md', 'Tasks', 'Insert']
|
||||
- ['tasks_lease.md', 'Tasks', 'Lease']
|
||||
- ['tasks_list.md', 'Tasks', 'List']
|
||||
- ['tasks_patch.md', 'Tasks', 'Patch']
|
||||
- ['tasks_update.md', 'Tasks', 'Update']
|
||||
|
||||
theme: readthedocs
|
||||
|
||||
copyright: Copyright © 2015, `Sebastian Thiel`
|
||||
|
||||
454
gen/taskqueue1_beta2-cli/src/cmn.rs
Normal file
454
gen/taskqueue1_beta2-cli/src/cmn.rs
Normal file
@@ -0,0 +1,454 @@
|
||||
// COPY OF 'src/rust/cli/cmn.rs'
|
||||
// DO NOT EDIT
|
||||
use oauth2::{ApplicationSecret, ConsoleApplicationSecret, TokenStorage, Token};
|
||||
use rustc_serialize::json;
|
||||
use mime::Mime;
|
||||
|
||||
use std::fs;
|
||||
use std::env;
|
||||
use std::io;
|
||||
use std::fmt;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::str::FromStr;
|
||||
use std::string::ToString;
|
||||
use std::io::{Write, Read, stdout};
|
||||
|
||||
use std::default::Default;
|
||||
|
||||
const FIELD_SEP: char = '.';
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct FieldCursor(Vec<String>);
|
||||
|
||||
impl ToString for FieldCursor {
|
||||
fn to_string(&self) -> String {
|
||||
self.0.connect(".")
|
||||
}
|
||||
}
|
||||
|
||||
impl FieldCursor {
|
||||
pub fn set(&mut self, value: &str) -> Result<(), CLIError> {
|
||||
if value.len() == 0 {
|
||||
return Err(CLIError::Field(FieldError::Empty))
|
||||
}
|
||||
|
||||
let mut first_is_field_sep = false;
|
||||
let mut char_count: usize = 0;
|
||||
let mut last_c = FIELD_SEP;
|
||||
let mut num_conscutive_field_seps = 0;
|
||||
|
||||
let mut field = String::new();
|
||||
let mut fields = self.0.clone();
|
||||
|
||||
let push_field = |fs: &mut Vec<String>, f: &mut String| {
|
||||
if f.len() > 0 {
|
||||
fs.push(f.clone());
|
||||
f.truncate(0);
|
||||
}
|
||||
};
|
||||
|
||||
for (cid, c) in value.chars().enumerate() {
|
||||
char_count += 1;
|
||||
|
||||
if c == FIELD_SEP {
|
||||
if cid == 0 {
|
||||
first_is_field_sep = true;
|
||||
}
|
||||
num_conscutive_field_seps += 1;
|
||||
if cid > 0 && last_c == FIELD_SEP {
|
||||
if fields.pop().is_none() {
|
||||
return Err(CLIError::Field(FieldError::PopOnEmpty(value.to_string())))
|
||||
}
|
||||
} else {
|
||||
push_field(&mut fields, &mut field);
|
||||
}
|
||||
} else {
|
||||
num_conscutive_field_seps = 0;
|
||||
if cid == 1 {
|
||||
if first_is_field_sep {
|
||||
fields.truncate(0);
|
||||
}
|
||||
}
|
||||
field.push(c);
|
||||
}
|
||||
|
||||
last_c = c;
|
||||
}
|
||||
|
||||
push_field(&mut fields, &mut field);
|
||||
|
||||
if char_count == 1 && first_is_field_sep {
|
||||
fields.truncate(0);
|
||||
}
|
||||
if char_count > 1 && num_conscutive_field_seps == 1 {
|
||||
return Err(CLIError::Field(FieldError::TrailingFieldSep(value.to_string())))
|
||||
}
|
||||
|
||||
self.0 = fields;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn num_fields(&self) -> usize {
|
||||
self.0.len()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_kv_arg<'a>(kv: &'a str, err: &mut InvalidOptionsError, for_hashmap: bool)
|
||||
-> (&'a str, Option<&'a str>) {
|
||||
let mut add_err = || err.issues.push(CLIError::InvalidKeyValueSyntax(kv.to_string(),for_hashmap));
|
||||
match kv.find('=') {
|
||||
None => {
|
||||
add_err();
|
||||
return (kv, None)
|
||||
},
|
||||
Some(pos) => {
|
||||
let key = &kv[..pos];
|
||||
if kv.len() <= pos + 1 {
|
||||
add_err();
|
||||
return (key, None)
|
||||
}
|
||||
(key, Some(&kv[pos+1..]))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn input_file_from_opts(file_path: &str, err: &mut InvalidOptionsError) -> Option<fs::File> {
|
||||
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<Mime> {
|
||||
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<Write> {
|
||||
if !flag || arg == "-" {
|
||||
Box::new(stdout())
|
||||
} else {
|
||||
Box::new(fs::OpenOptions::new().create(true).write(true).open(arg).unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn arg_from_str<T>(arg: &str, err: &mut InvalidOptionsError,
|
||||
arg_name: &'static str,
|
||||
arg_type: &'static str) -> T
|
||||
where T: FromStr + Default,
|
||||
<T as FromStr>::Err: fmt::Display {
|
||||
match FromStr::from_str(arg) {
|
||||
Err(perr) => {
|
||||
err.issues.push(
|
||||
CLIError::ParseError((arg_name, arg_type, arg.to_string(), format!("{}", perr)))
|
||||
);
|
||||
Default::default()
|
||||
},
|
||||
Ok(v) => v,
|
||||
}
|
||||
}
|
||||
|
||||
pub struct JsonTokenStorage {
|
||||
pub program_name: &'static str,
|
||||
pub db_dir: String,
|
||||
}
|
||||
|
||||
impl JsonTokenStorage {
|
||||
fn path(&self, scope_hash: u64) -> PathBuf {
|
||||
Path::new(&self.db_dir).join(&format!("{}-token-{}.json", self.program_name, scope_hash))
|
||||
}
|
||||
}
|
||||
|
||||
impl TokenStorage for JsonTokenStorage {
|
||||
type Error = io::Error;
|
||||
|
||||
// NOTE: logging might be interesting, currently we swallow all errors
|
||||
fn set(&mut self, scope_hash: u64, _: &Vec<&str>, token: Option<Token>) -> Option<io::Error> {
|
||||
match token {
|
||||
None => {
|
||||
match fs::remove_file(self.path(scope_hash)) {
|
||||
Err(err) =>
|
||||
match err.kind() {
|
||||
io::ErrorKind::NotFound => None,
|
||||
_ => Some(err)
|
||||
},
|
||||
Ok(_) => None
|
||||
}
|
||||
}
|
||||
Some(token) => {
|
||||
let json_token = json::encode(&token).unwrap();
|
||||
match fs::OpenOptions::new().create(true).write(true).open(&self.path(scope_hash)) {
|
||||
Ok(mut f) => {
|
||||
match f.write(json_token.as_bytes()) {
|
||||
Ok(_) => None,
|
||||
Err(io_err) => Some(io_err),
|
||||
}
|
||||
},
|
||||
Err(io_err) => Some(io_err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get(&self, scope_hash: u64, _: &Vec<&str>) -> Result<Option<Token>, io::Error> {
|
||||
match fs::File::open(&self.path(scope_hash)) {
|
||||
Ok(mut f) => {
|
||||
let mut json_string = String::new();
|
||||
match f.read_to_string(&mut json_string) {
|
||||
Ok(_) => Ok(Some(json::decode::<Token>(&json_string).unwrap())),
|
||||
Err(io_err) => Err(io_err),
|
||||
}
|
||||
},
|
||||
Err(io_err) => {
|
||||
match io_err.kind() {
|
||||
io::ErrorKind::NotFound => Ok(None),
|
||||
_ => Err(io_err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug)]
|
||||
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)),
|
||||
}
|
||||
|
||||
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 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 FieldError {
|
||||
PopOnEmpty(String),
|
||||
TrailingFieldSep(String),
|
||||
Unknown(String),
|
||||
Empty,
|
||||
}
|
||||
|
||||
|
||||
impl fmt::Display for FieldError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
match *self {
|
||||
FieldError::PopOnEmpty(ref field)
|
||||
=> writeln!(f, "'{}': Cannot move up on empty field cursor", field),
|
||||
FieldError::TrailingFieldSep(ref field)
|
||||
=> writeln!(f, "'{}': Single field separator may not be last character", field),
|
||||
FieldError::Unknown(ref field)
|
||||
=> writeln!(f, "Field '{}' does not exist", field),
|
||||
FieldError::Empty
|
||||
=> writeln!(f, "Field names must not be empty"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum CLIError {
|
||||
Configuration(ConfigurationError),
|
||||
ParseError((&'static str, &'static str, String, String)),
|
||||
UnknownParameter(String),
|
||||
InvalidKeyValueSyntax(String, bool),
|
||||
Input(InputError),
|
||||
Field(FieldError),
|
||||
}
|
||||
|
||||
impl fmt::Display for CLIError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
match *self {
|
||||
CLIError::Configuration(ref err) => write!(f, "Configuration -> {}", err),
|
||||
CLIError::Input(ref err) => write!(f, "Input -> {}", err),
|
||||
CLIError::Field(ref err) => write!(f, "Field -> {}", 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),
|
||||
CLIError::UnknownParameter(ref param_name)
|
||||
=> writeln!(f, "Parameter '{}' is unknown.", param_name),
|
||||
CLIError::InvalidKeyValueSyntax(ref kv, is_hashmap) => {
|
||||
let hashmap_info = if is_hashmap { "hashmap " } else { "" };
|
||||
writeln!(f, "'{}' does not match {}pattern <key>=<value>", kv, hashmap_info)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct InvalidOptionsError {
|
||||
pub issues: Vec<CLIError>,
|
||||
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: CLIError, exit_code: i32) -> InvalidOptionsError {
|
||||
InvalidOptionsError {
|
||||
issues: vec![err],
|
||||
exit_code: exit_code,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new() -> InvalidOptionsError {
|
||||
InvalidOptionsError {
|
||||
issues: Vec::new(),
|
||||
exit_code: 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn assure_config_dir_exists(dir: &str) -> Result<String, CLIError> {
|
||||
let trdir = dir.trim();
|
||||
if trdir.len() == 0 {
|
||||
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(CLIError::Configuration(ConfigurationError::HomeExpansionFailed(trdir.to_string()))),
|
||||
Some(mut user) => {
|
||||
user.push_str(&trdir[1..]);
|
||||
user
|
||||
}
|
||||
}
|
||||
} else {
|
||||
trdir.to_string()
|
||||
};
|
||||
|
||||
if let Err(err) = fs::create_dir(&expanded_config_dir) {
|
||||
if err.kind() != io::ErrorKind::AlreadyExists {
|
||||
return Err(CLIError::Configuration(
|
||||
ConfigurationError::DirectoryCreationFailed((expanded_config_dir, err))))
|
||||
}
|
||||
}
|
||||
|
||||
Ok(expanded_config_dir)
|
||||
}
|
||||
|
||||
pub fn application_secret_from_directory(dir: &str,
|
||||
secret_basename: &str,
|
||||
json_app_secret: &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(CLIError::Configuration(ConfigurationError::IOError(
|
||||
(secret_str(), io_err)
|
||||
)))
|
||||
};
|
||||
|
||||
for _ in 0..2 {
|
||||
match fs::File::open(&secret_path) {
|
||||
Err(mut err) => {
|
||||
if err.kind() == io::ErrorKind::NotFound {
|
||||
// Write our built-in one - user may adjust the written file at will
|
||||
|
||||
err = match fs::OpenOptions::new().create(true).write(true).open(&secret_path) {
|
||||
Err(cfe) => cfe,
|
||||
Ok(mut f) => {
|
||||
match f.write(json_app_secret.as_bytes()) {
|
||||
Err(io_err) => io_err,
|
||||
Ok(_) => continue,
|
||||
}
|
||||
}
|
||||
};
|
||||
// fall through to IO error handling
|
||||
}
|
||||
return secret_io_error(err)
|
||||
},
|
||||
Ok(mut f) => {
|
||||
let mut json_encoded_secret = String::new();
|
||||
if let Err(io_err) = f.read_to_string(&mut json_encoded_secret) {
|
||||
return secret_io_error(io_err)
|
||||
}
|
||||
match json::decode::<ConsoleApplicationSecret>(&json_encoded_secret) {
|
||||
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(
|
||||
CLIError::Configuration(
|
||||
ConfigurationError::Secret(
|
||||
ApplicationSecretError::FormatError(secret_str())
|
||||
)))
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
unreachable!();
|
||||
}
|
||||
648
gen/taskqueue1_beta2-cli/src/main.rs
Normal file
648
gen/taskqueue1_beta2-cli/src/main.rs
Normal file
@@ -0,0 +1,648 @@
|
||||
// DO NOT EDIT !
|
||||
// This file was generated automatically from 'src/mako/cli/main.rs.mako'
|
||||
// DO NOT EDIT !
|
||||
#![feature(plugin, exit_status)]
|
||||
#![plugin(docopt_macros)]
|
||||
#![allow(unused_variables, unused_imports, dead_code, unused_mut)]
|
||||
|
||||
extern crate docopt;
|
||||
extern crate yup_oauth2 as oauth2;
|
||||
extern crate yup_hyper_mock as mock;
|
||||
extern crate rustc_serialize;
|
||||
extern crate serde;
|
||||
extern crate hyper;
|
||||
extern crate mime;
|
||||
extern crate google_taskqueue1_beta2 as api;
|
||||
|
||||
use std::env;
|
||||
use std::io::{self, Write};
|
||||
|
||||
docopt!(Options derive Debug, "
|
||||
Usage:
|
||||
taskqueue1-beta2 [options] taskqueues get <project> <taskqueue> [-p <v>]... [-o <out>]
|
||||
taskqueue1-beta2 [options] tasks delete <project> <taskqueue> <task> [-p <v>]...
|
||||
taskqueue1-beta2 [options] tasks get <project> <taskqueue> <task> [-p <v>]... [-o <out>]
|
||||
taskqueue1-beta2 [options] tasks insert <project> <taskqueue> -r <kv>... [-p <v>]... [-o <out>]
|
||||
taskqueue1-beta2 [options] tasks lease <project> <taskqueue> <num-tasks> <lease-secs> [-p <v>]... [-o <out>]
|
||||
taskqueue1-beta2 [options] tasks list <project> <taskqueue> [-p <v>]... [-o <out>]
|
||||
taskqueue1-beta2 [options] tasks patch <project> <taskqueue> <task> <new-lease-seconds> -r <kv>... [-p <v>]... [-o <out>]
|
||||
taskqueue1-beta2 [options] tasks update <project> <taskqueue> <task> <new-lease-seconds> -r <kv>... [-p <v>]... [-o <out>]
|
||||
taskqueue1-beta2 --help
|
||||
|
||||
All documentation details can be found TODO: <URL to github.io docs here, see #51>
|
||||
|
||||
Configuration:
|
||||
--scope <url>
|
||||
Specify the authentication a method should be executed in. Each scope requires
|
||||
the user to grant this application permission to use it.
|
||||
If unset, it defaults to the shortest scope url for a particular method.
|
||||
--config-dir <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: ~/.google-service-cli]
|
||||
--debug
|
||||
Output all server communication to standard error. `tx` and `rx` are placed into
|
||||
the same stream.
|
||||
--debug-auth
|
||||
Output all communication related to authentication to standard error. `tx` and `rx` are placed into
|
||||
the same stream.
|
||||
");
|
||||
|
||||
mod cmn;
|
||||
use cmn::{InvalidOptionsError, CLIError, JsonTokenStorage, arg_from_str, writer_from_opts, parse_kv_arg,
|
||||
input_file_from_opts, input_mime_from_opts, FieldCursor, FieldError};
|
||||
|
||||
use std::default::Default;
|
||||
use std::str::FromStr;
|
||||
|
||||
use oauth2::{Authenticator, DefaultAuthenticatorDelegate};
|
||||
use rustc_serialize::json;
|
||||
|
||||
struct Engine {
|
||||
opt: Options,
|
||||
hub: api::Taskqueue<hyper::Client, Authenticator<DefaultAuthenticatorDelegate, JsonTokenStorage, hyper::Client>>,
|
||||
}
|
||||
|
||||
|
||||
impl Engine {
|
||||
fn _taskqueues_get(&self, dry_run: bool, err: &mut InvalidOptionsError)
|
||||
-> Option<api::Error> {
|
||||
let mut call = self.hub.taskqueues().get(&self.opt.arg_project, &self.opt.arg_taskqueue);
|
||||
for parg in self.opt.arg_v.iter() {
|
||||
let (key, value) = parse_kv_arg(&*parg, err, false);
|
||||
match key {
|
||||
"get-stats" => {
|
||||
call = call.get_stats(arg_from_str(value.unwrap_or("false"), err, "get-stats", "boolean"));
|
||||
},
|
||||
"alt"
|
||||
|"fields"
|
||||
|"key"
|
||||
|"oauth-token"
|
||||
|"pretty-print"
|
||||
|"quota-user"
|
||||
|"user-ip" => {
|
||||
let map = [
|
||||
("oauth-token", "oauth_token"),
|
||||
("pretty-print", "prettyPrint"),
|
||||
("quota-user", "quotaUser"),
|
||||
("user-ip", "userIp"),
|
||||
];
|
||||
call = call.param(map.iter().find(|t| t.0 == key).unwrap_or(&("", key)).1, value.unwrap_or("unset"))
|
||||
},
|
||||
_ => err.issues.push(CLIError::UnknownParameter(key.to_string())),
|
||||
}
|
||||
}
|
||||
let protocol = "standard-request";
|
||||
if dry_run {
|
||||
None
|
||||
} else {
|
||||
assert!(err.issues.len() == 0);
|
||||
let mut ostream = writer_from_opts(self.opt.flag_o, &self.opt.arg_out);
|
||||
match match protocol {
|
||||
"standard-request" => call.doit(),
|
||||
_ => unreachable!(),
|
||||
} {
|
||||
Err(api_err) => Some(api_err),
|
||||
Ok((mut response, output_schema)) => {
|
||||
serde::json::to_writer_pretty(&mut ostream, &output_schema).unwrap();
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn _tasks_delete(&self, dry_run: bool, err: &mut InvalidOptionsError)
|
||||
-> Option<api::Error> {
|
||||
let mut call = self.hub.tasks().delete(&self.opt.arg_project, &self.opt.arg_taskqueue, &self.opt.arg_task);
|
||||
for parg in self.opt.arg_v.iter() {
|
||||
let (key, value) = parse_kv_arg(&*parg, err, false);
|
||||
match key {
|
||||
"alt"
|
||||
|"fields"
|
||||
|"key"
|
||||
|"oauth-token"
|
||||
|"pretty-print"
|
||||
|"quota-user"
|
||||
|"user-ip" => {
|
||||
let map = [
|
||||
("oauth-token", "oauth_token"),
|
||||
("pretty-print", "prettyPrint"),
|
||||
("quota-user", "quotaUser"),
|
||||
("user-ip", "userIp"),
|
||||
];
|
||||
call = call.param(map.iter().find(|t| t.0 == key).unwrap_or(&("", key)).1, value.unwrap_or("unset"))
|
||||
},
|
||||
_ => err.issues.push(CLIError::UnknownParameter(key.to_string())),
|
||||
}
|
||||
}
|
||||
let protocol = "standard-request";
|
||||
if dry_run {
|
||||
None
|
||||
} else {
|
||||
assert!(err.issues.len() == 0);
|
||||
match match protocol {
|
||||
"standard-request" => call.doit(),
|
||||
_ => unreachable!(),
|
||||
} {
|
||||
Err(api_err) => Some(api_err),
|
||||
Ok(mut response) => {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn _tasks_get(&self, dry_run: bool, err: &mut InvalidOptionsError)
|
||||
-> Option<api::Error> {
|
||||
let mut call = self.hub.tasks().get(&self.opt.arg_project, &self.opt.arg_taskqueue, &self.opt.arg_task);
|
||||
for parg in self.opt.arg_v.iter() {
|
||||
let (key, value) = parse_kv_arg(&*parg, err, false);
|
||||
match key {
|
||||
"alt"
|
||||
|"fields"
|
||||
|"key"
|
||||
|"oauth-token"
|
||||
|"pretty-print"
|
||||
|"quota-user"
|
||||
|"user-ip" => {
|
||||
let map = [
|
||||
("oauth-token", "oauth_token"),
|
||||
("pretty-print", "prettyPrint"),
|
||||
("quota-user", "quotaUser"),
|
||||
("user-ip", "userIp"),
|
||||
];
|
||||
call = call.param(map.iter().find(|t| t.0 == key).unwrap_or(&("", key)).1, value.unwrap_or("unset"))
|
||||
},
|
||||
_ => err.issues.push(CLIError::UnknownParameter(key.to_string())),
|
||||
}
|
||||
}
|
||||
let protocol = "standard-request";
|
||||
if dry_run {
|
||||
None
|
||||
} else {
|
||||
assert!(err.issues.len() == 0);
|
||||
let mut ostream = writer_from_opts(self.opt.flag_o, &self.opt.arg_out);
|
||||
match match protocol {
|
||||
"standard-request" => call.doit(),
|
||||
_ => unreachable!(),
|
||||
} {
|
||||
Err(api_err) => Some(api_err),
|
||||
Ok((mut response, output_schema)) => {
|
||||
serde::json::to_writer_pretty(&mut ostream, &output_schema).unwrap();
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn _tasks_insert(&self, dry_run: bool, err: &mut InvalidOptionsError)
|
||||
-> Option<api::Error> {
|
||||
let mut request = api::Task::default();
|
||||
let mut call = self.hub.tasks().insert(&request, &self.opt.arg_project, &self.opt.arg_taskqueue);
|
||||
for parg in self.opt.arg_v.iter() {
|
||||
let (key, value) = parse_kv_arg(&*parg, err, false);
|
||||
match key {
|
||||
"alt"
|
||||
|"fields"
|
||||
|"key"
|
||||
|"oauth-token"
|
||||
|"pretty-print"
|
||||
|"quota-user"
|
||||
|"user-ip" => {
|
||||
let map = [
|
||||
("oauth-token", "oauth_token"),
|
||||
("pretty-print", "prettyPrint"),
|
||||
("quota-user", "quotaUser"),
|
||||
("user-ip", "userIp"),
|
||||
];
|
||||
call = call.param(map.iter().find(|t| t.0 == key).unwrap_or(&("", key)).1, value.unwrap_or("unset"))
|
||||
},
|
||||
_ => err.issues.push(CLIError::UnknownParameter(key.to_string())),
|
||||
}
|
||||
}
|
||||
|
||||
let mut field_name = FieldCursor::default();
|
||||
for kvarg in self.opt.arg_kv.iter() {
|
||||
let (key, value) = parse_kv_arg(&*kvarg, err, false);
|
||||
if let Err(field_err) = field_name.set(&*key) {
|
||||
err.issues.push(field_err);
|
||||
}
|
||||
match &field_name.to_string()[..] {
|
||||
"kind" => {
|
||||
request.kind = Some(value.unwrap_or("").to_string());
|
||||
},
|
||||
"lease-timestamp" => {
|
||||
request.lease_timestamp = Some(value.unwrap_or("").to_string());
|
||||
},
|
||||
"id" => {
|
||||
request.id = Some(value.unwrap_or("").to_string());
|
||||
},
|
||||
"retry-count" => {
|
||||
request.retry_count = Some(arg_from_str(value.unwrap_or("-0"), err, "retry-count", "integer"));
|
||||
},
|
||||
"tag" => {
|
||||
request.tag = Some(value.unwrap_or("").to_string());
|
||||
},
|
||||
"payload-base64" => {
|
||||
request.payload_base64 = Some(value.unwrap_or("").to_string());
|
||||
},
|
||||
"queue-name" => {
|
||||
request.queue_name = Some(value.unwrap_or("").to_string());
|
||||
},
|
||||
"enqueue-timestamp" => {
|
||||
request.enqueue_timestamp = Some(value.unwrap_or("").to_string());
|
||||
},
|
||||
_ => {
|
||||
err.issues.push(CLIError::Field(FieldError::Unknown(field_name.to_string())));
|
||||
}
|
||||
}
|
||||
}
|
||||
let protocol = "standard-request";
|
||||
if dry_run {
|
||||
None
|
||||
} else {
|
||||
assert!(err.issues.len() == 0);
|
||||
let mut ostream = writer_from_opts(self.opt.flag_o, &self.opt.arg_out);
|
||||
match match protocol {
|
||||
"standard-request" => call.doit(),
|
||||
_ => unreachable!(),
|
||||
} {
|
||||
Err(api_err) => Some(api_err),
|
||||
Ok((mut response, output_schema)) => {
|
||||
serde::json::to_writer_pretty(&mut ostream, &output_schema).unwrap();
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn _tasks_lease(&self, dry_run: bool, err: &mut InvalidOptionsError)
|
||||
-> Option<api::Error> {
|
||||
let num_tasks: i32 = arg_from_str(&self.opt.arg_num_tasks, err, "<num-tasks>", "integer");
|
||||
let lease_secs: i32 = arg_from_str(&self.opt.arg_lease_secs, err, "<lease-secs>", "integer");
|
||||
let mut call = self.hub.tasks().lease(&self.opt.arg_project, &self.opt.arg_taskqueue, num_tasks, lease_secs);
|
||||
for parg in self.opt.arg_v.iter() {
|
||||
let (key, value) = parse_kv_arg(&*parg, err, false);
|
||||
match key {
|
||||
"tag" => {
|
||||
call = call.tag(value.unwrap_or(""));
|
||||
},
|
||||
"group-by-tag" => {
|
||||
call = call.group_by_tag(arg_from_str(value.unwrap_or("false"), err, "group-by-tag", "boolean"));
|
||||
},
|
||||
"alt"
|
||||
|"fields"
|
||||
|"key"
|
||||
|"oauth-token"
|
||||
|"pretty-print"
|
||||
|"quota-user"
|
||||
|"user-ip" => {
|
||||
let map = [
|
||||
("oauth-token", "oauth_token"),
|
||||
("pretty-print", "prettyPrint"),
|
||||
("quota-user", "quotaUser"),
|
||||
("user-ip", "userIp"),
|
||||
];
|
||||
call = call.param(map.iter().find(|t| t.0 == key).unwrap_or(&("", key)).1, value.unwrap_or("unset"))
|
||||
},
|
||||
_ => err.issues.push(CLIError::UnknownParameter(key.to_string())),
|
||||
}
|
||||
}
|
||||
let protocol = "standard-request";
|
||||
if dry_run {
|
||||
None
|
||||
} else {
|
||||
assert!(err.issues.len() == 0);
|
||||
let mut ostream = writer_from_opts(self.opt.flag_o, &self.opt.arg_out);
|
||||
match match protocol {
|
||||
"standard-request" => call.doit(),
|
||||
_ => unreachable!(),
|
||||
} {
|
||||
Err(api_err) => Some(api_err),
|
||||
Ok((mut response, output_schema)) => {
|
||||
serde::json::to_writer_pretty(&mut ostream, &output_schema).unwrap();
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn _tasks_list(&self, dry_run: bool, err: &mut InvalidOptionsError)
|
||||
-> Option<api::Error> {
|
||||
let mut call = self.hub.tasks().list(&self.opt.arg_project, &self.opt.arg_taskqueue);
|
||||
for parg in self.opt.arg_v.iter() {
|
||||
let (key, value) = parse_kv_arg(&*parg, err, false);
|
||||
match key {
|
||||
"alt"
|
||||
|"fields"
|
||||
|"key"
|
||||
|"oauth-token"
|
||||
|"pretty-print"
|
||||
|"quota-user"
|
||||
|"user-ip" => {
|
||||
let map = [
|
||||
("oauth-token", "oauth_token"),
|
||||
("pretty-print", "prettyPrint"),
|
||||
("quota-user", "quotaUser"),
|
||||
("user-ip", "userIp"),
|
||||
];
|
||||
call = call.param(map.iter().find(|t| t.0 == key).unwrap_or(&("", key)).1, value.unwrap_or("unset"))
|
||||
},
|
||||
_ => err.issues.push(CLIError::UnknownParameter(key.to_string())),
|
||||
}
|
||||
}
|
||||
let protocol = "standard-request";
|
||||
if dry_run {
|
||||
None
|
||||
} else {
|
||||
assert!(err.issues.len() == 0);
|
||||
let mut ostream = writer_from_opts(self.opt.flag_o, &self.opt.arg_out);
|
||||
match match protocol {
|
||||
"standard-request" => call.doit(),
|
||||
_ => unreachable!(),
|
||||
} {
|
||||
Err(api_err) => Some(api_err),
|
||||
Ok((mut response, output_schema)) => {
|
||||
serde::json::to_writer_pretty(&mut ostream, &output_schema).unwrap();
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn _tasks_patch(&self, dry_run: bool, err: &mut InvalidOptionsError)
|
||||
-> Option<api::Error> {
|
||||
let mut request = api::Task::default();
|
||||
let new_lease_seconds: i32 = arg_from_str(&self.opt.arg_new_lease_seconds, err, "<new-lease-seconds>", "integer");
|
||||
let mut call = self.hub.tasks().patch(&request, &self.opt.arg_project, &self.opt.arg_taskqueue, &self.opt.arg_task, new_lease_seconds);
|
||||
for parg in self.opt.arg_v.iter() {
|
||||
let (key, value) = parse_kv_arg(&*parg, err, false);
|
||||
match key {
|
||||
"alt"
|
||||
|"fields"
|
||||
|"key"
|
||||
|"oauth-token"
|
||||
|"pretty-print"
|
||||
|"quota-user"
|
||||
|"user-ip" => {
|
||||
let map = [
|
||||
("oauth-token", "oauth_token"),
|
||||
("pretty-print", "prettyPrint"),
|
||||
("quota-user", "quotaUser"),
|
||||
("user-ip", "userIp"),
|
||||
];
|
||||
call = call.param(map.iter().find(|t| t.0 == key).unwrap_or(&("", key)).1, value.unwrap_or("unset"))
|
||||
},
|
||||
_ => err.issues.push(CLIError::UnknownParameter(key.to_string())),
|
||||
}
|
||||
}
|
||||
|
||||
let mut field_name = FieldCursor::default();
|
||||
for kvarg in self.opt.arg_kv.iter() {
|
||||
let (key, value) = parse_kv_arg(&*kvarg, err, false);
|
||||
if let Err(field_err) = field_name.set(&*key) {
|
||||
err.issues.push(field_err);
|
||||
}
|
||||
match &field_name.to_string()[..] {
|
||||
"kind" => {
|
||||
request.kind = Some(value.unwrap_or("").to_string());
|
||||
},
|
||||
"lease-timestamp" => {
|
||||
request.lease_timestamp = Some(value.unwrap_or("").to_string());
|
||||
},
|
||||
"id" => {
|
||||
request.id = Some(value.unwrap_or("").to_string());
|
||||
},
|
||||
"retry-count" => {
|
||||
request.retry_count = Some(arg_from_str(value.unwrap_or("-0"), err, "retry-count", "integer"));
|
||||
},
|
||||
"tag" => {
|
||||
request.tag = Some(value.unwrap_or("").to_string());
|
||||
},
|
||||
"payload-base64" => {
|
||||
request.payload_base64 = Some(value.unwrap_or("").to_string());
|
||||
},
|
||||
"queue-name" => {
|
||||
request.queue_name = Some(value.unwrap_or("").to_string());
|
||||
},
|
||||
"enqueue-timestamp" => {
|
||||
request.enqueue_timestamp = Some(value.unwrap_or("").to_string());
|
||||
},
|
||||
_ => {
|
||||
err.issues.push(CLIError::Field(FieldError::Unknown(field_name.to_string())));
|
||||
}
|
||||
}
|
||||
}
|
||||
let protocol = "standard-request";
|
||||
if dry_run {
|
||||
None
|
||||
} else {
|
||||
assert!(err.issues.len() == 0);
|
||||
let mut ostream = writer_from_opts(self.opt.flag_o, &self.opt.arg_out);
|
||||
match match protocol {
|
||||
"standard-request" => call.doit(),
|
||||
_ => unreachable!(),
|
||||
} {
|
||||
Err(api_err) => Some(api_err),
|
||||
Ok((mut response, output_schema)) => {
|
||||
serde::json::to_writer_pretty(&mut ostream, &output_schema).unwrap();
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn _tasks_update(&self, dry_run: bool, err: &mut InvalidOptionsError)
|
||||
-> Option<api::Error> {
|
||||
let mut request = api::Task::default();
|
||||
let new_lease_seconds: i32 = arg_from_str(&self.opt.arg_new_lease_seconds, err, "<new-lease-seconds>", "integer");
|
||||
let mut call = self.hub.tasks().update(&request, &self.opt.arg_project, &self.opt.arg_taskqueue, &self.opt.arg_task, new_lease_seconds);
|
||||
for parg in self.opt.arg_v.iter() {
|
||||
let (key, value) = parse_kv_arg(&*parg, err, false);
|
||||
match key {
|
||||
"alt"
|
||||
|"fields"
|
||||
|"key"
|
||||
|"oauth-token"
|
||||
|"pretty-print"
|
||||
|"quota-user"
|
||||
|"user-ip" => {
|
||||
let map = [
|
||||
("oauth-token", "oauth_token"),
|
||||
("pretty-print", "prettyPrint"),
|
||||
("quota-user", "quotaUser"),
|
||||
("user-ip", "userIp"),
|
||||
];
|
||||
call = call.param(map.iter().find(|t| t.0 == key).unwrap_or(&("", key)).1, value.unwrap_or("unset"))
|
||||
},
|
||||
_ => err.issues.push(CLIError::UnknownParameter(key.to_string())),
|
||||
}
|
||||
}
|
||||
|
||||
let mut field_name = FieldCursor::default();
|
||||
for kvarg in self.opt.arg_kv.iter() {
|
||||
let (key, value) = parse_kv_arg(&*kvarg, err, false);
|
||||
if let Err(field_err) = field_name.set(&*key) {
|
||||
err.issues.push(field_err);
|
||||
}
|
||||
match &field_name.to_string()[..] {
|
||||
"kind" => {
|
||||
request.kind = Some(value.unwrap_or("").to_string());
|
||||
},
|
||||
"lease-timestamp" => {
|
||||
request.lease_timestamp = Some(value.unwrap_or("").to_string());
|
||||
},
|
||||
"id" => {
|
||||
request.id = Some(value.unwrap_or("").to_string());
|
||||
},
|
||||
"retry-count" => {
|
||||
request.retry_count = Some(arg_from_str(value.unwrap_or("-0"), err, "retry-count", "integer"));
|
||||
},
|
||||
"tag" => {
|
||||
request.tag = Some(value.unwrap_or("").to_string());
|
||||
},
|
||||
"payload-base64" => {
|
||||
request.payload_base64 = Some(value.unwrap_or("").to_string());
|
||||
},
|
||||
"queue-name" => {
|
||||
request.queue_name = Some(value.unwrap_or("").to_string());
|
||||
},
|
||||
"enqueue-timestamp" => {
|
||||
request.enqueue_timestamp = Some(value.unwrap_or("").to_string());
|
||||
},
|
||||
_ => {
|
||||
err.issues.push(CLIError::Field(FieldError::Unknown(field_name.to_string())));
|
||||
}
|
||||
}
|
||||
}
|
||||
let protocol = "standard-request";
|
||||
if dry_run {
|
||||
None
|
||||
} else {
|
||||
assert!(err.issues.len() == 0);
|
||||
let mut ostream = writer_from_opts(self.opt.flag_o, &self.opt.arg_out);
|
||||
match match protocol {
|
||||
"standard-request" => call.doit(),
|
||||
_ => unreachable!(),
|
||||
} {
|
||||
Err(api_err) => Some(api_err),
|
||||
Ok((mut response, output_schema)) => {
|
||||
serde::json::to_writer_pretty(&mut ostream, &output_schema).unwrap();
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn _doit(&self, dry_run: bool) -> (Option<api::Error>, Option<InvalidOptionsError>) {
|
||||
let mut err = InvalidOptionsError::new();
|
||||
let mut call_result: Option<api::Error>;
|
||||
let mut err_opt: Option<InvalidOptionsError> = None;
|
||||
|
||||
if self.opt.cmd_taskqueues {
|
||||
if self.opt.cmd_get {
|
||||
call_result = self._taskqueues_get(dry_run, &mut err);
|
||||
} else {
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
else if self.opt.cmd_tasks {
|
||||
if self.opt.cmd_delete {
|
||||
call_result = self._tasks_delete(dry_run, &mut err);
|
||||
} else if self.opt.cmd_get {
|
||||
call_result = self._tasks_get(dry_run, &mut err);
|
||||
} else if self.opt.cmd_insert {
|
||||
call_result = self._tasks_insert(dry_run, &mut err);
|
||||
} else if self.opt.cmd_lease {
|
||||
call_result = self._tasks_lease(dry_run, &mut err);
|
||||
} else if self.opt.cmd_list {
|
||||
call_result = self._tasks_list(dry_run, &mut err);
|
||||
} else if self.opt.cmd_patch {
|
||||
call_result = self._tasks_patch(dry_run, &mut err);
|
||||
} else if self.opt.cmd_update {
|
||||
call_result = self._tasks_update(dry_run, &mut err);
|
||||
} else {
|
||||
unreachable!();
|
||||
}
|
||||
} else {
|
||||
unreachable!();
|
||||
}
|
||||
|
||||
if dry_run {
|
||||
if err.issues.len() > 0 {
|
||||
err_opt = Some(err);
|
||||
}
|
||||
}
|
||||
(call_result, err_opt)
|
||||
}
|
||||
|
||||
// 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(&opt.flag_config_dir) {
|
||||
Err(e) => return Err(InvalidOptionsError::single(e, 3)),
|
||||
Ok(p) => p,
|
||||
};
|
||||
|
||||
match cmn::application_secret_from_directory(&config_dir, "taskqueue1-beta2-secret.json",
|
||||
"{\"installed\":{\"auth_uri\":\"https://accounts.google.com/o/oauth2/auth\",\"client_secret\":\"hCsslbCUyfehWMmbkG8vTYxG\",\"token_uri\":\"https://accounts.google.com/o/oauth2/token\",\"client_email\":\"\",\"redirect_uris\":[\"urn:ietf:wg:oauth:2.0:oob\",\"oob\"],\"client_x509_cert_url\":\"\",\"client_id\":\"620010449518-9ngf7o4dhs0dka470npqvor6dc5lqb9b.apps.googleusercontent.com\",\"auth_provider_x509_cert_url\":\"https://www.googleapis.com/oauth2/v1/certs\"}}") {
|
||||
Ok(secret) => (config_dir, secret),
|
||||
Err(e) => return Err(InvalidOptionsError::single(e, 4))
|
||||
}
|
||||
};
|
||||
|
||||
let auth = Authenticator::new( &secret, DefaultAuthenticatorDelegate,
|
||||
if opt.flag_debug_auth {
|
||||
hyper::Client::with_connector(mock::TeeConnector {
|
||||
connector: hyper::net::HttpConnector(None)
|
||||
})
|
||||
} else {
|
||||
hyper::Client::new()
|
||||
},
|
||||
JsonTokenStorage {
|
||||
program_name: "taskqueue1-beta2",
|
||||
db_dir: config_dir.clone(),
|
||||
}, None);
|
||||
|
||||
let client =
|
||||
if opt.flag_debug {
|
||||
hyper::Client::with_connector(mock::TeeConnector {
|
||||
connector: hyper::net::HttpConnector(None)
|
||||
})
|
||||
} else {
|
||||
hyper::Client::new()
|
||||
};
|
||||
let engine = Engine {
|
||||
opt: opt,
|
||||
hub: api::Taskqueue::new(client, auth),
|
||||
};
|
||||
|
||||
match engine._doit(true) {
|
||||
(_, Some(err)) => Err(err),
|
||||
_ => 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> {
|
||||
self._doit(false).0
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let opts: Options = Options::docopt().decode().unwrap_or_else(|e| e.exit());
|
||||
match Engine::new(opts) {
|
||||
Err(err) => {
|
||||
writeln!(io::stderr(), "{}", err).ok();
|
||||
env::set_exit_status(err.exit_code);
|
||||
},
|
||||
Ok(engine) => {
|
||||
if let Some(err) = engine.doit() {
|
||||
writeln!(io::stderr(), "{:?}", err).ok();
|
||||
writeln!(io::stderr(), "{}", err).ok();
|
||||
env::set_exit_status(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
31
gen/webfonts1-cli/Cargo.toml
Normal file
31
gen/webfonts1-cli/Cargo.toml
Normal file
@@ -0,0 +1,31 @@
|
||||
# DO NOT EDIT !
|
||||
# This file was generated automatically from 'src/mako/Cargo.toml.mako'
|
||||
# DO NOT EDIT !
|
||||
[package]
|
||||
|
||||
name = "google-webfonts1-cli"
|
||||
version = "0.0.1+20140210"
|
||||
authors = ["Sebastian Thiel <byronimo@gmail.com>"]
|
||||
description = "A complete library to interact with webfonts (protocol v1)"
|
||||
repository = "https://github.com/Byron/google-apis-rs/tree/master/gen/webfonts1-cli"
|
||||
homepage = "https://developers.google.com/fonts/docs/developer_api"
|
||||
documentation = "http://byron.github.io/google-apis-rs/google_webfonts1_cli"
|
||||
license = "MIT"
|
||||
keywords = ["webfonts", "google", "cli"]
|
||||
|
||||
[[bin]]
|
||||
name = "webfonts1"
|
||||
|
||||
[dependencies]
|
||||
hyper = "*"
|
||||
mime = "*"
|
||||
yup-oauth2 = "*"
|
||||
docopt = "*"
|
||||
docopt_macros = "*"
|
||||
rustc-serialize = "*"
|
||||
yup-hyper-mock = "*"
|
||||
serde = ">= 0.3.0"
|
||||
serde_macros = "*"
|
||||
|
||||
[dependencies.google-webfonts1]
|
||||
path = "../webfonts1"
|
||||
30
gen/webfonts1-cli/LICENSE.md
Normal file
30
gen/webfonts1-cli/LICENSE.md
Normal file
@@ -0,0 +1,30 @@
|
||||
<!---
|
||||
DO NOT EDIT !
|
||||
This file was generated automatically from 'src/mako/LICENSE.md.mako'
|
||||
DO NOT EDIT !
|
||||
-->
|
||||
The MIT License (MIT)
|
||||
=====================
|
||||
|
||||
Copyright © `2015` `Sebastian Thiel`
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the “Software”), to deal in the Software without
|
||||
restriction, including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
4
gen/webfonts1-cli/README.md
Normal file
4
gen/webfonts1-cli/README.md
Normal file
@@ -0,0 +1,4 @@
|
||||
# HELLO WEBFONTS:V1
|
||||
|
||||
|
||||
Include information about application secret files, and how we automatically write a default one.
|
||||
17
gen/webfonts1-cli/mkdocs.yml
Normal file
17
gen/webfonts1-cli/mkdocs.yml
Normal file
@@ -0,0 +1,17 @@
|
||||
site_name: webfonts v0.0.1+20140210
|
||||
site_url: http://byron.github.io/google-apis-rs/google-webfonts1-cli
|
||||
site_description: Write integrating applications with bcore
|
||||
|
||||
repo_url: https://github.com/Byron/google-apis-rs/tree/master/gen/webfonts1-cli
|
||||
|
||||
docs_dir: docs
|
||||
site_dir: build_html
|
||||
|
||||
pages:
|
||||
- ['index.md', 'Home']
|
||||
- ['webfonts_list.md', 'Webfonts', 'List']
|
||||
|
||||
theme: readthedocs
|
||||
|
||||
copyright: Copyright © 2015, `Sebastian Thiel`
|
||||
|
||||
454
gen/webfonts1-cli/src/cmn.rs
Normal file
454
gen/webfonts1-cli/src/cmn.rs
Normal file
@@ -0,0 +1,454 @@
|
||||
// COPY OF 'src/rust/cli/cmn.rs'
|
||||
// DO NOT EDIT
|
||||
use oauth2::{ApplicationSecret, ConsoleApplicationSecret, TokenStorage, Token};
|
||||
use rustc_serialize::json;
|
||||
use mime::Mime;
|
||||
|
||||
use std::fs;
|
||||
use std::env;
|
||||
use std::io;
|
||||
use std::fmt;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::str::FromStr;
|
||||
use std::string::ToString;
|
||||
use std::io::{Write, Read, stdout};
|
||||
|
||||
use std::default::Default;
|
||||
|
||||
const FIELD_SEP: char = '.';
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct FieldCursor(Vec<String>);
|
||||
|
||||
impl ToString for FieldCursor {
|
||||
fn to_string(&self) -> String {
|
||||
self.0.connect(".")
|
||||
}
|
||||
}
|
||||
|
||||
impl FieldCursor {
|
||||
pub fn set(&mut self, value: &str) -> Result<(), CLIError> {
|
||||
if value.len() == 0 {
|
||||
return Err(CLIError::Field(FieldError::Empty))
|
||||
}
|
||||
|
||||
let mut first_is_field_sep = false;
|
||||
let mut char_count: usize = 0;
|
||||
let mut last_c = FIELD_SEP;
|
||||
let mut num_conscutive_field_seps = 0;
|
||||
|
||||
let mut field = String::new();
|
||||
let mut fields = self.0.clone();
|
||||
|
||||
let push_field = |fs: &mut Vec<String>, f: &mut String| {
|
||||
if f.len() > 0 {
|
||||
fs.push(f.clone());
|
||||
f.truncate(0);
|
||||
}
|
||||
};
|
||||
|
||||
for (cid, c) in value.chars().enumerate() {
|
||||
char_count += 1;
|
||||
|
||||
if c == FIELD_SEP {
|
||||
if cid == 0 {
|
||||
first_is_field_sep = true;
|
||||
}
|
||||
num_conscutive_field_seps += 1;
|
||||
if cid > 0 && last_c == FIELD_SEP {
|
||||
if fields.pop().is_none() {
|
||||
return Err(CLIError::Field(FieldError::PopOnEmpty(value.to_string())))
|
||||
}
|
||||
} else {
|
||||
push_field(&mut fields, &mut field);
|
||||
}
|
||||
} else {
|
||||
num_conscutive_field_seps = 0;
|
||||
if cid == 1 {
|
||||
if first_is_field_sep {
|
||||
fields.truncate(0);
|
||||
}
|
||||
}
|
||||
field.push(c);
|
||||
}
|
||||
|
||||
last_c = c;
|
||||
}
|
||||
|
||||
push_field(&mut fields, &mut field);
|
||||
|
||||
if char_count == 1 && first_is_field_sep {
|
||||
fields.truncate(0);
|
||||
}
|
||||
if char_count > 1 && num_conscutive_field_seps == 1 {
|
||||
return Err(CLIError::Field(FieldError::TrailingFieldSep(value.to_string())))
|
||||
}
|
||||
|
||||
self.0 = fields;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn num_fields(&self) -> usize {
|
||||
self.0.len()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_kv_arg<'a>(kv: &'a str, err: &mut InvalidOptionsError, for_hashmap: bool)
|
||||
-> (&'a str, Option<&'a str>) {
|
||||
let mut add_err = || err.issues.push(CLIError::InvalidKeyValueSyntax(kv.to_string(),for_hashmap));
|
||||
match kv.find('=') {
|
||||
None => {
|
||||
add_err();
|
||||
return (kv, None)
|
||||
},
|
||||
Some(pos) => {
|
||||
let key = &kv[..pos];
|
||||
if kv.len() <= pos + 1 {
|
||||
add_err();
|
||||
return (key, None)
|
||||
}
|
||||
(key, Some(&kv[pos+1..]))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn input_file_from_opts(file_path: &str, err: &mut InvalidOptionsError) -> Option<fs::File> {
|
||||
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<Mime> {
|
||||
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<Write> {
|
||||
if !flag || arg == "-" {
|
||||
Box::new(stdout())
|
||||
} else {
|
||||
Box::new(fs::OpenOptions::new().create(true).write(true).open(arg).unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn arg_from_str<T>(arg: &str, err: &mut InvalidOptionsError,
|
||||
arg_name: &'static str,
|
||||
arg_type: &'static str) -> T
|
||||
where T: FromStr + Default,
|
||||
<T as FromStr>::Err: fmt::Display {
|
||||
match FromStr::from_str(arg) {
|
||||
Err(perr) => {
|
||||
err.issues.push(
|
||||
CLIError::ParseError((arg_name, arg_type, arg.to_string(), format!("{}", perr)))
|
||||
);
|
||||
Default::default()
|
||||
},
|
||||
Ok(v) => v,
|
||||
}
|
||||
}
|
||||
|
||||
pub struct JsonTokenStorage {
|
||||
pub program_name: &'static str,
|
||||
pub db_dir: String,
|
||||
}
|
||||
|
||||
impl JsonTokenStorage {
|
||||
fn path(&self, scope_hash: u64) -> PathBuf {
|
||||
Path::new(&self.db_dir).join(&format!("{}-token-{}.json", self.program_name, scope_hash))
|
||||
}
|
||||
}
|
||||
|
||||
impl TokenStorage for JsonTokenStorage {
|
||||
type Error = io::Error;
|
||||
|
||||
// NOTE: logging might be interesting, currently we swallow all errors
|
||||
fn set(&mut self, scope_hash: u64, _: &Vec<&str>, token: Option<Token>) -> Option<io::Error> {
|
||||
match token {
|
||||
None => {
|
||||
match fs::remove_file(self.path(scope_hash)) {
|
||||
Err(err) =>
|
||||
match err.kind() {
|
||||
io::ErrorKind::NotFound => None,
|
||||
_ => Some(err)
|
||||
},
|
||||
Ok(_) => None
|
||||
}
|
||||
}
|
||||
Some(token) => {
|
||||
let json_token = json::encode(&token).unwrap();
|
||||
match fs::OpenOptions::new().create(true).write(true).open(&self.path(scope_hash)) {
|
||||
Ok(mut f) => {
|
||||
match f.write(json_token.as_bytes()) {
|
||||
Ok(_) => None,
|
||||
Err(io_err) => Some(io_err),
|
||||
}
|
||||
},
|
||||
Err(io_err) => Some(io_err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get(&self, scope_hash: u64, _: &Vec<&str>) -> Result<Option<Token>, io::Error> {
|
||||
match fs::File::open(&self.path(scope_hash)) {
|
||||
Ok(mut f) => {
|
||||
let mut json_string = String::new();
|
||||
match f.read_to_string(&mut json_string) {
|
||||
Ok(_) => Ok(Some(json::decode::<Token>(&json_string).unwrap())),
|
||||
Err(io_err) => Err(io_err),
|
||||
}
|
||||
},
|
||||
Err(io_err) => {
|
||||
match io_err.kind() {
|
||||
io::ErrorKind::NotFound => Ok(None),
|
||||
_ => Err(io_err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug)]
|
||||
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)),
|
||||
}
|
||||
|
||||
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 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 FieldError {
|
||||
PopOnEmpty(String),
|
||||
TrailingFieldSep(String),
|
||||
Unknown(String),
|
||||
Empty,
|
||||
}
|
||||
|
||||
|
||||
impl fmt::Display for FieldError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
match *self {
|
||||
FieldError::PopOnEmpty(ref field)
|
||||
=> writeln!(f, "'{}': Cannot move up on empty field cursor", field),
|
||||
FieldError::TrailingFieldSep(ref field)
|
||||
=> writeln!(f, "'{}': Single field separator may not be last character", field),
|
||||
FieldError::Unknown(ref field)
|
||||
=> writeln!(f, "Field '{}' does not exist", field),
|
||||
FieldError::Empty
|
||||
=> writeln!(f, "Field names must not be empty"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum CLIError {
|
||||
Configuration(ConfigurationError),
|
||||
ParseError((&'static str, &'static str, String, String)),
|
||||
UnknownParameter(String),
|
||||
InvalidKeyValueSyntax(String, bool),
|
||||
Input(InputError),
|
||||
Field(FieldError),
|
||||
}
|
||||
|
||||
impl fmt::Display for CLIError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
match *self {
|
||||
CLIError::Configuration(ref err) => write!(f, "Configuration -> {}", err),
|
||||
CLIError::Input(ref err) => write!(f, "Input -> {}", err),
|
||||
CLIError::Field(ref err) => write!(f, "Field -> {}", 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),
|
||||
CLIError::UnknownParameter(ref param_name)
|
||||
=> writeln!(f, "Parameter '{}' is unknown.", param_name),
|
||||
CLIError::InvalidKeyValueSyntax(ref kv, is_hashmap) => {
|
||||
let hashmap_info = if is_hashmap { "hashmap " } else { "" };
|
||||
writeln!(f, "'{}' does not match {}pattern <key>=<value>", kv, hashmap_info)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct InvalidOptionsError {
|
||||
pub issues: Vec<CLIError>,
|
||||
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: CLIError, exit_code: i32) -> InvalidOptionsError {
|
||||
InvalidOptionsError {
|
||||
issues: vec![err],
|
||||
exit_code: exit_code,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new() -> InvalidOptionsError {
|
||||
InvalidOptionsError {
|
||||
issues: Vec::new(),
|
||||
exit_code: 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn assure_config_dir_exists(dir: &str) -> Result<String, CLIError> {
|
||||
let trdir = dir.trim();
|
||||
if trdir.len() == 0 {
|
||||
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(CLIError::Configuration(ConfigurationError::HomeExpansionFailed(trdir.to_string()))),
|
||||
Some(mut user) => {
|
||||
user.push_str(&trdir[1..]);
|
||||
user
|
||||
}
|
||||
}
|
||||
} else {
|
||||
trdir.to_string()
|
||||
};
|
||||
|
||||
if let Err(err) = fs::create_dir(&expanded_config_dir) {
|
||||
if err.kind() != io::ErrorKind::AlreadyExists {
|
||||
return Err(CLIError::Configuration(
|
||||
ConfigurationError::DirectoryCreationFailed((expanded_config_dir, err))))
|
||||
}
|
||||
}
|
||||
|
||||
Ok(expanded_config_dir)
|
||||
}
|
||||
|
||||
pub fn application_secret_from_directory(dir: &str,
|
||||
secret_basename: &str,
|
||||
json_app_secret: &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(CLIError::Configuration(ConfigurationError::IOError(
|
||||
(secret_str(), io_err)
|
||||
)))
|
||||
};
|
||||
|
||||
for _ in 0..2 {
|
||||
match fs::File::open(&secret_path) {
|
||||
Err(mut err) => {
|
||||
if err.kind() == io::ErrorKind::NotFound {
|
||||
// Write our built-in one - user may adjust the written file at will
|
||||
|
||||
err = match fs::OpenOptions::new().create(true).write(true).open(&secret_path) {
|
||||
Err(cfe) => cfe,
|
||||
Ok(mut f) => {
|
||||
match f.write(json_app_secret.as_bytes()) {
|
||||
Err(io_err) => io_err,
|
||||
Ok(_) => continue,
|
||||
}
|
||||
}
|
||||
};
|
||||
// fall through to IO error handling
|
||||
}
|
||||
return secret_io_error(err)
|
||||
},
|
||||
Ok(mut f) => {
|
||||
let mut json_encoded_secret = String::new();
|
||||
if let Err(io_err) = f.read_to_string(&mut json_encoded_secret) {
|
||||
return secret_io_error(io_err)
|
||||
}
|
||||
match json::decode::<ConsoleApplicationSecret>(&json_encoded_secret) {
|
||||
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(
|
||||
CLIError::Configuration(
|
||||
ConfigurationError::Secret(
|
||||
ApplicationSecretError::FormatError(secret_str())
|
||||
)))
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
unreachable!();
|
||||
}
|
||||
195
gen/webfonts1-cli/src/main.rs
Normal file
195
gen/webfonts1-cli/src/main.rs
Normal file
@@ -0,0 +1,195 @@
|
||||
// DO NOT EDIT !
|
||||
// This file was generated automatically from 'src/mako/cli/main.rs.mako'
|
||||
// DO NOT EDIT !
|
||||
#![feature(plugin, exit_status)]
|
||||
#![plugin(docopt_macros)]
|
||||
#![allow(unused_variables, unused_imports, dead_code, unused_mut)]
|
||||
|
||||
extern crate docopt;
|
||||
extern crate yup_oauth2 as oauth2;
|
||||
extern crate yup_hyper_mock as mock;
|
||||
extern crate rustc_serialize;
|
||||
extern crate serde;
|
||||
extern crate hyper;
|
||||
extern crate mime;
|
||||
extern crate google_webfonts1 as api;
|
||||
|
||||
use std::env;
|
||||
use std::io::{self, Write};
|
||||
|
||||
docopt!(Options derive Debug, "
|
||||
Usage:
|
||||
webfonts1 [options] webfonts list [-p <v>]... [-o <out>]
|
||||
webfonts1 --help
|
||||
|
||||
All documentation details can be found TODO: <URL to github.io docs here, see #51>
|
||||
|
||||
Configuration:
|
||||
--config-dir <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: ~/.google-service-cli]
|
||||
--debug
|
||||
Output all server communication to standard error. `tx` and `rx` are placed into
|
||||
the same stream.
|
||||
--debug-auth
|
||||
Output all communication related to authentication to standard error. `tx` and `rx` are placed into
|
||||
the same stream.
|
||||
");
|
||||
|
||||
mod cmn;
|
||||
use cmn::{InvalidOptionsError, CLIError, JsonTokenStorage, arg_from_str, writer_from_opts, parse_kv_arg,
|
||||
input_file_from_opts, input_mime_from_opts, FieldCursor, FieldError};
|
||||
|
||||
use std::default::Default;
|
||||
use std::str::FromStr;
|
||||
|
||||
use oauth2::{Authenticator, DefaultAuthenticatorDelegate};
|
||||
use rustc_serialize::json;
|
||||
|
||||
struct Engine {
|
||||
opt: Options,
|
||||
hub: api::Webfonts<hyper::Client, Authenticator<DefaultAuthenticatorDelegate, JsonTokenStorage, hyper::Client>>,
|
||||
}
|
||||
|
||||
|
||||
impl Engine {
|
||||
fn _webfonts_list(&self, dry_run: bool, err: &mut InvalidOptionsError)
|
||||
-> Option<api::Error> {
|
||||
let mut call = self.hub.webfonts().list();
|
||||
for parg in self.opt.arg_v.iter() {
|
||||
let (key, value) = parse_kv_arg(&*parg, err, false);
|
||||
match key {
|
||||
"sort" => {
|
||||
call = call.sort(value.unwrap_or(""));
|
||||
},
|
||||
"alt"
|
||||
|"fields"
|
||||
|"key"
|
||||
|"oauth-token"
|
||||
|"pretty-print"
|
||||
|"quota-user"
|
||||
|"user-ip" => {
|
||||
let map = [
|
||||
("oauth-token", "oauth_token"),
|
||||
("pretty-print", "prettyPrint"),
|
||||
("quota-user", "quotaUser"),
|
||||
("user-ip", "userIp"),
|
||||
];
|
||||
call = call.param(map.iter().find(|t| t.0 == key).unwrap_or(&("", key)).1, value.unwrap_or("unset"))
|
||||
},
|
||||
_ => err.issues.push(CLIError::UnknownParameter(key.to_string())),
|
||||
}
|
||||
}
|
||||
let protocol = "standard-request";
|
||||
if dry_run {
|
||||
None
|
||||
} else {
|
||||
assert!(err.issues.len() == 0);
|
||||
let mut ostream = writer_from_opts(self.opt.flag_o, &self.opt.arg_out);
|
||||
match match protocol {
|
||||
"standard-request" => call.doit(),
|
||||
_ => unreachable!(),
|
||||
} {
|
||||
Err(api_err) => Some(api_err),
|
||||
Ok((mut response, output_schema)) => {
|
||||
serde::json::to_writer_pretty(&mut ostream, &output_schema).unwrap();
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn _doit(&self, dry_run: bool) -> (Option<api::Error>, Option<InvalidOptionsError>) {
|
||||
let mut err = InvalidOptionsError::new();
|
||||
let mut call_result: Option<api::Error>;
|
||||
let mut err_opt: Option<InvalidOptionsError> = None;
|
||||
|
||||
if self.opt.cmd_webfonts {
|
||||
if self.opt.cmd_list {
|
||||
call_result = self._webfonts_list(dry_run, &mut err);
|
||||
} else {
|
||||
unreachable!();
|
||||
}
|
||||
} else {
|
||||
unreachable!();
|
||||
}
|
||||
|
||||
if dry_run {
|
||||
if err.issues.len() > 0 {
|
||||
err_opt = Some(err);
|
||||
}
|
||||
}
|
||||
(call_result, err_opt)
|
||||
}
|
||||
|
||||
// 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(&opt.flag_config_dir) {
|
||||
Err(e) => return Err(InvalidOptionsError::single(e, 3)),
|
||||
Ok(p) => p,
|
||||
};
|
||||
|
||||
match cmn::application_secret_from_directory(&config_dir, "webfonts1-secret.json",
|
||||
"{\"installed\":{\"auth_uri\":\"https://accounts.google.com/o/oauth2/auth\",\"client_secret\":\"hCsslbCUyfehWMmbkG8vTYxG\",\"token_uri\":\"https://accounts.google.com/o/oauth2/token\",\"client_email\":\"\",\"redirect_uris\":[\"urn:ietf:wg:oauth:2.0:oob\",\"oob\"],\"client_x509_cert_url\":\"\",\"client_id\":\"620010449518-9ngf7o4dhs0dka470npqvor6dc5lqb9b.apps.googleusercontent.com\",\"auth_provider_x509_cert_url\":\"https://www.googleapis.com/oauth2/v1/certs\"}}") {
|
||||
Ok(secret) => (config_dir, secret),
|
||||
Err(e) => return Err(InvalidOptionsError::single(e, 4))
|
||||
}
|
||||
};
|
||||
|
||||
let auth = Authenticator::new( &secret, DefaultAuthenticatorDelegate,
|
||||
if opt.flag_debug_auth {
|
||||
hyper::Client::with_connector(mock::TeeConnector {
|
||||
connector: hyper::net::HttpConnector(None)
|
||||
})
|
||||
} else {
|
||||
hyper::Client::new()
|
||||
},
|
||||
JsonTokenStorage {
|
||||
program_name: "webfonts1",
|
||||
db_dir: config_dir.clone(),
|
||||
}, None);
|
||||
|
||||
let client =
|
||||
if opt.flag_debug {
|
||||
hyper::Client::with_connector(mock::TeeConnector {
|
||||
connector: hyper::net::HttpConnector(None)
|
||||
})
|
||||
} else {
|
||||
hyper::Client::new()
|
||||
};
|
||||
let engine = Engine {
|
||||
opt: opt,
|
||||
hub: api::Webfonts::new(client, auth),
|
||||
};
|
||||
|
||||
match engine._doit(true) {
|
||||
(_, Some(err)) => Err(err),
|
||||
_ => 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> {
|
||||
self._doit(false).0
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let opts: Options = Options::docopt().decode().unwrap_or_else(|e| e.exit());
|
||||
match Engine::new(opts) {
|
||||
Err(err) => {
|
||||
writeln!(io::stderr(), "{}", err).ok();
|
||||
env::set_exit_status(err.exit_code);
|
||||
},
|
||||
Ok(engine) => {
|
||||
if let Some(err) = engine.doit() {
|
||||
writeln!(io::stderr(), "{:?}", err).ok();
|
||||
writeln!(io::stderr(), "{}", err).ok();
|
||||
env::set_exit_status(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -51,7 +51,7 @@ use std::io;
|
||||
use std::fs;
|
||||
use std::thread::sleep_ms;
|
||||
|
||||
pub use cmn::{MultiPartReader, ToParts, MethodInfo, Result, Error, CallBuilder, Hub, ReadSeek, Part, ResponseResult, RequestValue, NestedType, Delegate, DefaultDelegate, MethodsBuilder, Resource, JsonServerError};
|
||||
pub use cmn::{MultiPartReader, ToParts, MethodInfo, Result, Error, CallBuilder, Hub, ReadSeek, Part, ResponseResult, RequestValue, NestedType, Delegate, DefaultDelegate, MethodsBuilder, Resource, ErrorResponse};
|
||||
|
||||
|
||||
// ##############
|
||||
|
||||
@@ -368,14 +368,15 @@ match result {
|
||||
Err(e) => match e {
|
||||
// The Error enum provides details about what exactly happened.
|
||||
// You can also just use its `Debug`, `Display` or `Error` traits
|
||||
Error::HttpError(_)
|
||||
Error::HttpError(_)
|
||||
|Error::MissingAPIKey
|
||||
|Error::MissingToken
|
||||
|Error::Cancelled
|
||||
|Error::UploadSizeLimitExceeded(_, _)
|
||||
|Error::Failure(_)
|
||||
|Error::BadRequest(_)
|
||||
|Error::FieldClash(_)
|
||||
|Error::JsonDecodeError(_) => println!("{}", e),
|
||||
|Error::JsonDecodeError(_, _) => println!("{}", e),
|
||||
},
|
||||
Ok(res) => println!("Success: {:?}", res),
|
||||
}
|
||||
@@ -770,12 +771,17 @@ else {
|
||||
if !res.status.is_success() {
|
||||
let mut json_err = String::new();
|
||||
res.read_to_string(&mut json_err).unwrap();
|
||||
if let oauth2::Retry::After(d) = dlg.http_failure(&res, json::from_str(&json_err).ok()) {
|
||||
if let oauth2::Retry::After(d) = dlg.http_failure(&res,
|
||||
json::from_str(&json_err).ok(),
|
||||
json::from_str(&json_err).ok()) {
|
||||
sleep_ms(d.num_milliseconds() as u32);
|
||||
continue;
|
||||
}
|
||||
${delegate_finish}(false);
|
||||
return Err(Error::Failure(res))
|
||||
return match json::from_str::<ErrorResponse>(&json_err){
|
||||
Err(_) => Err(Error::Failure(res)),
|
||||
Ok(serr) => Err(Error::BadRequest(serr))
|
||||
}
|
||||
}
|
||||
% if resumable_media_param:
|
||||
if protocol == "${resumable_media_param.protocol}" {
|
||||
@@ -836,7 +842,7 @@ if enable_resource_parsing \
|
||||
Ok(decoded) => (res, decoded),
|
||||
Err(err) => {
|
||||
dlg.response_json_decode_error(&json_response, &err);
|
||||
return Err(Error::JsonDecodeError(err));
|
||||
return Err(Error::JsonDecodeError(json_response, err));
|
||||
}
|
||||
}
|
||||
}\
|
||||
|
||||
@@ -32,6 +32,7 @@ ${engine.new(c)}\
|
||||
|
||||
fn main() {
|
||||
let opts: Options = Options::docopt().decode().unwrap_or_else(|e| e.exit());
|
||||
let debug = opts.flag_debug;
|
||||
match Engine::new(opts) {
|
||||
Err(err) => {
|
||||
writeln!(io::stderr(), "{}", err).ok();
|
||||
@@ -39,8 +40,11 @@ fn main() {
|
||||
},
|
||||
Ok(engine) => {
|
||||
if let Some(err) = engine.doit() {
|
||||
writeln!(io::stderr(), "{:?}", err).ok();
|
||||
writeln!(io::stderr(), "{}", err).ok();
|
||||
if debug {
|
||||
writeln!(io::stderr(), "{:?}", err).ok();
|
||||
} else {
|
||||
writeln!(io::stderr(), "{}", err).ok();
|
||||
}
|
||||
env::set_exit_status(1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,7 +58,6 @@ pub trait ToParts {
|
||||
fn to_parts(&self) -> String;
|
||||
}
|
||||
|
||||
|
||||
/// A utility type which can decode a server response that indicates error
|
||||
#[derive(Deserialize)]
|
||||
pub struct JsonServerError {
|
||||
@@ -66,6 +65,30 @@ pub struct JsonServerError {
|
||||
pub error_description: Option<String>
|
||||
}
|
||||
|
||||
/// A utility to represent detailed errors we might see in case there are BadRequests.
|
||||
/// The latter happen if the sent parameters or request structures are unsound
|
||||
#[derive(Deserialize, Serialize, Debug)]
|
||||
pub struct ErrorResponse {
|
||||
error: ServerError,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Debug)]
|
||||
pub struct ServerError {
|
||||
errors: Vec<ServerMessage>,
|
||||
code: u16,
|
||||
message: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Debug)]
|
||||
pub struct ServerMessage {
|
||||
domain: String,
|
||||
reason: String,
|
||||
message: String,
|
||||
#[serde(rename="locationType")]
|
||||
location_type: Option<String>,
|
||||
location: Option<String>
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct DummyNetworkStream;
|
||||
|
||||
@@ -173,7 +196,7 @@ pub trait Delegate {
|
||||
///
|
||||
/// If you choose to retry after a duration, the duration should be chosen using the
|
||||
/// [exponential backoff algorithm](http://en.wikipedia.org/wiki/Exponential_backoff).
|
||||
fn http_failure(&mut self, _: &hyper::client::Response, Option<JsonServerError>) -> Retry {
|
||||
fn http_failure(&mut self, _: &hyper::client::Response, Option<JsonServerError>, _: Option<ServerError>) -> Retry {
|
||||
Retry::Abort
|
||||
}
|
||||
|
||||
@@ -228,6 +251,10 @@ pub enum Error {
|
||||
/// even though the maximum upload size is what is stored in field `.1`.
|
||||
UploadSizeLimitExceeded(u64, u64),
|
||||
|
||||
/// Represents information about a request that was not understood by the server.
|
||||
/// Details are included.
|
||||
BadRequest(ErrorResponse),
|
||||
|
||||
/// We needed an API key for authentication, but didn't obtain one.
|
||||
/// Neither through the authenticator, nor through the Delegate.
|
||||
MissingAPIKey,
|
||||
@@ -243,7 +270,7 @@ pub enum Error {
|
||||
|
||||
/// Shows that we failed to decode the server response.
|
||||
/// This can happen if the protocol changes in conjunction with strict json decoding.
|
||||
JsonDecodeError(serde::json::Error),
|
||||
JsonDecodeError(String, serde::json::Error),
|
||||
|
||||
/// Indicates an HTTP repsonse with a non-success status code
|
||||
Failure(hyper::client::Response),
|
||||
@@ -261,13 +288,16 @@ impl Display for Error {
|
||||
writeln!(f, "The application's API key was not found in the configuration").ok();
|
||||
writeln!(f, "It is used as there are no Scopes defined for this method.")
|
||||
},
|
||||
Error::BadRequest(ref err)
|
||||
=> writeln!(f, "Bad Requst ({}): {}", err.error.code, err.error.message),
|
||||
Error::MissingToken(ref err) =>
|
||||
writeln!(f, "Token retrieval failed with error: {}", err),
|
||||
Error::Cancelled =>
|
||||
writeln!(f, "Operation cancelled by delegate"),
|
||||
Error::FieldClash(field) =>
|
||||
writeln!(f, "The custom parameter '{}' is already provided natively by the CallBuilder.", field),
|
||||
Error::JsonDecodeError(ref err) => err.fmt(f),
|
||||
Error::JsonDecodeError(ref json_str, ref err)
|
||||
=> writeln!(f, "{}: {}", err, json_str),
|
||||
Error::Failure(ref response) =>
|
||||
writeln!(f, "Http status indicates failure: {:?}", response),
|
||||
}
|
||||
@@ -278,7 +308,7 @@ impl error::Error for Error {
|
||||
fn description(&self) -> &str {
|
||||
match *self {
|
||||
Error::HttpError(ref err) => err.description(),
|
||||
Error::JsonDecodeError(ref err) => err.description(),
|
||||
Error::JsonDecodeError(_, ref err) => err.description(),
|
||||
_ => "NO DESCRIPTION POSSIBLE - use `Display.fmt()` instead"
|
||||
}
|
||||
}
|
||||
@@ -286,7 +316,7 @@ impl error::Error for Error {
|
||||
fn cause(&self) -> Option<&error::Error> {
|
||||
match *self {
|
||||
Error::HttpError(ref err) => err.cause(),
|
||||
Error::JsonDecodeError(ref err) => err.cause(),
|
||||
Error::JsonDecodeError(_, ref err) => err.cause(),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
@@ -557,7 +587,7 @@ impl<'a, A> ResumableUploadHelper<'a, A>
|
||||
let h: &RangeResponseHeader = match headers.get() {
|
||||
Some(hh) if r.status == StatusCode::PermanentRedirect => hh,
|
||||
None|Some(_) => {
|
||||
if let Retry::After(d) = self.delegate.http_failure(&r, None) {
|
||||
if let Retry::After(d) = self.delegate.http_failure(&r, None, None) {
|
||||
sleep_ms(d.num_milliseconds() as u32);
|
||||
continue;
|
||||
}
|
||||
@@ -624,7 +654,9 @@ impl<'a, A> ResumableUploadHelper<'a, A>
|
||||
if !res.status.is_success() {
|
||||
let mut json_err = String::new();
|
||||
res.read_to_string(&mut json_err).unwrap();
|
||||
if let Retry::After(d) = self.delegate.http_failure(&res, serde::json::from_str(&json_err).ok()) {
|
||||
if let Retry::After(d) = self.delegate.http_failure(&res,
|
||||
serde::json::from_str(&json_err).ok(),
|
||||
serde::json::from_str(&json_err).ok()) {
|
||||
sleep_ms(d.num_milliseconds() as u32);
|
||||
continue;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user