CLI generation prototype (#349)
This commit is contained in:
parent
ca5f0674cf
commit
10dc4cafc6
|
@ -2103,7 +2103,7 @@ checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987"
|
|||
[[package]]
|
||||
name = "typify"
|
||||
version = "0.0.12-dev"
|
||||
source = "git+https://github.com/oxidecomputer/typify#3cda314def8e95043f45c172cfb8851e391ef7ad"
|
||||
source = "git+https://github.com/oxidecomputer/typify#6d77f63b3dc5312cd36549507f941cb5d783600e"
|
||||
dependencies = [
|
||||
"typify-impl",
|
||||
"typify-macro",
|
||||
|
@ -2112,7 +2112,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "typify-impl"
|
||||
version = "0.0.12-dev"
|
||||
source = "git+https://github.com/oxidecomputer/typify#3cda314def8e95043f45c172cfb8851e391ef7ad"
|
||||
source = "git+https://github.com/oxidecomputer/typify#6d77f63b3dc5312cd36549507f941cb5d783600e"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"log",
|
||||
|
@ -2129,7 +2129,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "typify-macro"
|
||||
version = "0.0.12-dev"
|
||||
source = "git+https://github.com/oxidecomputer/typify#3cda314def8e95043f45c172cfb8851e391ef7ad"
|
||||
source = "git+https://github.com/oxidecomputer/typify#6d77f63b3dc5312cd36549507f941cb5d783600e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
|
|
@ -32,3 +32,4 @@ expectorate = "1.0"
|
|||
http = "0.2.9"
|
||||
hyper = "0.14.25"
|
||||
serde_yaml = "0.9"
|
||||
serde_json = "1.0.91"
|
||||
|
|
|
@ -0,0 +1,538 @@
|
|||
// Copyright 2023 Oxide Computer Company
|
||||
|
||||
use heck::ToKebabCase;
|
||||
use openapiv3::OpenAPI;
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::{format_ident, quote};
|
||||
use typify::{TypeSpaceImpl, TypeStructPropInfo};
|
||||
|
||||
use crate::{
|
||||
method::{
|
||||
OperationParameterKind, OperationParameterType, OperationResponseStatus,
|
||||
},
|
||||
space_out_items,
|
||||
to_schema::ToSchema,
|
||||
util::{sanitize, Case},
|
||||
validate_openapi, Generator, Result,
|
||||
};
|
||||
|
||||
struct CliOperation {
|
||||
cli_fn: TokenStream,
|
||||
execute_fn: TokenStream,
|
||||
cli_trait: TokenStream,
|
||||
execute_trait: TokenStream,
|
||||
}
|
||||
|
||||
impl Generator {
|
||||
pub fn cli_text(
|
||||
&mut self,
|
||||
spec: &OpenAPI,
|
||||
crate_name: &str,
|
||||
) -> Result<String> {
|
||||
let output = self.cli(spec, crate_name)?;
|
||||
|
||||
let content = rustfmt_wrapper::rustfmt_config(
|
||||
rustfmt_wrapper::config::Config {
|
||||
format_strings: Some(true),
|
||||
..Default::default()
|
||||
},
|
||||
output,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
space_out_items(content)
|
||||
}
|
||||
|
||||
pub fn cli(
|
||||
&mut self,
|
||||
spec: &OpenAPI,
|
||||
crate_name: &str,
|
||||
) -> Result<TokenStream> {
|
||||
validate_openapi(spec)?;
|
||||
|
||||
// Convert our components dictionary to schemars
|
||||
let schemas = spec.components.iter().flat_map(|components| {
|
||||
components.schemas.iter().map(|(name, ref_or_schema)| {
|
||||
(name.clone(), ref_or_schema.to_schema())
|
||||
})
|
||||
});
|
||||
|
||||
self.type_space.add_ref_types(schemas)?;
|
||||
|
||||
let raw_methods = spec
|
||||
.paths
|
||||
.iter()
|
||||
.flat_map(|(path, ref_or_item)| {
|
||||
// Exclude externally defined path items.
|
||||
let item = ref_or_item.as_item().unwrap();
|
||||
item.iter().map(move |(method, operation)| {
|
||||
(path.as_str(), method, operation, &item.parameters)
|
||||
})
|
||||
})
|
||||
.map(|(path, method, operation, path_parameters)| {
|
||||
self.process_operation(
|
||||
operation,
|
||||
&spec.components,
|
||||
path,
|
||||
method,
|
||||
path_parameters,
|
||||
)
|
||||
})
|
||||
.collect::<Result<Vec<_>>>()?;
|
||||
|
||||
let methods = raw_methods
|
||||
.iter()
|
||||
.map(|method| self.cli_method(method))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let ops = methods.iter().map(
|
||||
|CliOperation {
|
||||
cli_fn,
|
||||
execute_fn,
|
||||
cli_trait: _,
|
||||
execute_trait: _,
|
||||
}| {
|
||||
quote! {
|
||||
#cli_fn
|
||||
#execute_fn
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
let trait_ops = methods.iter().map(
|
||||
|CliOperation {
|
||||
cli_fn: _,
|
||||
execute_fn: _,
|
||||
cli_trait,
|
||||
execute_trait,
|
||||
}| {
|
||||
quote! {
|
||||
#cli_trait
|
||||
#execute_trait
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
let cli_fns = raw_methods
|
||||
.iter()
|
||||
.map(|method| {
|
||||
format_ident!(
|
||||
"cli_{}",
|
||||
sanitize(&method.operation_id, Case::Snake)
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let execute_fns = raw_methods
|
||||
.iter()
|
||||
.map(|method| {
|
||||
format_ident!(
|
||||
"execute_{}",
|
||||
sanitize(&method.operation_id, Case::Snake)
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let cli_variants = raw_methods
|
||||
.iter()
|
||||
.map(|method| {
|
||||
format_ident!(
|
||||
"{}",
|
||||
sanitize(&method.operation_id, Case::Pascal)
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let crate_ident = format_ident!("{}", crate_name);
|
||||
|
||||
let code = quote! {
|
||||
pub struct Cli {
|
||||
client: #crate_ident::Client,
|
||||
}
|
||||
impl Cli {
|
||||
pub fn new(client: #crate_ident::Client) -> Self {
|
||||
Self { client }
|
||||
}
|
||||
|
||||
#(#ops)*
|
||||
|
||||
pub fn get_command(cmd: CliCommand) -> clap::Command {
|
||||
match cmd {
|
||||
#(
|
||||
CliCommand::#cli_variants => Self::#cli_fns(),
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn execute(
|
||||
&self,
|
||||
cmd: CliCommand,
|
||||
matches: &clap::ArgMatches,
|
||||
) {
|
||||
let _ = match cmd {
|
||||
#(
|
||||
CliCommand::#cli_variants => {
|
||||
// TODO ... do something with output
|
||||
self.#execute_fns(matches).await;
|
||||
}
|
||||
)*
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
pub trait CliOverride {
|
||||
#(#trait_ops)*
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum CliCommand {
|
||||
#(#cli_variants,)*
|
||||
}
|
||||
|
||||
impl CliCommand {
|
||||
pub fn iter() -> impl Iterator<Item = CliCommand> {
|
||||
vec![
|
||||
#(
|
||||
CliCommand::#cli_variants,
|
||||
)*
|
||||
].into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
Ok(code)
|
||||
}
|
||||
|
||||
fn cli_method(
|
||||
&mut self,
|
||||
method: &crate::method::OperationMethod,
|
||||
) -> CliOperation {
|
||||
let fn_name = format_ident!("cli_{}", &method.operation_id);
|
||||
|
||||
let args = method
|
||||
.params
|
||||
.iter()
|
||||
.filter(|param| {
|
||||
!matches!(¶m.kind, OperationParameterKind::Body(_))
|
||||
&& (method.dropshot_paginated.is_none()
|
||||
|| (param.name.as_str() != "page_token"))
|
||||
})
|
||||
.map(|param| {
|
||||
let arg_name = param.name.to_kebab_case();
|
||||
|
||||
let required = match ¶m.kind {
|
||||
OperationParameterKind::Path => true,
|
||||
OperationParameterKind::Query(required) => *required,
|
||||
OperationParameterKind::Header(required) => *required,
|
||||
OperationParameterKind::Body(_) => unreachable!(),
|
||||
};
|
||||
|
||||
let OperationParameterType::Type(arg_type_id) = ¶m.typ else {
|
||||
panic!()
|
||||
};
|
||||
let arg_type = self.type_space.get_type(arg_type_id).unwrap();
|
||||
let arg_details = arg_type.details();
|
||||
let arg_type_name = match &arg_details{
|
||||
typify::TypeDetails::Option(opt_id) => {
|
||||
let inner_type = self.type_space.get_type(opt_id).unwrap();
|
||||
inner_type.ident()
|
||||
}
|
||||
_ => {
|
||||
arg_type.ident()
|
||||
}
|
||||
};
|
||||
|
||||
let help = param.description.as_ref().map(|description| {
|
||||
quote! {
|
||||
.help(#description)
|
||||
}
|
||||
});
|
||||
|
||||
quote! {
|
||||
clap::Arg::new(#arg_name)
|
||||
.long(#arg_name)
|
||||
.required(#required)
|
||||
.value_parser(clap::value_parser!(#arg_type_name))
|
||||
#help
|
||||
}
|
||||
});
|
||||
|
||||
let maybe_body_param = method.params.iter().find(|param| {
|
||||
matches!(¶m.kind, OperationParameterKind::Body(_))
|
||||
// TODO not sure how to deal with raw bodies right now
|
||||
&& matches!(¶m.typ, OperationParameterType::Type(_))
|
||||
});
|
||||
|
||||
let body_arg = maybe_body_param.map(|param| {
|
||||
let OperationParameterType::Type(type_id) = ¶m.typ else {
|
||||
unreachable!();
|
||||
};
|
||||
|
||||
let body_args = self.type_space.get_type(type_id).unwrap();
|
||||
|
||||
let body_arg = match body_args.details() {
|
||||
typify::TypeDetails::Struct(s) => {
|
||||
s.properties_info()
|
||||
.filter_map(|prop_info| {
|
||||
let TypeStructPropInfo {
|
||||
name: prop_name,
|
||||
description,
|
||||
required,
|
||||
type_id: prop_type_id,
|
||||
} = prop_info;
|
||||
let prop_type = self
|
||||
.type_space
|
||||
.get_type(&prop_type_id)
|
||||
.unwrap();
|
||||
let prop_type_ident = prop_type.ident();
|
||||
let good =
|
||||
prop_type.has_impl(TypeSpaceImpl::FromStr);
|
||||
let prop_name = prop_name.to_kebab_case();
|
||||
// assert!(good || !required);
|
||||
|
||||
good.then(|| {
|
||||
let help =
|
||||
description.as_ref().map(|description| {
|
||||
quote! {
|
||||
.help(#description)
|
||||
}
|
||||
});
|
||||
quote! {
|
||||
clap::Arg::new(#prop_name)
|
||||
.long(#prop_name)
|
||||
.required(#required)
|
||||
.value_parser(clap::value_parser!(
|
||||
#prop_type_ident
|
||||
))
|
||||
#help
|
||||
}
|
||||
})
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
_ => Vec::new(),
|
||||
};
|
||||
|
||||
quote! {
|
||||
#(
|
||||
.arg(#body_arg)
|
||||
)*
|
||||
}
|
||||
});
|
||||
|
||||
// TODO parameter for body as input json (--body-input?)
|
||||
// TODO parameter to output a body template (--body-template?)
|
||||
// TODO deal with all parameters?
|
||||
|
||||
let about = method.summary.as_ref().map(|summary| {
|
||||
let mut about_str = summary.clone();
|
||||
if let Some(description) = &method.description {
|
||||
about_str.push_str("\n\n");
|
||||
about_str.push_str(description);
|
||||
}
|
||||
|
||||
quote! {
|
||||
.about(#about_str)
|
||||
}
|
||||
});
|
||||
|
||||
let cli_fn = quote! {
|
||||
pub fn #fn_name() -> clap::Command
|
||||
{
|
||||
clap::Command::new("")
|
||||
#(
|
||||
.arg(#args)
|
||||
)*
|
||||
#body_arg
|
||||
#about
|
||||
}
|
||||
};
|
||||
|
||||
let cli_trait = quote! {
|
||||
fn #fn_name(cmd: clap::Command) -> clap::Command {
|
||||
cmd
|
||||
}
|
||||
};
|
||||
|
||||
let op_name = format_ident!("{}", &method.operation_id);
|
||||
|
||||
let fn_name = format_ident!("execute_{}", &method.operation_id);
|
||||
|
||||
let args = method
|
||||
.params
|
||||
.iter()
|
||||
.filter(|param| {
|
||||
!matches!(¶m.kind, OperationParameterKind::Body(_))
|
||||
&& (method.dropshot_paginated.is_none()
|
||||
|| (param.name.as_str() != "page_token"))
|
||||
})
|
||||
.map(|param| {
|
||||
let arg_name = param.name.to_kebab_case();
|
||||
let arg_fn_name = sanitize(¶m.name, Case::Snake);
|
||||
let arg_fn = format_ident!("{}", arg_fn_name);
|
||||
let OperationParameterType::Type(arg_type_id) = ¶m.typ else {
|
||||
panic!()
|
||||
};
|
||||
let arg_type = self.type_space.get_type(arg_type_id).unwrap();
|
||||
|
||||
// TODO this really should be simpler.
|
||||
let arg_details = arg_type.details();
|
||||
let arg_type_name = match &arg_details{
|
||||
typify::TypeDetails::Option(opt_id) => {
|
||||
let inner_type = self.type_space.get_type(opt_id).unwrap();
|
||||
inner_type.ident()
|
||||
}
|
||||
_ => {
|
||||
arg_type.ident()
|
||||
}
|
||||
};
|
||||
|
||||
quote! {
|
||||
if let Some(value) =
|
||||
matches.get_one::<#arg_type_name>(#arg_name)
|
||||
{
|
||||
// clone here in case the arg type doesn't impl
|
||||
// From<&T>
|
||||
request = request.#arg_fn(value.clone());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let body_arg = maybe_body_param.map(|param| {
|
||||
let OperationParameterType::Type(type_id) = ¶m.typ else {
|
||||
unreachable!();
|
||||
};
|
||||
|
||||
let body_type = self.type_space.get_type(type_id).unwrap();
|
||||
let body_type_ident = body_type.ident();
|
||||
|
||||
let maybe_body_args = match body_type.details() {
|
||||
typify::TypeDetails::Struct(s) => {
|
||||
let args = s
|
||||
.properties_info()
|
||||
.filter_map(|prop_info| {
|
||||
let TypeStructPropInfo {
|
||||
name: prop_name,
|
||||
description: _,
|
||||
required: _,
|
||||
type_id: body_type_id,
|
||||
} = prop_info;
|
||||
let prop_type = self
|
||||
.type_space
|
||||
.get_type(&body_type_id)
|
||||
.unwrap();
|
||||
let prop_fn = format_ident!(
|
||||
"{}",
|
||||
sanitize(prop_name, Case::Snake)
|
||||
);
|
||||
let prop_name = prop_name.to_kebab_case();
|
||||
let prop_type_ident = prop_type.ident();
|
||||
let good =
|
||||
prop_type.has_impl(TypeSpaceImpl::FromStr);
|
||||
// assert!(good || !required);
|
||||
|
||||
good.then(|| {
|
||||
quote! {
|
||||
if let Some(value) =
|
||||
matches.get_one::<#prop_type_ident>(
|
||||
#prop_name,
|
||||
)
|
||||
{
|
||||
// clone here in case the arg type
|
||||
// doesn't impl From<&T>
|
||||
body = body.#prop_fn(
|
||||
value.clone(),
|
||||
);
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
Some(args)
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
|
||||
maybe_body_args.map(|body_args| {
|
||||
quote! {
|
||||
let request = request.body({
|
||||
let mut body = #body_type_ident::builder();
|
||||
#( #body_args )*
|
||||
body
|
||||
});
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
let (_, success_type) = self.extract_responses(
|
||||
method,
|
||||
OperationResponseStatus::is_success_or_default,
|
||||
);
|
||||
let (_, error_type) = self.extract_responses(
|
||||
method,
|
||||
OperationResponseStatus::is_error_or_default,
|
||||
);
|
||||
|
||||
let success_output = match success_type {
|
||||
crate::method::OperationResponseType::Type(_) => {
|
||||
quote! { println!("success\n{:#?}", r) }
|
||||
}
|
||||
crate::method::OperationResponseType::None => {
|
||||
quote! { println!("success\n{:#?}", r) }
|
||||
}
|
||||
crate::method::OperationResponseType::Raw => quote! { todo!() },
|
||||
crate::method::OperationResponseType::Upgrade => quote! { todo!() },
|
||||
};
|
||||
|
||||
let error_output = match error_type {
|
||||
crate::method::OperationResponseType::Type(_) => {
|
||||
quote! { println!("error\n{:#?}", r) }
|
||||
}
|
||||
crate::method::OperationResponseType::None => {
|
||||
quote! { println!("success\n{:#?}", r) }
|
||||
}
|
||||
crate::method::OperationResponseType::Raw => quote! { todo!() },
|
||||
crate::method::OperationResponseType::Upgrade => quote! { todo!() },
|
||||
};
|
||||
|
||||
let execute_fn = quote! {
|
||||
pub async fn #fn_name(&self, matches: &clap::ArgMatches)
|
||||
// ->
|
||||
// Result<ResponseValue<#success_type>, Error<#error_type>>
|
||||
{
|
||||
let mut request = self.client.#op_name();
|
||||
#( #args )*
|
||||
#body_arg
|
||||
|
||||
let result = request.send().await;
|
||||
|
||||
match result {
|
||||
Ok(r) => {
|
||||
#success_output
|
||||
}
|
||||
Err(r) => {
|
||||
#error_output
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let execute_trait = quote! {
|
||||
fn #fn_name(
|
||||
&self,
|
||||
matches: &clap::ArgMatches,
|
||||
request: &mut (),
|
||||
body: &mut()
|
||||
) -> Result<(), String> {
|
||||
Ok(())
|
||||
}
|
||||
};
|
||||
|
||||
CliOperation {
|
||||
cli_fn,
|
||||
execute_fn,
|
||||
cli_trait,
|
||||
execute_trait,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -14,6 +14,7 @@ use crate::to_schema::ToSchema;
|
|||
pub use typify::TypeSpaceImpl as TypeImpl;
|
||||
pub use typify::TypeSpacePatch as TypePatch;
|
||||
|
||||
mod cli;
|
||||
mod method;
|
||||
mod template;
|
||||
mod to_schema;
|
||||
|
@ -212,15 +213,11 @@ impl Generator {
|
|||
validate_openapi(spec)?;
|
||||
|
||||
// Convert our components dictionary to schemars
|
||||
let schemas = spec
|
||||
.components
|
||||
.iter()
|
||||
.flat_map(|components| {
|
||||
components.schemas.iter().map(|(name, ref_or_schema)| {
|
||||
(name.clone(), ref_or_schema.to_schema())
|
||||
})
|
||||
let schemas = spec.components.iter().flat_map(|components| {
|
||||
components.schemas.iter().map(|(name, ref_or_schema)| {
|
||||
(name.clone(), ref_or_schema.to_schema())
|
||||
})
|
||||
.collect::<Vec<(String, _)>>();
|
||||
});
|
||||
|
||||
self.type_space.add_ref_types(schemas)?;
|
||||
|
||||
|
@ -521,14 +518,7 @@ impl Generator {
|
|||
// Format the file with rustfmt.
|
||||
let content = rustfmt_wrapper::rustfmt_config(config, output).unwrap();
|
||||
|
||||
// Add newlines after end-braces at <= two levels of indentation.
|
||||
Ok(if cfg!(not(windows)) {
|
||||
let regex = regex::Regex::new(r#"(})(\n\s{0,8}[^} ])"#).unwrap();
|
||||
regex.replace_all(&content, "$1\n$2").to_string()
|
||||
} else {
|
||||
let regex = regex::Regex::new(r#"(})(\r\n\s{0,8}[^} ])"#).unwrap();
|
||||
regex.replace_all(&content, "$1\r\n$2").to_string()
|
||||
})
|
||||
space_out_items(content)
|
||||
}
|
||||
|
||||
// TODO deprecate?
|
||||
|
@ -545,7 +535,18 @@ impl Generator {
|
|||
}
|
||||
}
|
||||
|
||||
fn validate_openapi(spec: &OpenAPI) -> Result<()> {
|
||||
pub(crate) fn space_out_items(content: String) -> Result<String> {
|
||||
// Add newlines after end-braces at <= two levels of indentation.
|
||||
Ok(if cfg!(not(windows)) {
|
||||
let regex = regex::Regex::new(r#"(})(\n\s{0,8}[^} ])"#).unwrap();
|
||||
regex.replace_all(&content, "$1\n$2").to_string()
|
||||
} else {
|
||||
let regex = regex::Regex::new(r#"(})(\r\n\s{0,8}[^} ])"#).unwrap();
|
||||
regex.replace_all(&content, "$1\r\n$2").to_string()
|
||||
})
|
||||
}
|
||||
|
||||
pub fn validate_openapi(spec: &OpenAPI) -> Result<()> {
|
||||
match spec.openapi.as_str() {
|
||||
"3.0.0" | "3.0.1" | "3.0.2" | "3.0.3" => (),
|
||||
v => {
|
||||
|
|
|
@ -9,7 +9,7 @@ use std::{
|
|||
use openapiv3::{Components, Parameter, ReferenceOr, Response, StatusCode};
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::{format_ident, quote, ToTokens};
|
||||
use typify::TypeId;
|
||||
use typify::{TypeId, TypeSpace};
|
||||
|
||||
use crate::{
|
||||
template::PathTemplate,
|
||||
|
@ -20,15 +20,15 @@ use crate::{to_schema::ToSchema, util::ReferenceOrExt};
|
|||
|
||||
/// The intermediate representation of an operation that will become a method.
|
||||
pub(crate) struct OperationMethod {
|
||||
operation_id: String,
|
||||
pub operation_id: String,
|
||||
pub tags: Vec<String>,
|
||||
method: HttpMethod,
|
||||
path: PathTemplate,
|
||||
summary: Option<String>,
|
||||
description: Option<String>,
|
||||
params: Vec<OperationParameter>,
|
||||
pub summary: Option<String>,
|
||||
pub description: Option<String>,
|
||||
pub params: Vec<OperationParameter>,
|
||||
responses: Vec<OperationResponse>,
|
||||
dropshot_paginated: Option<DropshotPagination>,
|
||||
pub dropshot_paginated: Option<DropshotPagination>,
|
||||
dropshot_websocket: bool,
|
||||
}
|
||||
|
||||
|
@ -87,28 +87,28 @@ struct BuilderImpl {
|
|||
body: TokenStream,
|
||||
}
|
||||
|
||||
struct DropshotPagination {
|
||||
pub struct DropshotPagination {
|
||||
item: TypeId,
|
||||
}
|
||||
|
||||
struct OperationParameter {
|
||||
pub struct OperationParameter {
|
||||
/// Sanitized parameter name.
|
||||
name: String,
|
||||
pub name: String,
|
||||
/// Original parameter name provided by the API.
|
||||
api_name: String,
|
||||
description: Option<String>,
|
||||
typ: OperationParameterType,
|
||||
kind: OperationParameterKind,
|
||||
pub description: Option<String>,
|
||||
pub typ: OperationParameterType,
|
||||
pub kind: OperationParameterKind,
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq)]
|
||||
enum OperationParameterType {
|
||||
pub enum OperationParameterType {
|
||||
Type(TypeId),
|
||||
RawBody,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
enum OperationParameterKind {
|
||||
pub enum OperationParameterKind {
|
||||
Path,
|
||||
Query(bool),
|
||||
Header(bool),
|
||||
|
@ -116,7 +116,7 @@ enum OperationParameterKind {
|
|||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
enum BodyContentType {
|
||||
pub enum BodyContentType {
|
||||
OctetStream,
|
||||
Json,
|
||||
FormUrlencoded,
|
||||
|
@ -139,7 +139,7 @@ impl FromStr for BodyContentType {
|
|||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct OperationResponse {
|
||||
pub(crate) struct OperationResponse {
|
||||
status_code: OperationResponseStatus,
|
||||
typ: OperationResponseType,
|
||||
// TODO this isn't currently used because dropshot doesn't give us a
|
||||
|
@ -166,7 +166,7 @@ impl PartialOrd for OperationResponse {
|
|||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
enum OperationResponseStatus {
|
||||
pub(crate) enum OperationResponseStatus {
|
||||
Code(u16),
|
||||
Range(u16),
|
||||
Default,
|
||||
|
@ -187,7 +187,7 @@ impl OperationResponseStatus {
|
|||
}
|
||||
}
|
||||
|
||||
fn is_success_or_default(&self) -> bool {
|
||||
pub fn is_success_or_default(&self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
OperationResponseStatus::Default
|
||||
|
@ -197,7 +197,7 @@ impl OperationResponseStatus {
|
|||
)
|
||||
}
|
||||
|
||||
fn is_error_or_default(&self) -> bool {
|
||||
pub fn is_error_or_default(&self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
OperationResponseStatus::Default
|
||||
|
@ -206,7 +206,7 @@ impl OperationResponseStatus {
|
|||
)
|
||||
}
|
||||
|
||||
fn is_default(&self) -> bool {
|
||||
pub fn is_default(&self) -> bool {
|
||||
matches!(self, OperationResponseStatus::Default)
|
||||
}
|
||||
}
|
||||
|
@ -224,13 +224,33 @@ impl PartialOrd for OperationResponseStatus {
|
|||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
|
||||
enum OperationResponseType {
|
||||
pub(crate) enum OperationResponseType {
|
||||
Type(TypeId),
|
||||
None,
|
||||
Raw,
|
||||
Upgrade,
|
||||
}
|
||||
|
||||
impl OperationResponseType {
|
||||
pub fn into_tokens(self, type_space: &TypeSpace) -> TokenStream {
|
||||
match self {
|
||||
OperationResponseType::Type(ref type_id) => {
|
||||
let type_name = type_space.get_type(type_id).unwrap().ident();
|
||||
quote! { #type_name }
|
||||
}
|
||||
OperationResponseType::None => {
|
||||
quote! { () }
|
||||
}
|
||||
OperationResponseType::Raw => {
|
||||
quote! { ByteStream }
|
||||
}
|
||||
OperationResponseType::Upgrade => {
|
||||
quote! { reqwest::Upgraded }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Generator {
|
||||
pub(crate) fn process_operation(
|
||||
&mut self,
|
||||
|
@ -730,6 +750,9 @@ impl Generator {
|
|||
Ok(all)
|
||||
}
|
||||
|
||||
/// Common code generation between positional and builder interface-styles.
|
||||
/// Returns a struct with the success and error types and the core body
|
||||
/// implementation that marshals arguments and executes the request.
|
||||
fn method_sig_body(
|
||||
&self,
|
||||
method: &OperationMethod,
|
||||
|
@ -1070,17 +1093,21 @@ impl Generator {
|
|||
};
|
||||
|
||||
Ok(MethodSigBody {
|
||||
success: response_type,
|
||||
error: error_type,
|
||||
success: response_type.into_tokens(&self.type_space),
|
||||
error: error_type.into_tokens(&self.type_space),
|
||||
body: body_impl,
|
||||
})
|
||||
}
|
||||
|
||||
fn extract_responses<'a>(
|
||||
/// Extract responses that match criteria specified by the `filter`. The
|
||||
/// result is a `Vec<OperationResponse>` that enumerates the cases matching
|
||||
/// the filter, and a `TokenStream` that respresents the generated type for
|
||||
/// those cases.
|
||||
pub(crate) fn extract_responses<'a>(
|
||||
&self,
|
||||
method: &'a OperationMethod,
|
||||
filter: fn(&OperationResponseStatus) -> bool,
|
||||
) -> (Vec<&'a OperationResponse>, TokenStream) {
|
||||
) -> (Vec<&'a OperationResponse>, OperationResponseType) {
|
||||
let mut response_items = method
|
||||
.responses
|
||||
.iter()
|
||||
|
@ -1091,22 +1118,20 @@ impl Generator {
|
|||
// If we have a success range and a default, we can pop off the default
|
||||
// since it will never be hit. Note that this is a no-op for error
|
||||
// responses.
|
||||
{
|
||||
let len = response_items.len();
|
||||
if len >= 2 {
|
||||
if let (
|
||||
OperationResponse {
|
||||
status_code: OperationResponseStatus::Range(2),
|
||||
..
|
||||
},
|
||||
OperationResponse {
|
||||
status_code: OperationResponseStatus::Default,
|
||||
..
|
||||
},
|
||||
) = (&response_items[len - 2], &response_items[len - 1])
|
||||
{
|
||||
response_items.pop();
|
||||
}
|
||||
let len = response_items.len();
|
||||
if len >= 2 {
|
||||
if let (
|
||||
OperationResponse {
|
||||
status_code: OperationResponseStatus::Range(2),
|
||||
..
|
||||
},
|
||||
OperationResponse {
|
||||
status_code: OperationResponseStatus::Default,
|
||||
..
|
||||
},
|
||||
) = (&response_items[len - 2], &response_items[len - 1])
|
||||
{
|
||||
response_items.pop();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1119,26 +1144,10 @@ impl Generator {
|
|||
// enum type with variants for each of the response types.
|
||||
assert!(response_types.len() <= 1);
|
||||
let response_type = response_types
|
||||
.iter()
|
||||
.into_iter()
|
||||
.next()
|
||||
.map(|typ| match typ {
|
||||
OperationResponseType::Type(type_id) => {
|
||||
let type_name =
|
||||
self.type_space.get_type(type_id).unwrap().ident();
|
||||
quote! { #type_name }
|
||||
}
|
||||
OperationResponseType::None => {
|
||||
quote! { () }
|
||||
}
|
||||
OperationResponseType::Raw => {
|
||||
quote! { ByteStream }
|
||||
}
|
||||
OperationResponseType::Upgrade => {
|
||||
quote! { reqwest::Upgraded }
|
||||
}
|
||||
})
|
||||
// TODO should this be a bytestream?
|
||||
.unwrap_or_else(|| quote! { () });
|
||||
// TODO should this be OperationResponseType::Raw?
|
||||
.unwrap_or(OperationResponseType::None);
|
||||
(response_items, response_type)
|
||||
}
|
||||
|
||||
|
@ -1271,9 +1280,9 @@ impl Generator {
|
|||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// All parameters are present and all their types are Result<T, String> or
|
||||
/// Result<Option<T>, String> for optional parameters. Each parameter also
|
||||
/// has a corresponding method:
|
||||
/// All parameters are present and all their types are `Result<T, String>`
|
||||
/// or `Result<Option<T>, String>` for optional parameters. Each parameter
|
||||
/// also has a corresponding method:
|
||||
/// ```ignore
|
||||
/// impl<'a> OperationId<'a> {
|
||||
/// pub fn param_1<V>(self, value: V)
|
||||
|
@ -1295,7 +1304,8 @@ impl Generator {
|
|||
/// ```
|
||||
///
|
||||
/// The Client's operation_id method simply invokes the builder's new
|
||||
/// method:
|
||||
/// method, which assigns an error value to mandatory field and a
|
||||
/// `Ok(None)` value to optional ones:
|
||||
/// ```ignore
|
||||
/// impl<'a> OperationId<'a> {
|
||||
/// pub fn new(client: &'a super::Client) -> Self {
|
||||
|
@ -1309,7 +1319,7 @@ impl Generator {
|
|||
/// ```
|
||||
///
|
||||
/// Finally, builders have methods to execute the operation. This simply
|
||||
/// resolves each parameter with the ? (Try operator).
|
||||
/// resolves each parameter with the ? (`Try` operator).
|
||||
/// ```ignore
|
||||
/// impl<'a> OperationId<'a> {
|
||||
/// pub fn send(self) -> Result<
|
||||
|
|
|
@ -0,0 +1,900 @@
|
|||
pub struct Cli {
|
||||
client: sdk::Client,
|
||||
}
|
||||
|
||||
impl Cli {
|
||||
pub fn new(client: sdk::Client) -> Self {
|
||||
Self { client }
|
||||
}
|
||||
|
||||
pub fn cli_control_hold() -> clap::Command {
|
||||
clap::Command::new("")
|
||||
}
|
||||
|
||||
pub async fn execute_control_hold(&self, matches: &clap::ArgMatches) {
|
||||
let mut request = self.client.control_hold();
|
||||
let result = request.send().await;
|
||||
match result {
|
||||
Ok(r) => {
|
||||
println!("success\n{:#?}", r)
|
||||
}
|
||||
Err(r) => {
|
||||
println!("success\n{:#?}", r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cli_control_resume() -> clap::Command {
|
||||
clap::Command::new("")
|
||||
}
|
||||
|
||||
pub async fn execute_control_resume(&self, matches: &clap::ArgMatches) {
|
||||
let mut request = self.client.control_resume();
|
||||
let result = request.send().await;
|
||||
match result {
|
||||
Ok(r) => {
|
||||
println!("success\n{:#?}", r)
|
||||
}
|
||||
Err(r) => {
|
||||
println!("success\n{:#?}", r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cli_task_get() -> clap::Command {
|
||||
clap::Command::new("").arg(
|
||||
clap::Arg::new("task")
|
||||
.long("task")
|
||||
.required(true)
|
||||
.value_parser(clap::value_parser!(String)),
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn execute_task_get(&self, matches: &clap::ArgMatches) {
|
||||
let mut request = self.client.task_get();
|
||||
if let Some(value) = matches.get_one::<String>("task") {
|
||||
request = request.task(value.clone());
|
||||
}
|
||||
|
||||
let result = request.send().await;
|
||||
match result {
|
||||
Ok(r) => {
|
||||
println!("success\n{:#?}", r)
|
||||
}
|
||||
Err(r) => {
|
||||
println!("success\n{:#?}", r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cli_tasks_get() -> clap::Command {
|
||||
clap::Command::new("")
|
||||
}
|
||||
|
||||
pub async fn execute_tasks_get(&self, matches: &clap::ArgMatches) {
|
||||
let mut request = self.client.tasks_get();
|
||||
let result = request.send().await;
|
||||
match result {
|
||||
Ok(r) => {
|
||||
println!("success\n{:#?}", r)
|
||||
}
|
||||
Err(r) => {
|
||||
println!("success\n{:#?}", r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cli_task_submit() -> clap::Command {
|
||||
clap::Command::new("")
|
||||
.arg(
|
||||
clap::Arg::new("name")
|
||||
.long("name")
|
||||
.required(true)
|
||||
.value_parser(clap::value_parser!(String)),
|
||||
)
|
||||
.arg(
|
||||
clap::Arg::new("script")
|
||||
.long("script")
|
||||
.required(true)
|
||||
.value_parser(clap::value_parser!(String)),
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn execute_task_submit(&self, matches: &clap::ArgMatches) {
|
||||
let mut request = self.client.task_submit();
|
||||
let request = request.body({
|
||||
let mut body = types::TaskSubmit::builder();
|
||||
if let Some(value) = matches.get_one::<String>("name") {
|
||||
body = body.name(value.clone());
|
||||
}
|
||||
if let Some(value) = matches.get_one::<String>("script") {
|
||||
body = body.script(value.clone());
|
||||
}
|
||||
body
|
||||
});
|
||||
let result = request.send().await;
|
||||
match result {
|
||||
Ok(r) => {
|
||||
println!("success\n{:#?}", r)
|
||||
}
|
||||
Err(r) => {
|
||||
println!("success\n{:#?}", r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cli_task_events_get() -> clap::Command {
|
||||
clap::Command::new("")
|
||||
.arg(
|
||||
clap::Arg::new("task")
|
||||
.long("task")
|
||||
.required(true)
|
||||
.value_parser(clap::value_parser!(String)),
|
||||
)
|
||||
.arg(
|
||||
clap::Arg::new("minseq")
|
||||
.long("minseq")
|
||||
.required(false)
|
||||
.value_parser(clap::value_parser!(u32)),
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn execute_task_events_get(&self, matches: &clap::ArgMatches) {
|
||||
let mut request = self.client.task_events_get();
|
||||
if let Some(value) = matches.get_one::<String>("task") {
|
||||
request = request.task(value.clone());
|
||||
}
|
||||
|
||||
if let Some(value) = matches.get_one::<u32>("minseq") {
|
||||
request = request.minseq(value.clone());
|
||||
}
|
||||
|
||||
let result = request.send().await;
|
||||
match result {
|
||||
Ok(r) => {
|
||||
println!("success\n{:#?}", r)
|
||||
}
|
||||
Err(r) => {
|
||||
println!("success\n{:#?}", r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cli_task_outputs_get() -> clap::Command {
|
||||
clap::Command::new("").arg(
|
||||
clap::Arg::new("task")
|
||||
.long("task")
|
||||
.required(true)
|
||||
.value_parser(clap::value_parser!(String)),
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn execute_task_outputs_get(&self, matches: &clap::ArgMatches) {
|
||||
let mut request = self.client.task_outputs_get();
|
||||
if let Some(value) = matches.get_one::<String>("task") {
|
||||
request = request.task(value.clone());
|
||||
}
|
||||
|
||||
let result = request.send().await;
|
||||
match result {
|
||||
Ok(r) => {
|
||||
println!("success\n{:#?}", r)
|
||||
}
|
||||
Err(r) => {
|
||||
println!("success\n{:#?}", r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cli_task_output_download() -> clap::Command {
|
||||
clap::Command::new("")
|
||||
.arg(
|
||||
clap::Arg::new("task")
|
||||
.long("task")
|
||||
.required(true)
|
||||
.value_parser(clap::value_parser!(String)),
|
||||
)
|
||||
.arg(
|
||||
clap::Arg::new("output")
|
||||
.long("output")
|
||||
.required(true)
|
||||
.value_parser(clap::value_parser!(String)),
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn execute_task_output_download(&self, matches: &clap::ArgMatches) {
|
||||
let mut request = self.client.task_output_download();
|
||||
if let Some(value) = matches.get_one::<String>("task") {
|
||||
request = request.task(value.clone());
|
||||
}
|
||||
|
||||
if let Some(value) = matches.get_one::<String>("output") {
|
||||
request = request.output(value.clone());
|
||||
}
|
||||
|
||||
let result = request.send().await;
|
||||
match result {
|
||||
Ok(r) => {
|
||||
todo!()
|
||||
}
|
||||
Err(r) => {
|
||||
println!("success\n{:#?}", r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cli_user_create() -> clap::Command {
|
||||
clap::Command::new("").arg(
|
||||
clap::Arg::new("name")
|
||||
.long("name")
|
||||
.required(true)
|
||||
.value_parser(clap::value_parser!(String)),
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn execute_user_create(&self, matches: &clap::ArgMatches) {
|
||||
let mut request = self.client.user_create();
|
||||
let request = request.body({
|
||||
let mut body = types::UserCreate::builder();
|
||||
if let Some(value) = matches.get_one::<String>("name") {
|
||||
body = body.name(value.clone());
|
||||
}
|
||||
body
|
||||
});
|
||||
let result = request.send().await;
|
||||
match result {
|
||||
Ok(r) => {
|
||||
println!("success\n{:#?}", r)
|
||||
}
|
||||
Err(r) => {
|
||||
println!("success\n{:#?}", r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cli_whoami() -> clap::Command {
|
||||
clap::Command::new("")
|
||||
}
|
||||
|
||||
pub async fn execute_whoami(&self, matches: &clap::ArgMatches) {
|
||||
let mut request = self.client.whoami();
|
||||
let result = request.send().await;
|
||||
match result {
|
||||
Ok(r) => {
|
||||
println!("success\n{:#?}", r)
|
||||
}
|
||||
Err(r) => {
|
||||
println!("success\n{:#?}", r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cli_worker_bootstrap() -> clap::Command {
|
||||
clap::Command::new("")
|
||||
.arg(
|
||||
clap::Arg::new("bootstrap")
|
||||
.long("bootstrap")
|
||||
.required(true)
|
||||
.value_parser(clap::value_parser!(String)),
|
||||
)
|
||||
.arg(
|
||||
clap::Arg::new("token")
|
||||
.long("token")
|
||||
.required(true)
|
||||
.value_parser(clap::value_parser!(String)),
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn execute_worker_bootstrap(&self, matches: &clap::ArgMatches) {
|
||||
let mut request = self.client.worker_bootstrap();
|
||||
let request = request.body({
|
||||
let mut body = types::WorkerBootstrap::builder();
|
||||
if let Some(value) = matches.get_one::<String>("bootstrap") {
|
||||
body = body.bootstrap(value.clone());
|
||||
}
|
||||
if let Some(value) = matches.get_one::<String>("token") {
|
||||
body = body.token(value.clone());
|
||||
}
|
||||
body
|
||||
});
|
||||
let result = request.send().await;
|
||||
match result {
|
||||
Ok(r) => {
|
||||
println!("success\n{:#?}", r)
|
||||
}
|
||||
Err(r) => {
|
||||
println!("success\n{:#?}", r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cli_worker_ping() -> clap::Command {
|
||||
clap::Command::new("")
|
||||
}
|
||||
|
||||
pub async fn execute_worker_ping(&self, matches: &clap::ArgMatches) {
|
||||
let mut request = self.client.worker_ping();
|
||||
let result = request.send().await;
|
||||
match result {
|
||||
Ok(r) => {
|
||||
println!("success\n{:#?}", r)
|
||||
}
|
||||
Err(r) => {
|
||||
println!("success\n{:#?}", r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cli_worker_task_append() -> clap::Command {
|
||||
clap::Command::new("")
|
||||
.arg(
|
||||
clap::Arg::new("task")
|
||||
.long("task")
|
||||
.required(true)
|
||||
.value_parser(clap::value_parser!(String)),
|
||||
)
|
||||
.arg(
|
||||
clap::Arg::new("payload")
|
||||
.long("payload")
|
||||
.required(true)
|
||||
.value_parser(clap::value_parser!(String)),
|
||||
)
|
||||
.arg(
|
||||
clap::Arg::new("stream")
|
||||
.long("stream")
|
||||
.required(true)
|
||||
.value_parser(clap::value_parser!(String)),
|
||||
)
|
||||
.arg(
|
||||
clap::Arg::new("time")
|
||||
.long("time")
|
||||
.required(true)
|
||||
.value_parser(clap::value_parser!(chrono::DateTime<chrono::offset::Utc>)),
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn execute_worker_task_append(&self, matches: &clap::ArgMatches) {
|
||||
let mut request = self.client.worker_task_append();
|
||||
if let Some(value) = matches.get_one::<String>("task") {
|
||||
request = request.task(value.clone());
|
||||
}
|
||||
|
||||
let request = request.body({
|
||||
let mut body = types::WorkerAppendTask::builder();
|
||||
if let Some(value) = matches.get_one::<String>("payload") {
|
||||
body = body.payload(value.clone());
|
||||
}
|
||||
if let Some(value) = matches.get_one::<String>("stream") {
|
||||
body = body.stream(value.clone());
|
||||
}
|
||||
if let Some(value) = matches.get_one::<chrono::DateTime<chrono::offset::Utc>>("time") {
|
||||
body = body.time(value.clone());
|
||||
}
|
||||
body
|
||||
});
|
||||
let result = request.send().await;
|
||||
match result {
|
||||
Ok(r) => {
|
||||
println!("success\n{:#?}", r)
|
||||
}
|
||||
Err(r) => {
|
||||
println!("success\n{:#?}", r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cli_worker_task_upload_chunk() -> clap::Command {
|
||||
clap::Command::new("").arg(
|
||||
clap::Arg::new("task")
|
||||
.long("task")
|
||||
.required(true)
|
||||
.value_parser(clap::value_parser!(String)),
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn execute_worker_task_upload_chunk(&self, matches: &clap::ArgMatches) {
|
||||
let mut request = self.client.worker_task_upload_chunk();
|
||||
if let Some(value) = matches.get_one::<String>("task") {
|
||||
request = request.task(value.clone());
|
||||
}
|
||||
|
||||
let result = request.send().await;
|
||||
match result {
|
||||
Ok(r) => {
|
||||
println!("success\n{:#?}", r)
|
||||
}
|
||||
Err(r) => {
|
||||
println!("success\n{:#?}", r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cli_worker_task_complete() -> clap::Command {
|
||||
clap::Command::new("")
|
||||
.arg(
|
||||
clap::Arg::new("task")
|
||||
.long("task")
|
||||
.required(true)
|
||||
.value_parser(clap::value_parser!(String)),
|
||||
)
|
||||
.arg(
|
||||
clap::Arg::new("failed")
|
||||
.long("failed")
|
||||
.required(true)
|
||||
.value_parser(clap::value_parser!(bool)),
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn execute_worker_task_complete(&self, matches: &clap::ArgMatches) {
|
||||
let mut request = self.client.worker_task_complete();
|
||||
if let Some(value) = matches.get_one::<String>("task") {
|
||||
request = request.task(value.clone());
|
||||
}
|
||||
|
||||
let request = request.body({
|
||||
let mut body = types::WorkerCompleteTask::builder();
|
||||
if let Some(value) = matches.get_one::<bool>("failed") {
|
||||
body = body.failed(value.clone());
|
||||
}
|
||||
body
|
||||
});
|
||||
let result = request.send().await;
|
||||
match result {
|
||||
Ok(r) => {
|
||||
println!("success\n{:#?}", r)
|
||||
}
|
||||
Err(r) => {
|
||||
println!("success\n{:#?}", r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cli_worker_task_add_output() -> clap::Command {
|
||||
clap::Command::new("")
|
||||
.arg(
|
||||
clap::Arg::new("task")
|
||||
.long("task")
|
||||
.required(true)
|
||||
.value_parser(clap::value_parser!(String)),
|
||||
)
|
||||
.arg(
|
||||
clap::Arg::new("path")
|
||||
.long("path")
|
||||
.required(true)
|
||||
.value_parser(clap::value_parser!(String)),
|
||||
)
|
||||
.arg(
|
||||
clap::Arg::new("size")
|
||||
.long("size")
|
||||
.required(true)
|
||||
.value_parser(clap::value_parser!(i64)),
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn execute_worker_task_add_output(&self, matches: &clap::ArgMatches) {
|
||||
let mut request = self.client.worker_task_add_output();
|
||||
if let Some(value) = matches.get_one::<String>("task") {
|
||||
request = request.task(value.clone());
|
||||
}
|
||||
|
||||
let request = request.body({
|
||||
let mut body = types::WorkerAddOutput::builder();
|
||||
if let Some(value) = matches.get_one::<String>("path") {
|
||||
body = body.path(value.clone());
|
||||
}
|
||||
if let Some(value) = matches.get_one::<i64>("size") {
|
||||
body = body.size(value.clone());
|
||||
}
|
||||
body
|
||||
});
|
||||
let result = request.send().await;
|
||||
match result {
|
||||
Ok(r) => {
|
||||
println!("success\n{:#?}", r)
|
||||
}
|
||||
Err(r) => {
|
||||
println!("success\n{:#?}", r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cli_workers_list() -> clap::Command {
|
||||
clap::Command::new("")
|
||||
}
|
||||
|
||||
pub async fn execute_workers_list(&self, matches: &clap::ArgMatches) {
|
||||
let mut request = self.client.workers_list();
|
||||
let result = request.send().await;
|
||||
match result {
|
||||
Ok(r) => {
|
||||
println!("success\n{:#?}", r)
|
||||
}
|
||||
Err(r) => {
|
||||
println!("success\n{:#?}", r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cli_workers_recycle() -> clap::Command {
|
||||
clap::Command::new("")
|
||||
}
|
||||
|
||||
pub async fn execute_workers_recycle(&self, matches: &clap::ArgMatches) {
|
||||
let mut request = self.client.workers_recycle();
|
||||
let result = request.send().await;
|
||||
match result {
|
||||
Ok(r) => {
|
||||
println!("success\n{:#?}", r)
|
||||
}
|
||||
Err(r) => {
|
||||
println!("success\n{:#?}", r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_command(cmd: CliCommand) -> clap::Command {
|
||||
match cmd {
|
||||
CliCommand::ControlHold => Self::cli_control_hold(),
|
||||
CliCommand::ControlResume => Self::cli_control_resume(),
|
||||
CliCommand::TaskGet => Self::cli_task_get(),
|
||||
CliCommand::TasksGet => Self::cli_tasks_get(),
|
||||
CliCommand::TaskSubmit => Self::cli_task_submit(),
|
||||
CliCommand::TaskEventsGet => Self::cli_task_events_get(),
|
||||
CliCommand::TaskOutputsGet => Self::cli_task_outputs_get(),
|
||||
CliCommand::TaskOutputDownload => Self::cli_task_output_download(),
|
||||
CliCommand::UserCreate => Self::cli_user_create(),
|
||||
CliCommand::Whoami => Self::cli_whoami(),
|
||||
CliCommand::WorkerBootstrap => Self::cli_worker_bootstrap(),
|
||||
CliCommand::WorkerPing => Self::cli_worker_ping(),
|
||||
CliCommand::WorkerTaskAppend => Self::cli_worker_task_append(),
|
||||
CliCommand::WorkerTaskUploadChunk => Self::cli_worker_task_upload_chunk(),
|
||||
CliCommand::WorkerTaskComplete => Self::cli_worker_task_complete(),
|
||||
CliCommand::WorkerTaskAddOutput => Self::cli_worker_task_add_output(),
|
||||
CliCommand::WorkersList => Self::cli_workers_list(),
|
||||
CliCommand::WorkersRecycle => Self::cli_workers_recycle(),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn execute(&self, cmd: CliCommand, matches: &clap::ArgMatches) {
|
||||
let _ = match cmd {
|
||||
CliCommand::ControlHold => {
|
||||
self.execute_control_hold(matches).await;
|
||||
}
|
||||
CliCommand::ControlResume => {
|
||||
self.execute_control_resume(matches).await;
|
||||
}
|
||||
CliCommand::TaskGet => {
|
||||
self.execute_task_get(matches).await;
|
||||
}
|
||||
CliCommand::TasksGet => {
|
||||
self.execute_tasks_get(matches).await;
|
||||
}
|
||||
CliCommand::TaskSubmit => {
|
||||
self.execute_task_submit(matches).await;
|
||||
}
|
||||
CliCommand::TaskEventsGet => {
|
||||
self.execute_task_events_get(matches).await;
|
||||
}
|
||||
CliCommand::TaskOutputsGet => {
|
||||
self.execute_task_outputs_get(matches).await;
|
||||
}
|
||||
CliCommand::TaskOutputDownload => {
|
||||
self.execute_task_output_download(matches).await;
|
||||
}
|
||||
CliCommand::UserCreate => {
|
||||
self.execute_user_create(matches).await;
|
||||
}
|
||||
CliCommand::Whoami => {
|
||||
self.execute_whoami(matches).await;
|
||||
}
|
||||
CliCommand::WorkerBootstrap => {
|
||||
self.execute_worker_bootstrap(matches).await;
|
||||
}
|
||||
CliCommand::WorkerPing => {
|
||||
self.execute_worker_ping(matches).await;
|
||||
}
|
||||
CliCommand::WorkerTaskAppend => {
|
||||
self.execute_worker_task_append(matches).await;
|
||||
}
|
||||
CliCommand::WorkerTaskUploadChunk => {
|
||||
self.execute_worker_task_upload_chunk(matches).await;
|
||||
}
|
||||
CliCommand::WorkerTaskComplete => {
|
||||
self.execute_worker_task_complete(matches).await;
|
||||
}
|
||||
CliCommand::WorkerTaskAddOutput => {
|
||||
self.execute_worker_task_add_output(matches).await;
|
||||
}
|
||||
CliCommand::WorkersList => {
|
||||
self.execute_workers_list(matches).await;
|
||||
}
|
||||
CliCommand::WorkersRecycle => {
|
||||
self.execute_workers_recycle(matches).await;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
pub trait CliOverride {
|
||||
fn cli_control_hold(cmd: clap::Command) -> clap::Command {
|
||||
cmd
|
||||
}
|
||||
|
||||
fn execute_control_hold(
|
||||
&self,
|
||||
matches: &clap::ArgMatches,
|
||||
request: &mut (),
|
||||
body: &mut (),
|
||||
) -> Result<(), String> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn cli_control_resume(cmd: clap::Command) -> clap::Command {
|
||||
cmd
|
||||
}
|
||||
|
||||
fn execute_control_resume(
|
||||
&self,
|
||||
matches: &clap::ArgMatches,
|
||||
request: &mut (),
|
||||
body: &mut (),
|
||||
) -> Result<(), String> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn cli_task_get(cmd: clap::Command) -> clap::Command {
|
||||
cmd
|
||||
}
|
||||
|
||||
fn execute_task_get(
|
||||
&self,
|
||||
matches: &clap::ArgMatches,
|
||||
request: &mut (),
|
||||
body: &mut (),
|
||||
) -> Result<(), String> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn cli_tasks_get(cmd: clap::Command) -> clap::Command {
|
||||
cmd
|
||||
}
|
||||
|
||||
fn execute_tasks_get(
|
||||
&self,
|
||||
matches: &clap::ArgMatches,
|
||||
request: &mut (),
|
||||
body: &mut (),
|
||||
) -> Result<(), String> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn cli_task_submit(cmd: clap::Command) -> clap::Command {
|
||||
cmd
|
||||
}
|
||||
|
||||
fn execute_task_submit(
|
||||
&self,
|
||||
matches: &clap::ArgMatches,
|
||||
request: &mut (),
|
||||
body: &mut (),
|
||||
) -> Result<(), String> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn cli_task_events_get(cmd: clap::Command) -> clap::Command {
|
||||
cmd
|
||||
}
|
||||
|
||||
fn execute_task_events_get(
|
||||
&self,
|
||||
matches: &clap::ArgMatches,
|
||||
request: &mut (),
|
||||
body: &mut (),
|
||||
) -> Result<(), String> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn cli_task_outputs_get(cmd: clap::Command) -> clap::Command {
|
||||
cmd
|
||||
}
|
||||
|
||||
fn execute_task_outputs_get(
|
||||
&self,
|
||||
matches: &clap::ArgMatches,
|
||||
request: &mut (),
|
||||
body: &mut (),
|
||||
) -> Result<(), String> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn cli_task_output_download(cmd: clap::Command) -> clap::Command {
|
||||
cmd
|
||||
}
|
||||
|
||||
fn execute_task_output_download(
|
||||
&self,
|
||||
matches: &clap::ArgMatches,
|
||||
request: &mut (),
|
||||
body: &mut (),
|
||||
) -> Result<(), String> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn cli_user_create(cmd: clap::Command) -> clap::Command {
|
||||
cmd
|
||||
}
|
||||
|
||||
fn execute_user_create(
|
||||
&self,
|
||||
matches: &clap::ArgMatches,
|
||||
request: &mut (),
|
||||
body: &mut (),
|
||||
) -> Result<(), String> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn cli_whoami(cmd: clap::Command) -> clap::Command {
|
||||
cmd
|
||||
}
|
||||
|
||||
fn execute_whoami(
|
||||
&self,
|
||||
matches: &clap::ArgMatches,
|
||||
request: &mut (),
|
||||
body: &mut (),
|
||||
) -> Result<(), String> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn cli_worker_bootstrap(cmd: clap::Command) -> clap::Command {
|
||||
cmd
|
||||
}
|
||||
|
||||
fn execute_worker_bootstrap(
|
||||
&self,
|
||||
matches: &clap::ArgMatches,
|
||||
request: &mut (),
|
||||
body: &mut (),
|
||||
) -> Result<(), String> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn cli_worker_ping(cmd: clap::Command) -> clap::Command {
|
||||
cmd
|
||||
}
|
||||
|
||||
fn execute_worker_ping(
|
||||
&self,
|
||||
matches: &clap::ArgMatches,
|
||||
request: &mut (),
|
||||
body: &mut (),
|
||||
) -> Result<(), String> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn cli_worker_task_append(cmd: clap::Command) -> clap::Command {
|
||||
cmd
|
||||
}
|
||||
|
||||
fn execute_worker_task_append(
|
||||
&self,
|
||||
matches: &clap::ArgMatches,
|
||||
request: &mut (),
|
||||
body: &mut (),
|
||||
) -> Result<(), String> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn cli_worker_task_upload_chunk(cmd: clap::Command) -> clap::Command {
|
||||
cmd
|
||||
}
|
||||
|
||||
fn execute_worker_task_upload_chunk(
|
||||
&self,
|
||||
matches: &clap::ArgMatches,
|
||||
request: &mut (),
|
||||
body: &mut (),
|
||||
) -> Result<(), String> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn cli_worker_task_complete(cmd: clap::Command) -> clap::Command {
|
||||
cmd
|
||||
}
|
||||
|
||||
fn execute_worker_task_complete(
|
||||
&self,
|
||||
matches: &clap::ArgMatches,
|
||||
request: &mut (),
|
||||
body: &mut (),
|
||||
) -> Result<(), String> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn cli_worker_task_add_output(cmd: clap::Command) -> clap::Command {
|
||||
cmd
|
||||
}
|
||||
|
||||
fn execute_worker_task_add_output(
|
||||
&self,
|
||||
matches: &clap::ArgMatches,
|
||||
request: &mut (),
|
||||
body: &mut (),
|
||||
) -> Result<(), String> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn cli_workers_list(cmd: clap::Command) -> clap::Command {
|
||||
cmd
|
||||
}
|
||||
|
||||
fn execute_workers_list(
|
||||
&self,
|
||||
matches: &clap::ArgMatches,
|
||||
request: &mut (),
|
||||
body: &mut (),
|
||||
) -> Result<(), String> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn cli_workers_recycle(cmd: clap::Command) -> clap::Command {
|
||||
cmd
|
||||
}
|
||||
|
||||
fn execute_workers_recycle(
|
||||
&self,
|
||||
matches: &clap::ArgMatches,
|
||||
request: &mut (),
|
||||
body: &mut (),
|
||||
) -> Result<(), String> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum CliCommand {
|
||||
ControlHold,
|
||||
ControlResume,
|
||||
TaskGet,
|
||||
TasksGet,
|
||||
TaskSubmit,
|
||||
TaskEventsGet,
|
||||
TaskOutputsGet,
|
||||
TaskOutputDownload,
|
||||
UserCreate,
|
||||
Whoami,
|
||||
WorkerBootstrap,
|
||||
WorkerPing,
|
||||
WorkerTaskAppend,
|
||||
WorkerTaskUploadChunk,
|
||||
WorkerTaskComplete,
|
||||
WorkerTaskAddOutput,
|
||||
WorkersList,
|
||||
WorkersRecycle,
|
||||
}
|
||||
|
||||
impl CliCommand {
|
||||
pub fn iter() -> impl Iterator<Item = CliCommand> {
|
||||
vec![
|
||||
CliCommand::ControlHold,
|
||||
CliCommand::ControlResume,
|
||||
CliCommand::TaskGet,
|
||||
CliCommand::TasksGet,
|
||||
CliCommand::TaskSubmit,
|
||||
CliCommand::TaskEventsGet,
|
||||
CliCommand::TaskOutputsGet,
|
||||
CliCommand::TaskOutputDownload,
|
||||
CliCommand::UserCreate,
|
||||
CliCommand::Whoami,
|
||||
CliCommand::WorkerBootstrap,
|
||||
CliCommand::WorkerPing,
|
||||
CliCommand::WorkerTaskAppend,
|
||||
CliCommand::WorkerTaskUploadChunk,
|
||||
CliCommand::WorkerTaskComplete,
|
||||
CliCommand::WorkerTaskAddOutput,
|
||||
CliCommand::WorkersList,
|
||||
CliCommand::WorkersRecycle,
|
||||
]
|
||||
.into_iter()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,395 @@
|
|||
pub struct Cli {
|
||||
client: sdk::Client,
|
||||
}
|
||||
|
||||
impl Cli {
|
||||
pub fn new(client: sdk::Client) -> Self {
|
||||
Self { client }
|
||||
}
|
||||
|
||||
pub fn cli_enrol() -> clap::Command {
|
||||
clap::Command::new("")
|
||||
.arg(
|
||||
clap::Arg::new("authorization")
|
||||
.long("authorization")
|
||||
.required(true)
|
||||
.value_parser(clap::value_parser!(String))
|
||||
.help("Authorization header (bearer token)"),
|
||||
)
|
||||
.arg(
|
||||
clap::Arg::new("host")
|
||||
.long("host")
|
||||
.required(true)
|
||||
.value_parser(clap::value_parser!(String)),
|
||||
)
|
||||
.arg(
|
||||
clap::Arg::new("key")
|
||||
.long("key")
|
||||
.required(true)
|
||||
.value_parser(clap::value_parser!(String)),
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn execute_enrol(&self, matches: &clap::ArgMatches) {
|
||||
let mut request = self.client.enrol();
|
||||
if let Some(value) = matches.get_one::<String>("authorization") {
|
||||
request = request.authorization(value.clone());
|
||||
}
|
||||
|
||||
let request = request.body({
|
||||
let mut body = types::EnrolBody::builder();
|
||||
if let Some(value) = matches.get_one::<String>("host") {
|
||||
body = body.host(value.clone());
|
||||
}
|
||||
if let Some(value) = matches.get_one::<String>("key") {
|
||||
body = body.key(value.clone());
|
||||
}
|
||||
body
|
||||
});
|
||||
let result = request.send().await;
|
||||
match result {
|
||||
Ok(r) => {
|
||||
println!("success\n{:#?}", r)
|
||||
}
|
||||
Err(r) => {
|
||||
println!("success\n{:#?}", r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cli_global_jobs() -> clap::Command {
|
||||
clap::Command::new("").arg(
|
||||
clap::Arg::new("authorization")
|
||||
.long("authorization")
|
||||
.required(true)
|
||||
.value_parser(clap::value_parser!(String))
|
||||
.help("Authorization header (bearer token)"),
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn execute_global_jobs(&self, matches: &clap::ArgMatches) {
|
||||
let mut request = self.client.global_jobs();
|
||||
if let Some(value) = matches.get_one::<String>("authorization") {
|
||||
request = request.authorization(value.clone());
|
||||
}
|
||||
|
||||
let result = request.send().await;
|
||||
match result {
|
||||
Ok(r) => {
|
||||
println!("success\n{:#?}", r)
|
||||
}
|
||||
Err(r) => {
|
||||
println!("success\n{:#?}", r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cli_ping() -> clap::Command {
|
||||
clap::Command::new("").arg(
|
||||
clap::Arg::new("authorization")
|
||||
.long("authorization")
|
||||
.required(true)
|
||||
.value_parser(clap::value_parser!(String))
|
||||
.help("Authorization header (bearer token)"),
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn execute_ping(&self, matches: &clap::ArgMatches) {
|
||||
let mut request = self.client.ping();
|
||||
if let Some(value) = matches.get_one::<String>("authorization") {
|
||||
request = request.authorization(value.clone());
|
||||
}
|
||||
|
||||
let result = request.send().await;
|
||||
match result {
|
||||
Ok(r) => {
|
||||
println!("success\n{:#?}", r)
|
||||
}
|
||||
Err(r) => {
|
||||
println!("success\n{:#?}", r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cli_report_finish() -> clap::Command {
|
||||
clap::Command::new("")
|
||||
.arg(
|
||||
clap::Arg::new("authorization")
|
||||
.long("authorization")
|
||||
.required(true)
|
||||
.value_parser(clap::value_parser!(String))
|
||||
.help("Authorization header (bearer token)"),
|
||||
)
|
||||
.arg(
|
||||
clap::Arg::new("duration-millis")
|
||||
.long("duration-millis")
|
||||
.required(true)
|
||||
.value_parser(clap::value_parser!(i32)),
|
||||
)
|
||||
.arg(
|
||||
clap::Arg::new("end-time")
|
||||
.long("end-time")
|
||||
.required(true)
|
||||
.value_parser(clap::value_parser!(chrono::DateTime<chrono::offset::Utc>)),
|
||||
)
|
||||
.arg(
|
||||
clap::Arg::new("exit-status")
|
||||
.long("exit-status")
|
||||
.required(true)
|
||||
.value_parser(clap::value_parser!(i32)),
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn execute_report_finish(&self, matches: &clap::ArgMatches) {
|
||||
let mut request = self.client.report_finish();
|
||||
if let Some(value) = matches.get_one::<String>("authorization") {
|
||||
request = request.authorization(value.clone());
|
||||
}
|
||||
|
||||
let request = request.body({
|
||||
let mut body = types::ReportFinishBody::builder();
|
||||
if let Some(value) = matches.get_one::<i32>("duration-millis") {
|
||||
body = body.duration_millis(value.clone());
|
||||
}
|
||||
if let Some(value) =
|
||||
matches.get_one::<chrono::DateTime<chrono::offset::Utc>>("end-time")
|
||||
{
|
||||
body = body.end_time(value.clone());
|
||||
}
|
||||
if let Some(value) = matches.get_one::<i32>("exit-status") {
|
||||
body = body.exit_status(value.clone());
|
||||
}
|
||||
body
|
||||
});
|
||||
let result = request.send().await;
|
||||
match result {
|
||||
Ok(r) => {
|
||||
println!("success\n{:#?}", r)
|
||||
}
|
||||
Err(r) => {
|
||||
println!("success\n{:#?}", r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cli_report_output() -> clap::Command {
|
||||
clap::Command::new("").arg(
|
||||
clap::Arg::new("authorization")
|
||||
.long("authorization")
|
||||
.required(true)
|
||||
.value_parser(clap::value_parser!(String))
|
||||
.help("Authorization header (bearer token)"),
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn execute_report_output(&self, matches: &clap::ArgMatches) {
|
||||
let mut request = self.client.report_output();
|
||||
if let Some(value) = matches.get_one::<String>("authorization") {
|
||||
request = request.authorization(value.clone());
|
||||
}
|
||||
|
||||
let request = request.body({
|
||||
let mut body = types::ReportOutputBody::builder();
|
||||
body
|
||||
});
|
||||
let result = request.send().await;
|
||||
match result {
|
||||
Ok(r) => {
|
||||
println!("success\n{:#?}", r)
|
||||
}
|
||||
Err(r) => {
|
||||
println!("success\n{:#?}", r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cli_report_start() -> clap::Command {
|
||||
clap::Command::new("")
|
||||
.arg(
|
||||
clap::Arg::new("authorization")
|
||||
.long("authorization")
|
||||
.required(true)
|
||||
.value_parser(clap::value_parser!(String))
|
||||
.help("Authorization header (bearer token)"),
|
||||
)
|
||||
.arg(
|
||||
clap::Arg::new("script")
|
||||
.long("script")
|
||||
.required(true)
|
||||
.value_parser(clap::value_parser!(String)),
|
||||
)
|
||||
.arg(
|
||||
clap::Arg::new("start-time")
|
||||
.long("start-time")
|
||||
.required(true)
|
||||
.value_parser(clap::value_parser!(chrono::DateTime<chrono::offset::Utc>)),
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn execute_report_start(&self, matches: &clap::ArgMatches) {
|
||||
let mut request = self.client.report_start();
|
||||
if let Some(value) = matches.get_one::<String>("authorization") {
|
||||
request = request.authorization(value.clone());
|
||||
}
|
||||
|
||||
let request = request.body({
|
||||
let mut body = types::ReportStartBody::builder();
|
||||
if let Some(value) = matches.get_one::<String>("script") {
|
||||
body = body.script(value.clone());
|
||||
}
|
||||
if let Some(value) =
|
||||
matches.get_one::<chrono::DateTime<chrono::offset::Utc>>("start-time")
|
||||
{
|
||||
body = body.start_time(value.clone());
|
||||
}
|
||||
body
|
||||
});
|
||||
let result = request.send().await;
|
||||
match result {
|
||||
Ok(r) => {
|
||||
println!("success\n{:#?}", r)
|
||||
}
|
||||
Err(r) => {
|
||||
println!("success\n{:#?}", r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_command(cmd: CliCommand) -> clap::Command {
|
||||
match cmd {
|
||||
CliCommand::Enrol => Self::cli_enrol(),
|
||||
CliCommand::GlobalJobs => Self::cli_global_jobs(),
|
||||
CliCommand::Ping => Self::cli_ping(),
|
||||
CliCommand::ReportFinish => Self::cli_report_finish(),
|
||||
CliCommand::ReportOutput => Self::cli_report_output(),
|
||||
CliCommand::ReportStart => Self::cli_report_start(),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn execute(&self, cmd: CliCommand, matches: &clap::ArgMatches) {
|
||||
let _ = match cmd {
|
||||
CliCommand::Enrol => {
|
||||
self.execute_enrol(matches).await;
|
||||
}
|
||||
CliCommand::GlobalJobs => {
|
||||
self.execute_global_jobs(matches).await;
|
||||
}
|
||||
CliCommand::Ping => {
|
||||
self.execute_ping(matches).await;
|
||||
}
|
||||
CliCommand::ReportFinish => {
|
||||
self.execute_report_finish(matches).await;
|
||||
}
|
||||
CliCommand::ReportOutput => {
|
||||
self.execute_report_output(matches).await;
|
||||
}
|
||||
CliCommand::ReportStart => {
|
||||
self.execute_report_start(matches).await;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
pub trait CliOverride {
|
||||
fn cli_enrol(cmd: clap::Command) -> clap::Command {
|
||||
cmd
|
||||
}
|
||||
|
||||
fn execute_enrol(
|
||||
&self,
|
||||
matches: &clap::ArgMatches,
|
||||
request: &mut (),
|
||||
body: &mut (),
|
||||
) -> Result<(), String> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn cli_global_jobs(cmd: clap::Command) -> clap::Command {
|
||||
cmd
|
||||
}
|
||||
|
||||
fn execute_global_jobs(
|
||||
&self,
|
||||
matches: &clap::ArgMatches,
|
||||
request: &mut (),
|
||||
body: &mut (),
|
||||
) -> Result<(), String> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn cli_ping(cmd: clap::Command) -> clap::Command {
|
||||
cmd
|
||||
}
|
||||
|
||||
fn execute_ping(
|
||||
&self,
|
||||
matches: &clap::ArgMatches,
|
||||
request: &mut (),
|
||||
body: &mut (),
|
||||
) -> Result<(), String> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn cli_report_finish(cmd: clap::Command) -> clap::Command {
|
||||
cmd
|
||||
}
|
||||
|
||||
fn execute_report_finish(
|
||||
&self,
|
||||
matches: &clap::ArgMatches,
|
||||
request: &mut (),
|
||||
body: &mut (),
|
||||
) -> Result<(), String> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn cli_report_output(cmd: clap::Command) -> clap::Command {
|
||||
cmd
|
||||
}
|
||||
|
||||
fn execute_report_output(
|
||||
&self,
|
||||
matches: &clap::ArgMatches,
|
||||
request: &mut (),
|
||||
body: &mut (),
|
||||
) -> Result<(), String> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn cli_report_start(cmd: clap::Command) -> clap::Command {
|
||||
cmd
|
||||
}
|
||||
|
||||
fn execute_report_start(
|
||||
&self,
|
||||
matches: &clap::ArgMatches,
|
||||
request: &mut (),
|
||||
body: &mut (),
|
||||
) -> Result<(), String> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum CliCommand {
|
||||
Enrol,
|
||||
GlobalJobs,
|
||||
Ping,
|
||||
ReportFinish,
|
||||
ReportOutput,
|
||||
ReportStart,
|
||||
}
|
||||
|
||||
impl CliCommand {
|
||||
pub fn iter() -> impl Iterator<Item = CliCommand> {
|
||||
vec![
|
||||
CliCommand::Enrol,
|
||||
CliCommand::GlobalJobs,
|
||||
CliCommand::Ping,
|
||||
CliCommand::ReportFinish,
|
||||
CliCommand::ReportOutput,
|
||||
CliCommand::ReportStart,
|
||||
]
|
||||
.into_iter()
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,88 @@
|
|||
pub struct Cli {
|
||||
client: sdk::Client,
|
||||
}
|
||||
|
||||
impl Cli {
|
||||
pub fn new(client: sdk::Client) -> Self {
|
||||
Self { client }
|
||||
}
|
||||
|
||||
pub fn cli_key_get() -> clap::Command {
|
||||
clap::Command::new("")
|
||||
.arg(
|
||||
clap::Arg::new("key")
|
||||
.long("key")
|
||||
.required(false)
|
||||
.value_parser(clap::value_parser!(bool))
|
||||
.help("The same key parameter that overlaps with the path level parameter"),
|
||||
)
|
||||
.arg(
|
||||
clap::Arg::new("unique-key")
|
||||
.long("unique-key")
|
||||
.required(false)
|
||||
.value_parser(clap::value_parser!(String))
|
||||
.help("A key parameter that will not be overridden by the path spec"),
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn execute_key_get(&self, matches: &clap::ArgMatches) {
|
||||
let mut request = self.client.key_get();
|
||||
if let Some(value) = matches.get_one::<bool>("key") {
|
||||
request = request.key(value.clone());
|
||||
}
|
||||
|
||||
if let Some(value) = matches.get_one::<String>("unique-key") {
|
||||
request = request.unique_key(value.clone());
|
||||
}
|
||||
|
||||
let result = request.send().await;
|
||||
match result {
|
||||
Ok(r) => {
|
||||
println!("success\n{:#?}", r)
|
||||
}
|
||||
Err(r) => {
|
||||
println!("success\n{:#?}", r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_command(cmd: CliCommand) -> clap::Command {
|
||||
match cmd {
|
||||
CliCommand::KeyGet => Self::cli_key_get(),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn execute(&self, cmd: CliCommand, matches: &clap::ArgMatches) {
|
||||
let _ = match cmd {
|
||||
CliCommand::KeyGet => {
|
||||
self.execute_key_get(matches).await;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
pub trait CliOverride {
|
||||
fn cli_key_get(cmd: clap::Command) -> clap::Command {
|
||||
cmd
|
||||
}
|
||||
|
||||
fn execute_key_get(
|
||||
&self,
|
||||
matches: &clap::ArgMatches,
|
||||
request: &mut (),
|
||||
body: &mut (),
|
||||
) -> Result<(), String> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum CliCommand {
|
||||
KeyGet,
|
||||
}
|
||||
|
||||
impl CliCommand {
|
||||
pub fn iter() -> impl Iterator<Item = CliCommand> {
|
||||
vec![CliCommand::KeyGet].into_iter()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,340 @@
|
|||
pub struct Cli {
|
||||
client: sdk::Client,
|
||||
}
|
||||
|
||||
impl Cli {
|
||||
pub fn new(client: sdk::Client) -> Self {
|
||||
Self { client }
|
||||
}
|
||||
|
||||
pub fn cli_instance_get() -> clap::Command {
|
||||
clap::Command::new("")
|
||||
}
|
||||
|
||||
pub async fn execute_instance_get(&self, matches: &clap::ArgMatches) {
|
||||
let mut request = self.client.instance_get();
|
||||
let result = request.send().await;
|
||||
match result {
|
||||
Ok(r) => {
|
||||
println!("success\n{:#?}", r)
|
||||
}
|
||||
Err(r) => {
|
||||
println!("error\n{:#?}", r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cli_instance_ensure() -> clap::Command {
|
||||
clap::Command::new("")
|
||||
}
|
||||
|
||||
pub async fn execute_instance_ensure(&self, matches: &clap::ArgMatches) {
|
||||
let mut request = self.client.instance_ensure();
|
||||
let request = request.body({
|
||||
let mut body = types::InstanceEnsureRequest::builder();
|
||||
body
|
||||
});
|
||||
let result = request.send().await;
|
||||
match result {
|
||||
Ok(r) => {
|
||||
println!("success\n{:#?}", r)
|
||||
}
|
||||
Err(r) => {
|
||||
println!("error\n{:#?}", r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cli_instance_issue_crucible_snapshot_request() -> clap::Command {
|
||||
clap::Command::new("")
|
||||
.arg(
|
||||
clap::Arg::new("id")
|
||||
.long("id")
|
||||
.required(true)
|
||||
.value_parser(clap::value_parser!(uuid::Uuid)),
|
||||
)
|
||||
.arg(
|
||||
clap::Arg::new("snapshot-id")
|
||||
.long("snapshot-id")
|
||||
.required(true)
|
||||
.value_parser(clap::value_parser!(uuid::Uuid)),
|
||||
)
|
||||
.about("Issue a snapshot request to a crucible backend")
|
||||
}
|
||||
|
||||
pub async fn execute_instance_issue_crucible_snapshot_request(
|
||||
&self,
|
||||
matches: &clap::ArgMatches,
|
||||
) {
|
||||
let mut request = self.client.instance_issue_crucible_snapshot_request();
|
||||
if let Some(value) = matches.get_one::<uuid::Uuid>("id") {
|
||||
request = request.id(value.clone());
|
||||
}
|
||||
|
||||
if let Some(value) = matches.get_one::<uuid::Uuid>("snapshot-id") {
|
||||
request = request.snapshot_id(value.clone());
|
||||
}
|
||||
|
||||
let result = request.send().await;
|
||||
match result {
|
||||
Ok(r) => {
|
||||
println!("success\n{:#?}", r)
|
||||
}
|
||||
Err(r) => {
|
||||
println!("error\n{:#?}", r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cli_instance_migrate_status() -> clap::Command {
|
||||
clap::Command::new("").arg(
|
||||
clap::Arg::new("migration-id")
|
||||
.long("migration-id")
|
||||
.required(true)
|
||||
.value_parser(clap::value_parser!(uuid::Uuid)),
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn execute_instance_migrate_status(&self, matches: &clap::ArgMatches) {
|
||||
let mut request = self.client.instance_migrate_status();
|
||||
let request = request.body({
|
||||
let mut body = types::InstanceMigrateStatusRequest::builder();
|
||||
if let Some(value) = matches.get_one::<uuid::Uuid>("migration-id") {
|
||||
body = body.migration_id(value.clone());
|
||||
}
|
||||
body
|
||||
});
|
||||
let result = request.send().await;
|
||||
match result {
|
||||
Ok(r) => {
|
||||
println!("success\n{:#?}", r)
|
||||
}
|
||||
Err(r) => {
|
||||
println!("error\n{:#?}", r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cli_instance_serial() -> clap::Command {
|
||||
clap::Command::new("")
|
||||
}
|
||||
|
||||
pub async fn execute_instance_serial(&self, matches: &clap::ArgMatches) {
|
||||
let mut request = self.client.instance_serial();
|
||||
let result = request.send().await;
|
||||
match result {
|
||||
Ok(r) => {
|
||||
todo!()
|
||||
}
|
||||
Err(r) => {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cli_instance_state_put() -> clap::Command {
|
||||
clap::Command::new("")
|
||||
}
|
||||
|
||||
pub async fn execute_instance_state_put(&self, matches: &clap::ArgMatches) {
|
||||
let mut request = self.client.instance_state_put();
|
||||
let result = request.send().await;
|
||||
match result {
|
||||
Ok(r) => {
|
||||
println!("success\n{:#?}", r)
|
||||
}
|
||||
Err(r) => {
|
||||
println!("error\n{:#?}", r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cli_instance_state_monitor() -> clap::Command {
|
||||
clap::Command::new("").arg(
|
||||
clap::Arg::new("gen")
|
||||
.long("gen")
|
||||
.required(true)
|
||||
.value_parser(clap::value_parser!(u64)),
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn execute_instance_state_monitor(&self, matches: &clap::ArgMatches) {
|
||||
let mut request = self.client.instance_state_monitor();
|
||||
let request = request.body({
|
||||
let mut body = types::InstanceStateMonitorRequest::builder();
|
||||
if let Some(value) = matches.get_one::<u64>("gen") {
|
||||
body = body.gen(value.clone());
|
||||
}
|
||||
body
|
||||
});
|
||||
let result = request.send().await;
|
||||
match result {
|
||||
Ok(r) => {
|
||||
println!("success\n{:#?}", r)
|
||||
}
|
||||
Err(r) => {
|
||||
println!("error\n{:#?}", r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_command(cmd: CliCommand) -> clap::Command {
|
||||
match cmd {
|
||||
CliCommand::InstanceGet => Self::cli_instance_get(),
|
||||
CliCommand::InstanceEnsure => Self::cli_instance_ensure(),
|
||||
CliCommand::InstanceIssueCrucibleSnapshotRequest => {
|
||||
Self::cli_instance_issue_crucible_snapshot_request()
|
||||
}
|
||||
CliCommand::InstanceMigrateStatus => Self::cli_instance_migrate_status(),
|
||||
CliCommand::InstanceSerial => Self::cli_instance_serial(),
|
||||
CliCommand::InstanceStatePut => Self::cli_instance_state_put(),
|
||||
CliCommand::InstanceStateMonitor => Self::cli_instance_state_monitor(),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn execute(&self, cmd: CliCommand, matches: &clap::ArgMatches) {
|
||||
let _ = match cmd {
|
||||
CliCommand::InstanceGet => {
|
||||
self.execute_instance_get(matches).await;
|
||||
}
|
||||
CliCommand::InstanceEnsure => {
|
||||
self.execute_instance_ensure(matches).await;
|
||||
}
|
||||
CliCommand::InstanceIssueCrucibleSnapshotRequest => {
|
||||
self.execute_instance_issue_crucible_snapshot_request(matches)
|
||||
.await;
|
||||
}
|
||||
CliCommand::InstanceMigrateStatus => {
|
||||
self.execute_instance_migrate_status(matches).await;
|
||||
}
|
||||
CliCommand::InstanceSerial => {
|
||||
self.execute_instance_serial(matches).await;
|
||||
}
|
||||
CliCommand::InstanceStatePut => {
|
||||
self.execute_instance_state_put(matches).await;
|
||||
}
|
||||
CliCommand::InstanceStateMonitor => {
|
||||
self.execute_instance_state_monitor(matches).await;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
pub trait CliOverride {
|
||||
fn cli_instance_get(cmd: clap::Command) -> clap::Command {
|
||||
cmd
|
||||
}
|
||||
|
||||
fn execute_instance_get(
|
||||
&self,
|
||||
matches: &clap::ArgMatches,
|
||||
request: &mut (),
|
||||
body: &mut (),
|
||||
) -> Result<(), String> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn cli_instance_ensure(cmd: clap::Command) -> clap::Command {
|
||||
cmd
|
||||
}
|
||||
|
||||
fn execute_instance_ensure(
|
||||
&self,
|
||||
matches: &clap::ArgMatches,
|
||||
request: &mut (),
|
||||
body: &mut (),
|
||||
) -> Result<(), String> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn cli_instance_issue_crucible_snapshot_request(cmd: clap::Command) -> clap::Command {
|
||||
cmd
|
||||
}
|
||||
|
||||
fn execute_instance_issue_crucible_snapshot_request(
|
||||
&self,
|
||||
matches: &clap::ArgMatches,
|
||||
request: &mut (),
|
||||
body: &mut (),
|
||||
) -> Result<(), String> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn cli_instance_migrate_status(cmd: clap::Command) -> clap::Command {
|
||||
cmd
|
||||
}
|
||||
|
||||
fn execute_instance_migrate_status(
|
||||
&self,
|
||||
matches: &clap::ArgMatches,
|
||||
request: &mut (),
|
||||
body: &mut (),
|
||||
) -> Result<(), String> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn cli_instance_serial(cmd: clap::Command) -> clap::Command {
|
||||
cmd
|
||||
}
|
||||
|
||||
fn execute_instance_serial(
|
||||
&self,
|
||||
matches: &clap::ArgMatches,
|
||||
request: &mut (),
|
||||
body: &mut (),
|
||||
) -> Result<(), String> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn cli_instance_state_put(cmd: clap::Command) -> clap::Command {
|
||||
cmd
|
||||
}
|
||||
|
||||
fn execute_instance_state_put(
|
||||
&self,
|
||||
matches: &clap::ArgMatches,
|
||||
request: &mut (),
|
||||
body: &mut (),
|
||||
) -> Result<(), String> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn cli_instance_state_monitor(cmd: clap::Command) -> clap::Command {
|
||||
cmd
|
||||
}
|
||||
|
||||
fn execute_instance_state_monitor(
|
||||
&self,
|
||||
matches: &clap::ArgMatches,
|
||||
request: &mut (),
|
||||
body: &mut (),
|
||||
) -> Result<(), String> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum CliCommand {
|
||||
InstanceGet,
|
||||
InstanceEnsure,
|
||||
InstanceIssueCrucibleSnapshotRequest,
|
||||
InstanceMigrateStatus,
|
||||
InstanceSerial,
|
||||
InstanceStatePut,
|
||||
InstanceStateMonitor,
|
||||
}
|
||||
|
||||
impl CliCommand {
|
||||
pub fn iter() -> impl Iterator<Item = CliCommand> {
|
||||
vec![
|
||||
CliCommand::InstanceGet,
|
||||
CliCommand::InstanceEnsure,
|
||||
CliCommand::InstanceIssueCrucibleSnapshotRequest,
|
||||
CliCommand::InstanceMigrateStatus,
|
||||
CliCommand::InstanceSerial,
|
||||
CliCommand::InstanceStatePut,
|
||||
CliCommand::InstanceStateMonitor,
|
||||
]
|
||||
.into_iter()
|
||||
}
|
||||
}
|
|
@ -34,6 +34,7 @@ fn verify_apis(openapi_file: &str) {
|
|||
|
||||
let spec = load_api(in_path);
|
||||
|
||||
// Positional generation.
|
||||
let mut generator = Generator::default();
|
||||
let output = generator.generate_text_normalize_comments(&spec).unwrap();
|
||||
expectorate::assert_contents(
|
||||
|
@ -41,6 +42,7 @@ fn verify_apis(openapi_file: &str) {
|
|||
&output,
|
||||
);
|
||||
|
||||
// Builder generation with derives and patches.
|
||||
let mut generator = Generator::new(
|
||||
GenerationSettings::default()
|
||||
.with_interface(InterfaceStyle::Builder)
|
||||
|
@ -65,17 +67,24 @@ fn verify_apis(openapi_file: &str) {
|
|||
&output,
|
||||
);
|
||||
|
||||
// Builder generation with tags.
|
||||
let mut generator = Generator::new(
|
||||
GenerationSettings::default()
|
||||
.with_interface(InterfaceStyle::Builder)
|
||||
.with_tag(TagStyle::Separate),
|
||||
);
|
||||
let output = generator.generate_text_normalize_comments(&spec).unwrap();
|
||||
println!("{output}");
|
||||
expectorate::assert_contents(
|
||||
format!("tests/output/{}-builder-tagged.out", openapi_stem),
|
||||
&output,
|
||||
);
|
||||
|
||||
// CLI generation.
|
||||
let output = generator.cli_text(&spec, "sdk").unwrap();
|
||||
expectorate::assert_contents(
|
||||
format!("tests/output/{}-cli.out", openapi_stem),
|
||||
&output,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -27,20 +27,17 @@ mod builder_untagged {
|
|||
use futures::StreamExt;
|
||||
|
||||
mod nexus_client {
|
||||
use std::convert::Infallible;
|
||||
|
||||
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
|
||||
pub struct MyIpv4Net(pub String);
|
||||
impl std::fmt::Display for MyIpv4Net {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str(&self.0)
|
||||
#[derive(Clone, serde::Serialize, serde::Deserialize, Debug)]
|
||||
pub struct MyIpv4Net(String);
|
||||
impl std::str::FromStr for MyIpv4Net {
|
||||
type Err = std::convert::Infallible;
|
||||
fn from_str(value: &str) -> Result<Self, Self::Err> {
|
||||
Ok(Self(value.to_string()))
|
||||
}
|
||||
}
|
||||
impl std::str::FromStr for MyIpv4Net {
|
||||
type Err = Infallible;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Ok(Self(s.to_string()))
|
||||
impl ToString for MyIpv4Net {
|
||||
fn to_string(&self) -> String {
|
||||
self.0.clone()
|
||||
}
|
||||
}
|
||||
progenitor::generate_api!(
|
||||
|
@ -62,9 +59,7 @@ mod builder_untagged {
|
|||
|
||||
pub fn _ignore() {
|
||||
// Verify the replacement above.
|
||||
let _ignore = nexus_client::types::IpNet::V4(nexus_client::MyIpv4Net(
|
||||
String::new(),
|
||||
));
|
||||
let _ignore = nexus_client::types::IpNet::V4("".parse().unwrap());
|
||||
|
||||
let client = Client::new("");
|
||||
let stream = client
|
||||
|
|
Loading…
Reference in New Issue