prepare release of version 2.0, regen all APIs

This commit is contained in:
Sebastian Thiel
2021-04-01 23:46:26 +08:00
parent f1c05f2554
commit c606d37dd3
3415 changed files with 3620084 additions and 2930380 deletions

View File

@@ -0,0 +1,40 @@
# DO NOT EDIT !
# This file was generated automatically from 'src/mako/Cargo.toml.mako'
# DO NOT EDIT !
[package]
name = "google-metastore1_beta-cli"
version = "2.0.0+20210325"
authors = ["Sebastian Thiel <byronimo@gmail.com>"]
description = "A complete library to interact with Dataproc Metastore (protocol v1beta)"
repository = "https://github.com/Byron/google-apis-rs/tree/master/gen/metastore1_beta-cli"
homepage = "https://cloud.google.com/dataproc-metastore/docs"
documentation = "http://byron.github.io/google-apis-rs/google_metastore1_beta_cli"
license = "MIT"
keywords = ["metastore", "google", "cli"]
autobins = false
edition = "2018"
[[bin]]
name = "metastore1-beta"
path = "src/main.rs"
[dependencies]
hyper-rustls = "^0.22"
mime = "^ 0.2.0"
serde = "^ 1.0"
serde_json = "^ 1.0"
serde_derive = "^ 1.0"
yup-oauth2 = "^ 5.0"
itertools = "^ 0.10"
strsim = "^0.5"
clap = "^2.0"
hyper = { version = "0.14", features = ["full"] }
tokio = { version = "^ 1.0", features = ["full"] }
[dependencies.google-metastore1_beta]
path = "../metastore1_beta"
version = "2.0.0+20210325"

View 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-2020` `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.

View File

@@ -0,0 +1,127 @@
<!---
DO NOT EDIT !
This file was generated automatically from 'src/mako/cli/README.md.mako'
DO NOT EDIT !
-->
The `metastore1-beta` command-line interface *(CLI)* allows to use most features of the *Google Dataproc Metastore* service from the comfort of your terminal.
By default all output is printed to standard out, but flags can be set to direct it into a file independent of your shell's
capabilities. Errors will be printed to standard error, and cause the program's exit code to be non-zero.
If data-structures are requested, these will be returned as pretty-printed JSON, to be useful as input to other tools.
Everything else about the *Dataproc Metastore* API can be found at the
[official documentation site](https://cloud.google.com/dataproc-metastore/docs).
# Installation and Source Code
Install the command-line interface with cargo using:
```bash
cargo install google-metastore1_beta-cli
```
Find the source code [on github](https://github.com/Byron/google-apis-rs/tree/master/gen/metastore1_beta-cli).
# Usage
This documentation was generated from the *Dataproc Metastore* API at revision *20210325*. The CLI is at version *2.0.0*.
```bash
metastore1-beta [options]
projects
locations-get <name> [-p <v>]... [-o <out>]
locations-list <name> [-p <v>]... [-o <out>]
locations-operations-delete <name> [-p <v>]... [-o <out>]
locations-operations-get <name> [-p <v>]... [-o <out>]
locations-operations-list <name> [-p <v>]... [-o <out>]
locations-services-backups-create <parent> (-r <kv>)... [-p <v>]... [-o <out>]
locations-services-backups-delete <name> [-p <v>]... [-o <out>]
locations-services-backups-get <name> [-p <v>]... [-o <out>]
locations-services-backups-list <parent> [-p <v>]... [-o <out>]
locations-services-create <parent> (-r <kv>)... [-p <v>]... [-o <out>]
locations-services-delete <name> [-p <v>]... [-o <out>]
locations-services-export-metadata <service> (-r <kv>)... [-p <v>]... [-o <out>]
locations-services-get <name> [-p <v>]... [-o <out>]
locations-services-get-iam-policy <resource> [-p <v>]... [-o <out>]
locations-services-list <parent> [-p <v>]... [-o <out>]
locations-services-metadata-imports-create <parent> (-r <kv>)... [-p <v>]... [-o <out>]
locations-services-metadata-imports-get <name> [-p <v>]... [-o <out>]
locations-services-metadata-imports-list <parent> [-p <v>]... [-o <out>]
locations-services-metadata-imports-patch <name> (-r <kv>)... [-p <v>]... [-o <out>]
locations-services-patch <name> (-r <kv>)... [-p <v>]... [-o <out>]
locations-services-restore <service> (-r <kv>)... [-p <v>]... [-o <out>]
locations-services-set-iam-policy <resource> (-r <kv>)... [-p <v>]... [-o <out>]
locations-services-test-iam-permissions <resource> (-r <kv>)... [-p <v>]... [-o <out>]
metastore1-beta --help
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]
```
# Configuration
The program will store all persistent data in the `~/.google-service-cli` directory in *JSON* files prefixed with `metastore1-beta-`. You can change the directory used to store configuration with the `--config-dir` flag on a per-invocation basis.
More information about the various kinds of persistent data are given in the following paragraphs.
# Authentication
Most APIs require a user to authenticate any request. If this is the case, the [scope][scopes] determines the
set of permissions granted. The granularity of these is usually no more than *read-only* or *full-access*.
If not set, the system will automatically select the smallest feasible scope, e.g. when invoking a
method that is read-only, it will ask only for a read-only scope.
You may use the `--scope` flag to specify a scope directly.
All applicable scopes are documented in the respective method's CLI documentation.
The first time a scope is used, the user is asked for permission. Follow the instructions given
by the CLI to grant permissions, or to decline.
If a scope was authenticated by the user, the respective information will be stored as *JSON* in the configuration
directory, e.g. `~/.google-service-cli/metastore1-beta-token-<scope-hash>.json`. No manual management of these tokens
is necessary.
To revoke granted authentication, please refer to the [official documentation][revoke-access].
# Application Secrets
In order to allow any application to use Google services, it will need to be registered using the
[Google Developer Console][google-dev-console]. APIs the application may use are then enabled for it
one by one. Most APIs can be used for free and have a daily quota.
To allow more comfortable usage of the CLI without forcing anyone to register an own application, the CLI
comes with a default application secret that is configured accordingly. This also means that heavy usage
all around the world may deplete the daily quota.
You can workaround this limitation by putting your own secrets file at this location:
`~/.google-service-cli/metastore1-beta-secret.json`, assuming that the required *metastore* API
was enabled for it. Such a secret file can be downloaded in the *Google Developer Console* at
*APIs & auth -> Credentials -> Download JSON* and used as is.
Learn more about how to setup Google projects and enable APIs using the [official documentation][google-project-new].
# Debugging
Even though the CLI does its best to provide usable error messages, sometimes it might be desirable to know
what exactly led to a particular issue. This is done by allowing all client-server communication to be
output to standard error *as-is*.
The `--debug` flag will print errors using the `Debug` representation to standard error.
You may consider redirecting standard error into a file for ease of use, e.g. `metastore1-beta --debug <resource> <method> [options] 2>debug.txt`.
[scopes]: https://developers.google.com/+/api/oauth#scopes
[revoke-access]: http://webapps.stackexchange.com/a/30849
[google-dev-console]: https://console.developers.google.com/
[google-project-new]: https://developers.google.com/console/help/new/

View File

@@ -0,0 +1,39 @@
site_name: Dataproc Metastore v2.0.0+20210325
site_url: http://byron.github.io/google-apis-rs/google-metastore1_beta-cli
site_description: A complete library to interact with Dataproc Metastore (protocol v1beta)
repo_url: https://github.com/Byron/google-apis-rs/tree/master/gen/metastore1_beta-cli
docs_dir: docs
site_dir: build_html
pages:
- ['index.md', 'Home']
- ['projects_locations-get.md', 'Projects', 'Locations Get']
- ['projects_locations-list.md', 'Projects', 'Locations List']
- ['projects_locations-operations-delete.md', 'Projects', 'Locations Operations Delete']
- ['projects_locations-operations-get.md', 'Projects', 'Locations Operations Get']
- ['projects_locations-operations-list.md', 'Projects', 'Locations Operations List']
- ['projects_locations-services-backups-create.md', 'Projects', 'Locations Services Backups Create']
- ['projects_locations-services-backups-delete.md', 'Projects', 'Locations Services Backups Delete']
- ['projects_locations-services-backups-get.md', 'Projects', 'Locations Services Backups Get']
- ['projects_locations-services-backups-list.md', 'Projects', 'Locations Services Backups List']
- ['projects_locations-services-create.md', 'Projects', 'Locations Services Create']
- ['projects_locations-services-delete.md', 'Projects', 'Locations Services Delete']
- ['projects_locations-services-export-metadata.md', 'Projects', 'Locations Services Export Metadata']
- ['projects_locations-services-get.md', 'Projects', 'Locations Services Get']
- ['projects_locations-services-get-iam-policy.md', 'Projects', 'Locations Services Get Iam Policy']
- ['projects_locations-services-list.md', 'Projects', 'Locations Services List']
- ['projects_locations-services-metadata-imports-create.md', 'Projects', 'Locations Services Metadata Imports Create']
- ['projects_locations-services-metadata-imports-get.md', 'Projects', 'Locations Services Metadata Imports Get']
- ['projects_locations-services-metadata-imports-list.md', 'Projects', 'Locations Services Metadata Imports List']
- ['projects_locations-services-metadata-imports-patch.md', 'Projects', 'Locations Services Metadata Imports Patch']
- ['projects_locations-services-patch.md', 'Projects', 'Locations Services Patch']
- ['projects_locations-services-restore.md', 'Projects', 'Locations Services Restore']
- ['projects_locations-services-set-iam-policy.md', 'Projects', 'Locations Services Set Iam Policy']
- ['projects_locations-services-test-iam-permissions.md', 'Projects', 'Locations Services Test Iam Permissions']
theme: readthedocs
copyright: Copyright &copy; 2015-2020, `Sebastian Thiel`

View File

@@ -0,0 +1,748 @@
// COPY OF 'src/rust/cli/client.rs'
// DO NOT EDIT
use clap::{App, SubCommand};
use mime::Mime;
use oauth2::{ApplicationSecret, ConsoleApplicationSecret};
use serde_json as json;
use serde_json::value::Value;
use strsim;
use std::env;
use std::error::Error as StdError;
use std::fmt;
use std::fs;
use std::io;
use std::io::{stdout, Read, Write};
use std::path::{Path, PathBuf};
use std::str::FromStr;
use std::string::ToString;
use std::default::Default;
const FIELD_SEP: char = '.';
pub enum ComplexType {
Pod,
Vec,
Map,
}
// Null,
// Bool(bool),
// I64(i64),
// U64(u64),
// F64(f64),
// String(String),
pub enum JsonType {
Boolean,
Int,
Uint,
Float,
String,
}
pub struct JsonTypeInfo {
pub jtype: JsonType,
pub ctype: ComplexType,
}
// Based on @erickt user comment. Thanks for the idea !
// Remove all keys whose values are null from given value (changed in place)
pub fn remove_json_null_values(value: &mut Value) {
match *value {
Value::Object(ref mut map) => {
let mut for_removal = Vec::new();
for (key, mut value) in map.iter_mut() {
if value.is_null() {
for_removal.push(key.clone());
} else {
remove_json_null_values(&mut value);
}
}
for key in &for_removal {
map.remove(key);
}
}
json::value::Value::Array(ref mut arr) => {
let mut i = 0;
while i < arr.len() {
if arr[i].is_null() {
arr.remove(i);
} else {
remove_json_null_values(&mut arr[i]);
i += 1;
}
}
}
_ => {}
}
}
fn did_you_mean<'a>(v: &str, possible_values: &[&'a str]) -> Option<&'a str> {
let mut candidate: Option<(f64, &str)> = None;
for pv in possible_values {
let confidence = strsim::jaro_winkler(v, pv);
if confidence > 0.8 && (candidate.is_none() || (candidate.as_ref().unwrap().0 < confidence))
{
candidate = Some((confidence, pv));
}
}
match candidate {
None => None,
Some((_, candidate)) => Some(candidate),
}
}
pub enum CallType {
Upload(UploadProtocol),
Standard,
}
arg_enum! {
pub enum UploadProtocol {
Simple,
// Resumable // This seems to be lost during the async conversion
}
}
impl AsRef<str> for UploadProtocol {
fn as_ref(&self) -> &str {
match *self {
UploadProtocol::Simple => "simple",
// UploadProtocol::Resumable => "resumable",
}
}
}
impl AsRef<str> for CallType {
fn as_ref(&self) -> &str {
match *self {
CallType::Upload(ref proto) => proto.as_ref(),
CallType::Standard => "standard-request",
}
}
}
#[derive(Clone, Default)]
pub struct FieldCursor(Vec<String>);
impl ToString for FieldCursor {
fn to_string(&self) -> String {
self.0.join(".")
}
}
impl From<&'static str> for FieldCursor {
fn from(value: &'static str) -> FieldCursor {
let mut res = FieldCursor::default();
res.set(value).unwrap();
res
}
}
fn assure_entry<'a, 'b>(m: &'a mut json::Map<String, Value>, k: &'b String) -> &'a mut Value {
if m.contains_key(k) {
return m.get_mut(k).expect("value to exist");
}
m.insert(k.to_owned(), Value::Object(Default::default()));
m.get_mut(k).expect("value to exist")
}
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 did_you_mean(value: &str, possible_values: &[&str]) -> Option<String> {
if value.len() == 0 {
return None;
}
let mut last_c = FIELD_SEP;
let mut field = String::new();
let mut output = String::new();
let push_field = |fs: &mut String, f: &mut String| {
if f.len() > 0 {
fs.push_str(match did_you_mean(&f, possible_values) {
Some(candidate) => candidate,
None => &f,
});
f.truncate(0);
}
};
for (cid, c) in value.chars().enumerate() {
if c == FIELD_SEP {
if last_c != FIELD_SEP {
push_field(&mut output, &mut field);
}
output.push(c);
} else {
field.push(c);
}
last_c = c;
}
push_field(&mut output, &mut field);
if &output == value {
None
} else {
Some(output)
}
}
pub fn set_json_value(
&self,
mut object: &mut Value,
value: &str,
type_info: JsonTypeInfo,
err: &mut InvalidOptionsError,
orig_cursor: &FieldCursor,
) {
assert!(self.0.len() > 0);
for field in &self.0[..self.0.len() - 1] {
let tmp = object;
object = match *tmp {
Value::Object(ref mut mapping) => assure_entry(mapping, &field),
_ => panic!("We don't expect non-object Values here ..."),
};
}
match *object {
Value::Object(ref mut mapping) => {
let field = &self.0[self.0.len() - 1];
let to_jval =
|value: &str, jtype: JsonType, err: &mut InvalidOptionsError| -> Value {
match jtype {
JsonType::Boolean => {
Value::Bool(arg_from_str(value, err, &field, "boolean"))
}
JsonType::Int => Value::Number(
json::Number::from_f64(arg_from_str(value, err, &field, "int"))
.expect("valid f64"),
),
JsonType::Uint => Value::Number(
json::Number::from_f64(arg_from_str(value, err, &field, "uint"))
.expect("valid f64"),
),
JsonType::Float => Value::Number(
json::Number::from_f64(arg_from_str(value, err, &field, "float"))
.expect("valid f64"),
),
JsonType::String => Value::String(value.to_owned()),
}
};
match type_info.ctype {
ComplexType::Pod => {
if mapping
.insert(field.to_owned(), to_jval(value, type_info.jtype, err))
.is_some()
{
err.issues.push(CLIError::Field(FieldError::Duplicate(
orig_cursor.to_string(),
)));
}
}
ComplexType::Vec => match *assure_entry(mapping, field) {
Value::Array(ref mut values) => {
values.push(to_jval(value, type_info.jtype, err))
}
_ => unreachable!(),
},
ComplexType::Map => {
let (key, value) = parse_kv_arg(value, err, true);
let jval = to_jval(value.unwrap_or(""), type_info.jtype, err);
match *assure_entry(mapping, &field) {
Value::Object(ref mut value_map) => {
if value_map.insert(key.to_owned(), jval).is_some() {
err.issues.push(CLIError::Field(FieldError::Duplicate(
orig_cursor.to_string(),
)));
}
}
_ => unreachable!(),
}
}
}
}
_ => unreachable!(),
}
}
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, Some(""));
}
(key, Some(&kv[pos + 1..]))
}
}
}
pub fn calltype_from_str(
name: &str,
valid_protocols: Vec<String>,
err: &mut InvalidOptionsError,
) -> CallType {
CallType::Upload(match UploadProtocol::from_str(name) {
Ok(up) => up,
Err(msg) => {
err.issues.push(CLIError::InvalidUploadProtocol(
name.to_string(),
valid_protocols,
));
UploadProtocol::Simple
}
})
}
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::Io((
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
}
}
}
pub fn writer_from_opts(arg: Option<&str>) -> Result<Box<dyn Write>, io::Error> {
let f = arg.unwrap_or("-");
match f {
"-" => Ok(Box::new(stdout())),
_ => match fs::OpenOptions::new()
.create(true)
.truncate(true)
.write(true)
.open(f)
{
Ok(f) => Ok(Box::new(f)),
Err(io_err) => Err(io_err),
},
}
}
pub fn arg_from_str<'a, T>(
arg: &str,
err: &mut InvalidOptionsError,
arg_name: &'a str,
arg_type: &'a 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.to_owned(),
arg_type.to_owned(),
arg.to_string(),
format!("{}", perr),
));
Default::default()
}
Ok(v) => v,
}
}
#[derive(Debug)]
pub enum ApplicationSecretError {
DecoderError((String, json::Error)),
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),
Io((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::Io((ref path, ref err)) => writeln!(
f,
"IO operation failed on path '{}' with error: {}.",
path, err
),
}
}
}
#[derive(Debug)]
pub enum InputError {
Io((String, io::Error)),
Mime(String),
}
impl fmt::Display for InputError {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
match *self {
InputError::Io((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, Option<String>, Option<String>),
Duplicate(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, ref suggestion, ref value) => {
let suffix = match *suggestion {
Some(ref s) => {
let kv = match *value {
Some(ref v) => format!("{}={}", s, v),
None => s.clone(),
};
format!(" Did you mean '{}' ?", kv)
}
None => String::new(),
};
writeln!(f, "Field '{}' does not exist.{}", field, suffix)
}
FieldError::Duplicate(ref cursor) => {
writeln!(f, "Value at '{}' was already set", cursor)
}
FieldError::Empty => writeln!(f, "Field names must not be empty."),
}
}
}
#[derive(Debug)]
pub enum CLIError {
Configuration(ConfigurationError),
ParseError(String, String, String, String),
UnknownParameter(String, Vec<&'static str>),
InvalidUploadProtocol(String, Vec<String>),
InvalidKeyValueSyntax(String, bool),
Input(InputError),
Field(FieldError),
MissingCommandError,
MissingMethodError(String),
}
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::InvalidUploadProtocol(ref proto_name, ref valid_names) => writeln!(
f,
"'{}' is not a valid upload protocol. Choose from one of {}.",
proto_name,
valid_names.join(", ")
),
CLIError::ParseError(ref arg_name, ref 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, ref possible_values) => {
let suffix = match did_you_mean(param_name, &possible_values) {
Some(v) => format!(" Did you mean '{}' ?", v),
None => String::new(),
};
write!(f, "Parameter '{}' is unknown.{}\n", param_name, suffix)
}
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
)
}
CLIError::MissingCommandError => writeln!(f, "Please specify the main sub-command."),
CLIError::MissingMethodError(ref cmd) => writeln!(
f,
"Please specify the method to call on the '{}' command.",
cmd
),
}
}
}
#[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 {
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_console_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::Io((
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)
.truncate(true)
.open(&secret_path)
{
Err(cfe) => cfe,
Ok(mut f) => {
// Assure we convert 'ugly' json string into pretty one
let console_secret: ConsoleApplicationSecret =
json::from_str(json_console_secret).unwrap();
match json::to_writer_pretty(&mut f, &console_secret) {
Err(serde_err) => {
panic!("Unexpected serde error: {:#?}", serde_err)
}
Ok(_) => continue,
}
}
};
// fall through to IO error handling
}
return secret_io_error(err);
}
Ok(f) => match json::de::from_reader::<_, ConsoleApplicationSecret>(f) {
Err(json_err) => {
return Err(CLIError::Configuration(ConfigurationError::Secret(
ApplicationSecretError::DecoderError((secret_str(), json_err)),
)))
}
Ok(console_secret) => match console_secret.installed {
Some(secret) => return Ok(secret),
None => {
return Err(CLIError::Configuration(ConfigurationError::Secret(
ApplicationSecretError::FormatError(secret_str()),
)))
}
},
},
}
}
unreachable!();
}

File diff suppressed because it is too large Load Diff