rust-server.bin-cli.mustache Maven / Gradle / Ivy
//! CLI tool driving the API client
use anyhow::{anyhow, Context, Result};
{{#apiHasDeleteMethods}}
use dialoguer::Confirm;
{{/apiHasDeleteMethods}}
use log::{debug, info};
// models may be unused if all inputs are primitive types
#[allow(unused_imports)]
use {{{externCrateName}}}::{
models, ApiNoContext, Client, ContextWrapperExt,
{{#apiInfo}}
{{#apis}}
{{#operations}}
{{#operation}}
{{{operationId}}}Response,
{{/operation}}
{{/operations}}
{{/apis}}
{{/apiInfo}}
};
use simple_logger::SimpleLogger;
use structopt::StructOpt;
use swagger::{AuthData, ContextBuilder, EmptyContext, Push, XSpanIdString};
type ClientContext = swagger::make_context_ty!(
ContextBuilder,
EmptyContext,
Option,
XSpanIdString
);
{{! See code in RustServerCodegen if you are adding additional short option usage here. }}
#[derive(StructOpt, Debug)]
#[structopt(
name = "{{appName}}",
version = "{{version}}",
about = "CLI access to {{appName}}"
)]
struct Cli {
#[structopt(subcommand)]
operation: Operation,
/// Address or hostname of the server hosting this API, including optional port
#[structopt(short = "a", long, default_value = "http://localhost")]
server_address: String,
/// Path to the client private key if using client-side TLS authentication
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
#[structopt(long, requires_all(&["client-certificate", "server-certificate"]))]
client_key: Option,
/// Path to the client's public certificate associated with the private key
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
#[structopt(long, requires_all(&["client-key", "server-certificate"]))]
client_certificate: Option,
/// Path to CA certificate used to authenticate the server
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
#[structopt(long)]
server_certificate: Option,
/// If set, write output to file instead of stdout
#[structopt(short, long)]
output_file: Option,
#[structopt(flatten)]
verbosity: clap_verbosity_flag::Verbosity,
{{#apiHasDeleteMethods}}
/// Don't ask for any confirmation prompts
#[allow(dead_code)]
#[structopt(short, long)]
force: bool,
{{/apiHasDeleteMethods}}
{{#hasHttpBearerMethods}}
/// Bearer token if used for authentication
#[structopt(env = "{{#lambda.uppercase}}{{externCrateName}}{{/lambda.uppercase}}_BEARER_TOKEN", hide_env_values = true)]
bearer_token: Option,
{{/hasHttpBearerMethods}}
{{^hasHttpBearerMethods}}
{{#hasOAuthMethods}}
/// Bearer token if used for authentication
#[structopt(env = "{{#lambda.uppercase}}{{externCrateName}}{{/lambda.uppercase}}_BEARER_TOKEN", hide_env_values = true)]
bearer_token: Option,
{{/hasOAuthMethods}}
{{/hasHttpBearerMethods}}
}
#[derive(StructOpt, Debug)]
enum Operation {
{{#apiInfo}}
{{#apis}}
{{#operations}}
{{#operation}}
{{#summary}}
/// {{{summary}}}
{{/summary}}
{{operationId}} {
{{#allParams}}
{{#description}}
/// {{{description}}}
{{/description}}
{{^isPrimitiveType}}
#[structopt(parse(try_from_str = parse_json){{#isArray}}, long{{/isArray}})]
{{/isPrimitiveType}}
{{#isByteArray}}
#[structopt(parse(try_from_str = parse_json))]
{{/isByteArray}}
{{#isBinary}}
#[structopt(parse(try_from_str = parse_json))]
{{/isBinary}}
{{#isBoolean}}
{{#isPrimitiveType}}
{{#vendorExtensions.x-provide-cli-short-opt}}
#[structopt(short, long)]
{{/vendorExtensions.x-provide-cli-short-opt}}
{{^vendorExtensions.x-provide-cli-short-opt}}
#[structopt(long)]
{{/vendorExtensions.x-provide-cli-short-opt}}
{{/isPrimitiveType}}
{{/isBoolean}}
{{{paramName}}}: {{^required}}Option<{{/required}}{{{dataType}}}{{^required}}>{{/required}},
{{/allParams}}
},
{{/operation}}
{{/operations}}
{{/apis}}
{{/apiInfo}}
}
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
fn create_client(args: &Cli, context: ClientContext) -> Result>> {
if args.client_certificate.is_some() {
debug!("Using mutual TLS");
let client = Client::try_new_https_mutual(
&args.server_address,
args.server_certificate.clone().unwrap(),
args.client_key.clone().unwrap(),
args.client_certificate.clone().unwrap(),
)
.context("Failed to create HTTPS client")?;
Ok(Box::new(client.with_context(context)))
} else if args.server_certificate.is_some() {
debug!("Using TLS with pinned server certificate");
let client =
Client::try_new_https_pinned(&args.server_address, args.server_certificate.clone().unwrap())
.context("Failed to create HTTPS client")?;
Ok(Box::new(client.with_context(context)))
} else {
debug!("Using client without certificates");
let client =
Client::try_new(&args.server_address).context("Failed to create HTTP(S) client")?;
Ok(Box::new(client.with_context(context)))
}
}
#[cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))]
fn create_client(args: &Cli, context: ClientContext) -> Result>> {
let client =
Client::try_new(&args.server_address).context("Failed to create HTTP(S) client")?;
Ok(Box::new(client.with_context(context)))
}
#[tokio::main]
async fn main() -> Result<()> {
let args = Cli::from_args();
if let Some(log_level) = args.verbosity.log_level() {
SimpleLogger::new().with_level(log_level.to_level_filter()).init()?;
}
debug!("Arguments: {:?}", &args);
{{#hasHttpBearerMethods}}
let mut auth_data: Option = None;
if let Some(ref bearer_token) = args.bearer_token {
debug!("Using bearer token");
auth_data = Some(AuthData::bearer(bearer_token));
}
{{/hasHttpBearerMethods}}
{{^hasHttpBearerMethods}}
{{#hasOAuthMethods}}
let mut auth_data: Option = None;
if let Some(ref bearer_token) = args.bearer_token {
debug!("Using bearer token");
auth_data = Some(AuthData::bearer(bearer_token));
}
{{/hasOAuthMethods}}
{{/hasHttpBearerMethods}}
{{^hasHttpBearerMethods}}
{{^hasOAuthMethods}}
let auth_data: Option = None;
{{/hasOAuthMethods}}
{{/hasHttpBearerMethods}}
#[allow(trivial_casts)]
let context = swagger::make_context!(
ContextBuilder,
EmptyContext,
auth_data,
XSpanIdString::default()
);
let client = create_client(&args, context)?;
let result = match args.operation {
{{#apiInfo}}
{{#apis}}
{{#operations}}
{{#operation}}
Operation::{{operationId}} {
{{#allParams}}
{{paramName}},
{{/allParams}}
} => {
{{#vendorExtensions.x-is-delete}}
prompt(args.force, "This will delete the given entry, are you sure?")?;
{{/vendorExtensions.x-is-delete}}
info!("Performing a {{operationId}} request{{^pathParams}}");{{/pathParams}}{{#pathParams}}{{#-first}} on {:?}", ({{/-first}}{{/pathParams}}
{{#pathParams}}
&{{paramName}}{{^-last}},{{/-last}}
{{#-last}}
));
{{/-last}}
{{/pathParams}}
let result = client.{{{vendorExtensions.x-operation-id}}}(
{{#allParams}}
{{{paramName}}}{{#isArray}}.as_ref(){{/isArray}},
{{/allParams}}
).await?;
debug!("Result: {:?}", result);
match result {
{{#responses}}
{{{operationId}}}Response::{{{vendorExtensions.x-response-id}}}
{{#dataType}}
{{^hasHeaders}}
(body)
{{/hasHeaders}}
{{#hasHeaders}}
{
body,
{{/hasHeaders}}
{{/dataType}}
{{^dataType}}
{{#hasHeaders}}
{
{{/hasHeaders}}
{{/dataType}}
{{#headers}}
{{{name}}},
{{#-last}}
}
{{/-last}}
{{/headers}}
=> "{{{vendorExtensions.x-response-id}}}\n".to_string()
{{#dataType}}
+
{{/dataType}}
{{^dataType}}
{{#hasHeaders}}
+
{{/hasHeaders}}
{{^hasHeaders}}
,
{{/hasHeaders}}
{{/dataType}}
{{#dataType}}
{{^hasHeaders}}
&serde_json::to_string_pretty(&body)?,
{{/hasHeaders}}
{{#hasHeaders}}
&format!("body: {}\n", serde_json::to_string_pretty(&body)?) +
{{/hasHeaders}}
{{/dataType}}
{{#headers}}
&format!(
"{{{name}}}: {}\n",
serde_json::to_string_pretty(&{{{name}}})?
){{^-last}} +{{/-last}}{{#-last}},{{/-last}}
{{/headers}}
{{/responses}}
}
}
{{/operation}}
{{/operations}}
{{/apis}}
{{/apiInfo}}
};
if let Some(output_file) = args.output_file {
std::fs::write(output_file, result)?
} else {
println!("{}", result);
}
Ok(())
}
{{#apiHasDeleteMethods}}
fn prompt(force: bool, text: &str) -> Result<()> {
if force || Confirm::new().with_prompt(text).interact()? {
Ok(())
} else {
Err(anyhow!("Aborting"))
}
}
{{/apiHasDeleteMethods}}
// May be unused if all inputs are primitive types
#[allow(dead_code)]
fn parse_json<'a, T: serde::de::Deserialize<'a>>(json_string: &'a str) -> Result {
serde_json::from_str(json_string).map_err(|err| anyhow!("Error parsing input: {}", err))
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy