add an optional builder pattern as well as extension traits for tags when using the builder interface (#86)

This commit is contained in:
Adam Leventhal 2022-07-02 19:09:38 -07:00 committed by GitHub
parent 9e84bde032
commit 9b28ac87c6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
36 changed files with 32268 additions and 1094 deletions

View File

@ -14,10 +14,26 @@ jobs:
runs-on: ubuntu-18.04
steps:
- uses: actions/checkout@v2
- name: Install nightly
uses: actions-rs/toolchain@v1
with:
toolchain: nightly
components: rustfmt
default: false
- name: Install stable
uses: actions-rs/toolchain@v1
with:
toolchain: stable
components: rustfmt
default: true
- name: Report cargo version
run: cargo --version
- name: Report rustfmt version
run: cargo fmt -- --version
- name: Report nightly cargo version
run: cargo +nightly --version
- name: Report nightly rustfmt version
run: cargo +nightly fmt -- --version
- name: Check style
run: cargo fmt -- --check
@ -28,6 +44,18 @@ jobs:
os: [ ubuntu-18.04, windows-2019, macos-10.15 ]
steps:
- uses: actions/checkout@v2
- name: Install nightly
uses: actions-rs/toolchain@v1
with:
toolchain: nightly
components: rustfmt
default: false
- name: Install stable
uses: actions-rs/toolchain@v1
with:
toolchain: stable
components: rustfmt
default: true
- name: Build
run: cargo build --tests --verbose
- name: Run tests

View File

@ -3,7 +3,7 @@
:icons: font
:toclevels: 1
= Typify Changelog
= Progenitor Changelog
// WARNING: This file is modified programmatically by `cargo release` as
// configured in release.toml. DO NOT change the format of the headers or the
@ -15,6 +15,8 @@
https://github.com/oxidecomputer/progenitor/compare/v0.1.1\...HEAD[Full list of commits]
* Add support for a builder-style generation in addition to the positional style (#86)
== 0.1.1 (released 2022-05-13)
First published version

120
Cargo.lock generated
View File

@ -116,6 +116,45 @@ dependencies = [
"winapi",
]
[[package]]
name = "clap"
version = "3.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "190814073e85d238f31ff738fcb0bf6910cedeb73376c87cd69291028966fd83"
dependencies = [
"atty",
"bitflags",
"clap_derive",
"clap_lex",
"indexmap",
"once_cell",
"strsim",
"termcolor",
"textwrap",
]
[[package]]
name = "clap_derive"
version = "3.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "759bf187376e1afa7b85b959e6a664a3e7a95203415dba952ad19139e798f902"
dependencies = [
"heck",
"proc-macro-error",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "clap_lex"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5"
dependencies = [
"os_str_bytes",
]
[[package]]
name = "console"
version = "0.15.0"
@ -776,9 +815,9 @@ dependencies = [
[[package]]
name = "once_cell"
version = "1.9.0"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5"
checksum = "7709cef83f0c1f58f666e746a08b21e0085f7440fa6a29cc194d68aac97a4225"
[[package]]
name = "openapiv3"
@ -824,6 +863,12 @@ dependencies = [
"vcpkg",
]
[[package]]
name = "os_str_bytes"
version = "6.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21326818e99cfe6ce1e524c2a805c189a99b5ae555a35d19f9a284b427d86afa"
[[package]]
name = "parking_lot"
version = "0.12.0"
@ -886,6 +931,30 @@ version = "0.3.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "58893f751c9b0412871a09abd62ecd2a00298c6c83befa223ef98c52aef40cbe"
[[package]]
name = "proc-macro-error"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
dependencies = [
"proc-macro-error-attr",
"proc-macro2",
"quote",
"syn",
"version_check",
]
[[package]]
name = "proc-macro-error-attr"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
dependencies = [
"proc-macro2",
"quote",
"version_check",
]
[[package]]
name = "proc-macro2"
version = "1.0.40"
@ -901,8 +970,8 @@ version = "0.1.2-dev"
dependencies = [
"anyhow",
"chrono",
"clap",
"futures",
"getopts",
"openapiv3",
"percent-encoding",
"progenitor-client",
@ -1394,6 +1463,12 @@ version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
[[package]]
name = "strsim"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
name = "syn"
version = "1.0.98"
@ -1436,6 +1511,15 @@ dependencies = [
"winapi",
]
[[package]]
name = "termcolor"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
dependencies = [
"winapi-util",
]
[[package]]
name = "terminal_size"
version = "0.1.17"
@ -1446,6 +1530,12 @@ dependencies = [
"winapi",
]
[[package]]
name = "textwrap"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb"
[[package]]
name = "thiserror"
version = "1.0.31"
@ -1640,9 +1730,9 @@ checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642"
[[package]]
name = "typify"
version = "0.0.8"
version = "0.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d94783d3e464d8b2b8f78e826521e9f11c7df83523dafd544c5e642bf31df5a4"
checksum = "505f18fc847efc93b45a763f36f8099d3415dbfc6c0d6c3857a3012e20db92c7"
dependencies = [
"typify-impl",
"typify-macro",
@ -1650,9 +1740,9 @@ dependencies = [
[[package]]
name = "typify-impl"
version = "0.0.8"
version = "0.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b27f7e4f8a3b375daabcd3721bc067920a59ea9a33c1f01b9b2b98d17c3b5497"
checksum = "b572369a55be8402a5a7e24c721e8d895dbd58bf3d9dccb4b3619fcb926e7bde"
dependencies = [
"heck",
"log",
@ -1663,14 +1753,14 @@ dependencies = [
"serde_json",
"syn",
"thiserror",
"unicode-xid",
"unicode-ident",
]
[[package]]
name = "typify-macro"
version = "0.0.8"
version = "0.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e4211d794d8e4e6fed99bdcd263529ee05af52d9794004cc235123216f1fef42"
checksum = "a615bfbbcf929b1733898d2afe00c83dbf8756f8028aa42c156c3c2713b0f03b"
dependencies = [
"proc-macro2",
"quote",
@ -1696,9 +1786,9 @@ checksum = "1a01404663e3db436ed2746d9fefef640d868edae3cceb81c3b8d5732fda678f"
[[package]]
name = "unicode-ident"
version = "1.0.0"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee"
checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c"
[[package]]
name = "unicode-normalization"
@ -1715,12 +1805,6 @@ version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973"
[[package]]
name = "unicode-xid"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04"
[[package]]
name = "untrusted"
version = "0.7.1"

View File

@ -8,7 +8,9 @@ members = [
"progenitor-macro",
]
#[patch."https://github.com/oxidecomputer/typify"]
#typify = { path = "../typify/typify" }
#[patch."https://github.com/oxidecomputer/dropshot"]
#dropshot = { path = "../dropshot/dropshot" }
#[patch.crates-io]
#typify = { path = "../typify/typify" }
#rustfmt-wrapper = { path = "../rustfmt-wrapper" }

View File

@ -51,11 +51,13 @@ The macro has some additional fancy options to control the generated code:
```rust
generate_api!(
spec = "path/to/openapi_document.json", // The OpenAPI document
inner_type = my_client::InnerType, // Client inner type available to pre and post hooks
pre_hook = closure::or::path::to::function, // Hook invoked before issuing the HTTP request
post_hook = closure::or::path::to::function, // Hook invoked prior to receiving the HTTP response
derives = [ schemars::JsonSchema ], // Additional derive macros applied to generated types
spec = "path/to/openapi_document.json", // The OpenAPI document
interface = Builder, // Choose positional (default) or builder style
tags = Separate, // Tags may be Merged or Separate (default)
inner_type = my_client::InnerType, // Client inner type available to pre and post hooks
pre_hook = closure::or::path::to::function, // Hook invoked before issuing the HTTP request
post_hook = closure::or::path::to::function, // Hook invoked prior to receiving the HTTP response
derives = [ schemars::JsonSchema ], // Additional derive macros applied to generated types
);
```
@ -76,7 +78,7 @@ fn main() {
println!("cargo:rerun-if-changed={}", src);
let file = File::open(src).unwrap();
let spec = serde_json::from_reader(file).unwrap();
let mut generator = progenitor::Generator::new();
let mut generator = progenitor::Generator::default();
let content = generator.generate_text(&spec).unwrap();

View File

@ -2,7 +2,7 @@
name = "example-build"
version = "0.0.1"
authors = ["Adam H. Leventhal <ahl@oxidecomputer.com>"]
edition = "2018"
edition = "2021"
[dependencies]
chrono = { version = "0.4", features = ["serde"] }

View File

@ -11,7 +11,7 @@ fn main() {
println!("cargo:rerun-if-changed={}", src);
let file = File::open(src).unwrap();
let spec = serde_json::from_reader(file).unwrap();
let mut generator = progenitor::Generator::new();
let mut generator = progenitor::Generator::default();
let content = generator.generate_text(&spec).unwrap();

View File

@ -2,7 +2,7 @@
name = "example-macro"
version = "0.0.1"
authors = ["Adam H. Leventhal <ahl@oxidecomputer.com>"]
edition = "2018"
edition = "2021"
[dependencies]
chrono = { version = "0.4", features = ["serde"] }

View File

@ -1,7 +1,7 @@
[package]
name = "progenitor-client"
version = "0.1.2-dev"
edition = "2018"
edition = "2021"
license = "MPL-2.0"
repository = "https://github.com/oxidecomputer/progenitor.git"
description = "An OpenAPI client generator - client support"

View File

@ -1,5 +1,7 @@
// Copyright 2022 Oxide Computer Company
#![allow(dead_code)]
//! Support code for generated clients.
use std::{

View File

@ -1,7 +1,7 @@
[package]
name = "progenitor-impl"
version = "0.1.2-dev"
edition = "2018"
edition = "2021"
license = "MPL-2.0"
repository = "https://github.com/oxidecomputer/progenitor.git"
description = "An OpenAPI client generator - core implementation"
@ -14,13 +14,13 @@ openapiv3 = "1.0.0"
proc-macro2 = "1.0"
quote = "1.0"
regex = "1.5"
rustfmt-wrapper = "0.2"
rustfmt-wrapper = "0.2.0"
schemars = { version = "0.8.10", features = ["chrono", "uuid1"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
syn = { version = "1.0", features = ["parsing"] }
thiserror = "1.0"
typify = "0.0.8"
typify = "0.0.9"
unicode-ident = "1.0.0"
[dev-dependencies]

View File

@ -3,6 +3,7 @@
use openapiv3::OpenAPI;
use proc_macro2::TokenStream;
use quote::quote;
use serde::Deserialize;
use thiserror::Error;
use typify::TypeSpace;
@ -32,17 +33,59 @@ pub type Result<T> = std::result::Result<T, Error>;
#[derive(Default)]
pub struct Generator {
type_space: TypeSpace,
inner_type: Option<TokenStream>,
pre_hook: Option<TokenStream>,
post_hook: Option<TokenStream>,
settings: GenerationSettings,
uses_futures: bool,
}
impl Generator {
#[derive(Default, Clone)]
pub struct GenerationSettings {
interface: InterfaceStyle,
tag: TagStyle,
inner_type: Option<TokenStream>,
pre_hook: Option<TokenStream>,
post_hook: Option<TokenStream>,
extra_derives: Vec<TokenStream>,
}
#[derive(Clone, Deserialize)]
pub enum InterfaceStyle {
Positional,
Builder,
}
impl Default for InterfaceStyle {
fn default() -> Self {
Self::Positional
}
}
#[derive(Clone, Deserialize)]
pub enum TagStyle {
Merged,
Separate,
}
impl Default for TagStyle {
fn default() -> Self {
Self::Merged
}
}
impl GenerationSettings {
pub fn new() -> Self {
Self::default()
}
pub fn with_interface(&mut self, interface: InterfaceStyle) -> &mut Self {
self.interface = interface;
self
}
pub fn with_tag(&mut self, tag: TagStyle) -> &mut Self {
self.tag = tag;
self
}
pub fn with_inner_type(&mut self, inner_type: TokenStream) -> &mut Self {
self.inner_type = Some(inner_type);
self
@ -58,10 +101,21 @@ impl Generator {
self
}
// TODO maybe change to a typify::Settings or something
pub fn with_derive(&mut self, derive: TokenStream) -> &mut Self {
self.type_space.add_derive(derive);
self.extra_derives.push(derive);
self
}
}
impl Generator {
pub fn new(settings: &GenerationSettings) -> Self {
Self {
type_space: TypeSpace::default(),
settings: settings.clone(),
uses_futures: false,
}
}
pub fn generate_tokens(&mut self, spec: &OpenAPI) -> Result<TokenStream> {
// Convert our components dictionary to schemars
@ -100,10 +154,23 @@ impl Generator {
})
.collect::<Result<Vec<_>>>()?;
let methods = raw_methods
.iter()
.map(|method| self.positional_method(method))
.collect::<Result<Vec<_>>>()?;
let operation_code = match (
&self.settings.interface,
&self.settings.tag,
) {
(InterfaceStyle::Positional, TagStyle::Merged) => {
self.generate_tokens_positional_merged(&raw_methods)
}
(InterfaceStyle::Positional, TagStyle::Separate) => {
unimplemented!("positional arguments with separate tags are currently unsupported")
}
(InterfaceStyle::Builder, TagStyle::Merged) => {
self.generate_tokens_builder_merged(&raw_methods)
}
(InterfaceStyle::Builder, TagStyle::Separate) => {
self.generate_tokens_builder_separate(&raw_methods)
}
}?;
let mut types = self
.type_space
@ -114,12 +181,17 @@ impl Generator {
let types = types.into_iter().map(|(_, def)| def);
let shared = self.type_space.common_code();
let inner_property = self.inner_type.as_ref().map(|inner| {
let inner_property = self.settings.inner_type.as_ref().map(|inner| {
quote! {
pub (crate) inner: #inner,
}
});
let inner_parameter = self.settings.inner_type.as_ref().map(|inner| {
quote! {
inner: #inner,
}
});
let inner_value = self.inner_type.as_ref().map(|_| {
let inner_value = self.settings.inner_type.as_ref().map(|_| {
quote! {
inner
}
@ -129,6 +201,8 @@ impl Generator {
// Re-export ResponseValue and Error since those are used by the
// public interface of Client.
pub use progenitor_client::{ByteStream, Error, ResponseValue};
#[allow(unused_imports)]
use progenitor_client::encode_path;
pub mod types {
use serde::{Deserialize, Serialize};
@ -138,15 +212,15 @@ impl Generator {
#[derive(Clone)]
pub struct Client {
baseurl: String,
client: reqwest::Client,
pub(crate) baseurl: String,
pub(crate) client: reqwest::Client,
#inner_property
}
impl Client {
pub fn new(
baseurl: &str,
#inner_property
#inner_parameter
) -> Self {
let dur = std::time::Duration::from_secs(15);
let client = reqwest::ClientBuilder::new()
@ -160,7 +234,7 @@ impl Generator {
pub fn new_with_client(
baseurl: &str,
client: reqwest::Client,
#inner_property
#inner_parameter
) -> Self {
Self {
baseurl: baseurl.to_string(),
@ -177,18 +251,126 @@ impl Generator {
&self.client
}
#(#methods)*
}
#operation_code
};
Ok(file)
}
fn generate_tokens_positional_merged(
&mut self,
input_methods: &[method::OperationMethod],
) -> Result<TokenStream> {
let methods = input_methods
.iter()
.map(|method| self.positional_method(method))
.collect::<Result<Vec<_>>>()?;
let out = quote! {
impl Client {
#(#methods)*
}
};
Ok(out)
}
fn generate_tokens_builder_merged(
&mut self,
input_methods: &[method::OperationMethod],
) -> Result<TokenStream> {
let builder_struct = input_methods
.iter()
.map(|method| self.builder_struct(method, TagStyle::Merged))
.collect::<Result<Vec<_>>>()?;
let builder_methods = input_methods
.iter()
.map(|method| self.builder_impl(method))
.collect::<Vec<_>>();
let out = quote! {
impl Client {
#(#builder_methods)*
}
pub mod builder {
use super::types;
#[allow(unused_imports)]
use super::{ByteStream, Error, ResponseValue};
#[allow(unused_imports)]
use super::encode_path;
#(#builder_struct)*
}
};
Ok(out)
}
fn generate_tokens_builder_separate(
&mut self,
input_methods: &[method::OperationMethod],
) -> Result<TokenStream> {
let builder_struct = input_methods
.iter()
.map(|method| self.builder_struct(method, TagStyle::Separate))
.collect::<Result<Vec<_>>>()?;
let traits_and_impls = self.builder_tags(input_methods);
let out = quote! {
#traits_and_impls
pub mod builder {
use super::types;
#[allow(unused_imports)]
use super::{ByteStream, Error, ResponseValue};
#[allow(unused_imports)]
use super::encode_path;
#(#builder_struct)*
}
};
Ok(out)
}
/// Render text output.
pub fn generate_text(&mut self, spec: &OpenAPI) -> Result<String> {
self.generate_text_impl(
spec,
rustfmt_wrapper::config::Config::default(),
)
}
/// Render text output and normalize doc comments
///
/// Requires a nightly install of `rustfmt` (even if the target project is
/// not using nightly).
pub fn generate_text_normalize_comments(
&mut self,
spec: &OpenAPI,
) -> Result<String> {
self.generate_text_impl(
spec,
rustfmt_wrapper::config::Config {
normalize_doc_attributes: Some(true),
wrap_comments: Some(true),
..Default::default()
},
)
}
fn generate_text_impl(
&mut self,
spec: &OpenAPI,
config: rustfmt_wrapper::config::Config,
) -> Result<String> {
let output = self.generate_tokens(spec)?;
// Format the file with rustfmt.
let content = rustfmt_wrapper::rustfmt(output).unwrap();
let content = rustfmt_wrapper::rustfmt_config(config, output).unwrap();
// Add newlines after end-braces at <= two levels of indentation.
Ok(if cfg!(not(windows)) {

File diff suppressed because it is too large Load Diff

View File

@ -19,7 +19,11 @@ pub struct PathTemplate {
}
impl PathTemplate {
pub fn compile(&self, rename: HashMap<&String, &String>) -> TokenStream {
pub fn compile(
&self,
rename: HashMap<&String, &String>,
client: TokenStream,
) -> TokenStream {
let mut fmt = String::new();
fmt.push_str("{}");
for c in self.components.iter() {
@ -39,7 +43,7 @@ impl PathTemplate {
.expect(&format!("missing path name mapping {}", n)),
);
Some(quote! {
progenitor_client::encode_path(&#param.to_string())
encode_path(&#param.to_string())
})
} else {
None
@ -47,7 +51,7 @@ impl PathTemplate {
});
quote! {
let url = format!(#fmt, self.baseurl, #(#components,)*);
let url = format!(#fmt, #client.baseurl, #(#components,)*);
}
}
@ -233,11 +237,11 @@ mod test {
let number = "number".to_string();
rename.insert(&number, &number);
let t = parse("/measure/{number}").unwrap();
let out = t.compile(rename);
let out = t.compile(rename, quote::quote! { self });
let want = quote::quote! {
let url = format!("{}/measure/{}",
self.baseurl,
progenitor_client::encode_path(&number.to_string()),
encode_path(&number.to_string()),
);
};
assert_eq!(want.to_string(), out.to_string());

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,3 +1,5 @@
#[allow(unused_imports)]
use progenitor_client::encode_path;
pub use progenitor_client::{ByteStream, Error, ResponseValue};
pub mod types {
use serde::{Deserialize, Serialize};
@ -132,8 +134,8 @@ pub mod types {
#[derive(Clone)]
pub struct Client {
baseurl: String,
client: reqwest::Client,
pub(crate) baseurl: String,
pub(crate) client: reqwest::Client,
}
impl Client {
@ -161,8 +163,10 @@ impl Client {
pub fn client(&self) -> &reqwest::Client {
&self.client
}
}
#[doc = "Sends a `POST` request to `/v1/control/hold`"]
impl Client {
///Sends a `POST` request to `/v1/control/hold`
pub async fn control_hold<'a>(&'a self) -> Result<ResponseValue<()>, Error<()>> {
let url = format!("{}/v1/control/hold", self.baseurl,);
let request = self.client.post(url).build()?;
@ -174,7 +178,7 @@ impl Client {
}
}
#[doc = "Sends a `POST` request to `/v1/control/resume`"]
///Sends a `POST` request to `/v1/control/resume`
pub async fn control_resume<'a>(&'a self) -> Result<ResponseValue<()>, Error<()>> {
let url = format!("{}/v1/control/resume", self.baseurl,);
let request = self.client.post(url).build()?;
@ -186,7 +190,7 @@ impl Client {
}
}
#[doc = "Sends a `GET` request to `/v1/task/{task}`"]
///Sends a `GET` request to `/v1/task/{task}`
pub async fn task_get<'a>(
&'a self,
task: &'a str,
@ -194,7 +198,7 @@ impl Client {
let url = format!(
"{}/v1/task/{}",
self.baseurl,
progenitor_client::encode_path(&task.to_string()),
encode_path(&task.to_string()),
);
let request = self.client.get(url).build()?;
let result = self.client.execute(request).await;
@ -205,7 +209,7 @@ impl Client {
}
}
#[doc = "Sends a `GET` request to `/v1/tasks`"]
///Sends a `GET` request to `/v1/tasks`
pub async fn tasks_get<'a>(&'a self) -> Result<ResponseValue<Vec<types::Task>>, Error<()>> {
let url = format!("{}/v1/tasks", self.baseurl,);
let request = self.client.get(url).build()?;
@ -217,13 +221,13 @@ impl Client {
}
}
#[doc = "Sends a `POST` request to `/v1/tasks`"]
///Sends a `POST` request to `/v1/tasks`
pub async fn task_submit<'a>(
&'a self,
body: &'a types::TaskSubmit,
) -> Result<ResponseValue<types::TaskSubmitResult>, Error<()>> {
let url = format!("{}/v1/tasks", self.baseurl,);
let request = self.client.post(url).json(body).build()?;
let request = self.client.post(url).json(&body).build()?;
let result = self.client.execute(request).await;
let response = result?;
match response.status().as_u16() {
@ -232,7 +236,7 @@ impl Client {
}
}
#[doc = "Sends a `GET` request to `/v1/tasks/{task}/events`"]
///Sends a `GET` request to `/v1/tasks/{task}/events`
pub async fn task_events_get<'a>(
&'a self,
task: &'a str,
@ -241,7 +245,7 @@ impl Client {
let url = format!(
"{}/v1/tasks/{}/events",
self.baseurl,
progenitor_client::encode_path(&task.to_string()),
encode_path(&task.to_string()),
);
let mut query = Vec::new();
if let Some(v) = &minseq {
@ -257,7 +261,7 @@ impl Client {
}
}
#[doc = "Sends a `GET` request to `/v1/tasks/{task}/outputs`"]
///Sends a `GET` request to `/v1/tasks/{task}/outputs`
pub async fn task_outputs_get<'a>(
&'a self,
task: &'a str,
@ -265,7 +269,7 @@ impl Client {
let url = format!(
"{}/v1/tasks/{}/outputs",
self.baseurl,
progenitor_client::encode_path(&task.to_string()),
encode_path(&task.to_string()),
);
let request = self.client.get(url).build()?;
let result = self.client.execute(request).await;
@ -276,7 +280,7 @@ impl Client {
}
}
#[doc = "Sends a `GET` request to `/v1/tasks/{task}/outputs/{output}`"]
///Sends a `GET` request to `/v1/tasks/{task}/outputs/{output}`
pub async fn task_output_download<'a>(
&'a self,
task: &'a str,
@ -285,8 +289,8 @@ impl Client {
let url = format!(
"{}/v1/tasks/{}/outputs/{}",
self.baseurl,
progenitor_client::encode_path(&task.to_string()),
progenitor_client::encode_path(&output.to_string()),
encode_path(&task.to_string()),
encode_path(&output.to_string()),
);
let request = self.client.get(url).build()?;
let result = self.client.execute(request).await;
@ -297,13 +301,13 @@ impl Client {
}
}
#[doc = "Sends a `POST` request to `/v1/users`"]
///Sends a `POST` request to `/v1/users`
pub async fn user_create<'a>(
&'a self,
body: &'a types::UserCreate,
) -> Result<ResponseValue<types::UserCreateResult>, Error<()>> {
let url = format!("{}/v1/users", self.baseurl,);
let request = self.client.post(url).json(body).build()?;
let request = self.client.post(url).json(&body).build()?;
let result = self.client.execute(request).await;
let response = result?;
match response.status().as_u16() {
@ -312,7 +316,7 @@ impl Client {
}
}
#[doc = "Sends a `GET` request to `/v1/whoami`"]
///Sends a `GET` request to `/v1/whoami`
pub async fn whoami<'a>(&'a self) -> Result<ResponseValue<types::WhoamiResult>, Error<()>> {
let url = format!("{}/v1/whoami", self.baseurl,);
let request = self.client.get(url).build()?;
@ -324,13 +328,13 @@ impl Client {
}
}
#[doc = "Sends a `POST` request to `/v1/worker/bootstrap`"]
///Sends a `POST` request to `/v1/worker/bootstrap`
pub async fn worker_bootstrap<'a>(
&'a self,
body: &'a types::WorkerBootstrap,
) -> Result<ResponseValue<types::WorkerBootstrapResult>, Error<()>> {
let url = format!("{}/v1/worker/bootstrap", self.baseurl,);
let request = self.client.post(url).json(body).build()?;
let request = self.client.post(url).json(&body).build()?;
let result = self.client.execute(request).await;
let response = result?;
match response.status().as_u16() {
@ -339,7 +343,7 @@ impl Client {
}
}
#[doc = "Sends a `GET` request to `/v1/worker/ping`"]
///Sends a `GET` request to `/v1/worker/ping`
pub async fn worker_ping<'a>(
&'a self,
) -> Result<ResponseValue<types::WorkerPingResult>, Error<()>> {
@ -353,7 +357,7 @@ impl Client {
}
}
#[doc = "Sends a `POST` request to `/v1/worker/task/{task}/append`"]
///Sends a `POST` request to `/v1/worker/task/{task}/append`
pub async fn worker_task_append<'a>(
&'a self,
task: &'a str,
@ -362,9 +366,9 @@ impl Client {
let url = format!(
"{}/v1/worker/task/{}/append",
self.baseurl,
progenitor_client::encode_path(&task.to_string()),
encode_path(&task.to_string()),
);
let request = self.client.post(url).json(body).build()?;
let request = self.client.post(url).json(&body).build()?;
let result = self.client.execute(request).await;
let response = result?;
match response.status().as_u16() {
@ -373,7 +377,7 @@ impl Client {
}
}
#[doc = "Sends a `POST` request to `/v1/worker/task/{task}/chunk`"]
///Sends a `POST` request to `/v1/worker/task/{task}/chunk`
pub async fn worker_task_upload_chunk<'a, B: Into<reqwest::Body>>(
&'a self,
task: &'a str,
@ -382,7 +386,7 @@ impl Client {
let url = format!(
"{}/v1/worker/task/{}/chunk",
self.baseurl,
progenitor_client::encode_path(&task.to_string()),
encode_path(&task.to_string()),
);
let request = self.client.post(url).body(body).build()?;
let result = self.client.execute(request).await;
@ -393,7 +397,7 @@ impl Client {
}
}
#[doc = "Sends a `POST` request to `/v1/worker/task/{task}/complete`"]
///Sends a `POST` request to `/v1/worker/task/{task}/complete`
pub async fn worker_task_complete<'a>(
&'a self,
task: &'a str,
@ -402,9 +406,9 @@ impl Client {
let url = format!(
"{}/v1/worker/task/{}/complete",
self.baseurl,
progenitor_client::encode_path(&task.to_string()),
encode_path(&task.to_string()),
);
let request = self.client.post(url).json(body).build()?;
let request = self.client.post(url).json(&body).build()?;
let result = self.client.execute(request).await;
let response = result?;
match response.status().as_u16() {
@ -413,7 +417,7 @@ impl Client {
}
}
#[doc = "Sends a `POST` request to `/v1/worker/task/{task}/output`"]
///Sends a `POST` request to `/v1/worker/task/{task}/output`
pub async fn worker_task_add_output<'a>(
&'a self,
task: &'a str,
@ -422,9 +426,9 @@ impl Client {
let url = format!(
"{}/v1/worker/task/{}/output",
self.baseurl,
progenitor_client::encode_path(&task.to_string()),
encode_path(&task.to_string()),
);
let request = self.client.post(url).json(body).build()?;
let request = self.client.post(url).json(&body).build()?;
let result = self.client.execute(request).await;
let response = result?;
match response.status().as_u16() {
@ -433,7 +437,7 @@ impl Client {
}
}
#[doc = "Sends a `GET` request to `/v1/workers`"]
///Sends a `GET` request to `/v1/workers`
pub async fn workers_list<'a>(
&'a self,
) -> Result<ResponseValue<types::WorkersResult>, Error<()>> {
@ -447,7 +451,7 @@ impl Client {
}
}
#[doc = "Sends a `POST` request to `/v1/workers/recycle`"]
///Sends a `POST` request to `/v1/workers/recycle`
pub async fn workers_recycle<'a>(&'a self) -> Result<ResponseValue<()>, Error<()>> {
let url = format!("{}/v1/workers/recycle", self.baseurl,);
let request = self.client.post(url).build()?;

View File

@ -0,0 +1,418 @@
#[allow(unused_imports)]
use progenitor_client::encode_path;
pub use progenitor_client::{ByteStream, Error, ResponseValue};
pub mod types {
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct EnrolBody {
pub host: String,
pub key: String,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct GlobalJobsResult {
pub summary: Vec<ReportSummary>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct OutputRecord {
pub msg: String,
pub stream: String,
pub time: chrono::DateTime<chrono::offset::Utc>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct PingResult {
pub host: String,
pub ok: bool,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct ReportFinishBody {
pub duration_millis: i32,
pub end_time: chrono::DateTime<chrono::offset::Utc>,
pub exit_status: i32,
pub id: ReportId,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct ReportId {
pub host: String,
pub job: String,
pub pid: u64,
pub time: chrono::DateTime<chrono::offset::Utc>,
pub uuid: String,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct ReportOutputBody {
pub id: ReportId,
pub record: OutputRecord,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct ReportResult {
pub existed_already: bool,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct ReportStartBody {
pub id: ReportId,
pub script: String,
pub start_time: chrono::DateTime<chrono::offset::Utc>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct ReportSummary {
pub age_seconds: i32,
pub duration_seconds: i32,
pub host: String,
pub job: String,
pub status: i32,
pub when: chrono::DateTime<chrono::offset::Utc>,
}
}
#[derive(Clone)]
pub struct Client {
pub(crate) baseurl: String,
pub(crate) client: reqwest::Client,
}
impl Client {
pub fn new(baseurl: &str) -> Self {
let dur = std::time::Duration::from_secs(15);
let client = reqwest::ClientBuilder::new()
.connect_timeout(dur)
.timeout(dur)
.build()
.unwrap();
Self::new_with_client(baseurl, client)
}
pub fn new_with_client(baseurl: &str, client: reqwest::Client) -> Self {
Self {
baseurl: baseurl.to_string(),
client,
}
}
pub fn baseurl(&self) -> &String {
&self.baseurl
}
pub fn client(&self) -> &reqwest::Client {
&self.client
}
}
impl Client {
///Sends a `POST` request to `/enrol`
///```ignore
/// let response = client.enrol()
/// .body(body)
/// .send()
/// .await;
/// ```
pub fn enrol(&self) -> builder::Enrol {
builder::Enrol::new(self)
}
///Sends a `GET` request to `/global/jobs`
///```ignore
/// let response = client.global_jobs()
/// .send()
/// .await;
/// ```
pub fn global_jobs(&self) -> builder::GlobalJobs {
builder::GlobalJobs::new(self)
}
///Sends a `GET` request to `/ping`
///```ignore
/// let response = client.ping()
/// .send()
/// .await;
/// ```
pub fn ping(&self) -> builder::Ping {
builder::Ping::new(self)
}
///Sends a `POST` request to `/report/finish`
///```ignore
/// let response = client.report_finish()
/// .body(body)
/// .send()
/// .await;
/// ```
pub fn report_finish(&self) -> builder::ReportFinish {
builder::ReportFinish::new(self)
}
///Sends a `POST` request to `/report/output`
///```ignore
/// let response = client.report_output()
/// .body(body)
/// .send()
/// .await;
/// ```
pub fn report_output(&self) -> builder::ReportOutput {
builder::ReportOutput::new(self)
}
///Sends a `POST` request to `/report/start`
///```ignore
/// let response = client.report_start()
/// .body(body)
/// .send()
/// .await;
/// ```
pub fn report_start(&self) -> builder::ReportStart {
builder::ReportStart::new(self)
}
}
pub mod builder {
#[allow(unused_imports)]
use super::encode_path;
use super::types;
#[allow(unused_imports)]
use super::{ByteStream, Error, ResponseValue};
///Builder for [`Client::enrol`]
///
///[`Client::enrol`]: super::Client::enrol
#[derive(Clone)]
pub struct Enrol<'a> {
client: &'a super::Client,
body: Option<types::EnrolBody>,
}
impl<'a> Enrol<'a> {
pub fn new(client: &'a super::Client) -> Self {
Self { client, body: None }
}
pub fn body(mut self, value: types::EnrolBody) -> Self {
self.body = Some(value);
self
}
///Sends a `POST` request to `/enrol`
pub async fn send(self) -> Result<ResponseValue<()>, Error<()>> {
let Self { client, body } = self;
let (body,) = match (body,) {
(Some(body),) => (body,),
(body,) => {
let mut missing = Vec::new();
if body.is_none() {
missing.push(stringify!(body));
}
return Err(super::Error::InvalidRequest(format!(
"the following parameters are required: {}",
missing.join(", "),
)));
}
};
let url = format!("{}/enrol", client.baseurl,);
let request = client.client.post(url).json(&body).build()?;
let result = client.client.execute(request).await;
let response = result?;
match response.status().as_u16() {
201u16 => Ok(ResponseValue::empty(response)),
_ => Err(Error::UnexpectedResponse(response)),
}
}
}
///Builder for [`Client::global_jobs`]
///
///[`Client::global_jobs`]: super::Client::global_jobs
#[derive(Clone)]
pub struct GlobalJobs<'a> {
client: &'a super::Client,
}
impl<'a> GlobalJobs<'a> {
pub fn new(client: &'a super::Client) -> Self {
Self { client }
}
///Sends a `GET` request to `/global/jobs`
pub async fn send(self) -> Result<ResponseValue<types::GlobalJobsResult>, Error<()>> {
let Self { client } = self;
let url = format!("{}/global/jobs", client.baseurl,);
let request = client.client.get(url).build()?;
let result = client.client.execute(request).await;
let response = result?;
match response.status().as_u16() {
201u16 => ResponseValue::from_response(response).await,
_ => Err(Error::UnexpectedResponse(response)),
}
}
}
///Builder for [`Client::ping`]
///
///[`Client::ping`]: super::Client::ping
#[derive(Clone)]
pub struct Ping<'a> {
client: &'a super::Client,
}
impl<'a> Ping<'a> {
pub fn new(client: &'a super::Client) -> Self {
Self { client }
}
///Sends a `GET` request to `/ping`
pub async fn send(self) -> Result<ResponseValue<types::PingResult>, Error<()>> {
let Self { client } = self;
let url = format!("{}/ping", client.baseurl,);
let request = client.client.get(url).build()?;
let result = client.client.execute(request).await;
let response = result?;
match response.status().as_u16() {
201u16 => ResponseValue::from_response(response).await,
_ => Err(Error::UnexpectedResponse(response)),
}
}
}
///Builder for [`Client::report_finish`]
///
///[`Client::report_finish`]: super::Client::report_finish
#[derive(Clone)]
pub struct ReportFinish<'a> {
client: &'a super::Client,
body: Option<types::ReportFinishBody>,
}
impl<'a> ReportFinish<'a> {
pub fn new(client: &'a super::Client) -> Self {
Self { client, body: None }
}
pub fn body(mut self, value: types::ReportFinishBody) -> Self {
self.body = Some(value);
self
}
///Sends a `POST` request to `/report/finish`
pub async fn send(self) -> Result<ResponseValue<types::ReportResult>, Error<()>> {
let Self { client, body } = self;
let (body,) = match (body,) {
(Some(body),) => (body,),
(body,) => {
let mut missing = Vec::new();
if body.is_none() {
missing.push(stringify!(body));
}
return Err(super::Error::InvalidRequest(format!(
"the following parameters are required: {}",
missing.join(", "),
)));
}
};
let url = format!("{}/report/finish", client.baseurl,);
let request = client.client.post(url).json(&body).build()?;
let result = client.client.execute(request).await;
let response = result?;
match response.status().as_u16() {
201u16 => ResponseValue::from_response(response).await,
_ => Err(Error::UnexpectedResponse(response)),
}
}
}
///Builder for [`Client::report_output`]
///
///[`Client::report_output`]: super::Client::report_output
#[derive(Clone)]
pub struct ReportOutput<'a> {
client: &'a super::Client,
body: Option<types::ReportOutputBody>,
}
impl<'a> ReportOutput<'a> {
pub fn new(client: &'a super::Client) -> Self {
Self { client, body: None }
}
pub fn body(mut self, value: types::ReportOutputBody) -> Self {
self.body = Some(value);
self
}
///Sends a `POST` request to `/report/output`
pub async fn send(self) -> Result<ResponseValue<types::ReportResult>, Error<()>> {
let Self { client, body } = self;
let (body,) = match (body,) {
(Some(body),) => (body,),
(body,) => {
let mut missing = Vec::new();
if body.is_none() {
missing.push(stringify!(body));
}
return Err(super::Error::InvalidRequest(format!(
"the following parameters are required: {}",
missing.join(", "),
)));
}
};
let url = format!("{}/report/output", client.baseurl,);
let request = client.client.post(url).json(&body).build()?;
let result = client.client.execute(request).await;
let response = result?;
match response.status().as_u16() {
201u16 => ResponseValue::from_response(response).await,
_ => Err(Error::UnexpectedResponse(response)),
}
}
}
///Builder for [`Client::report_start`]
///
///[`Client::report_start`]: super::Client::report_start
#[derive(Clone)]
pub struct ReportStart<'a> {
client: &'a super::Client,
body: Option<types::ReportStartBody>,
}
impl<'a> ReportStart<'a> {
pub fn new(client: &'a super::Client) -> Self {
Self { client, body: None }
}
pub fn body(mut self, value: types::ReportStartBody) -> Self {
self.body = Some(value);
self
}
///Sends a `POST` request to `/report/start`
pub async fn send(self) -> Result<ResponseValue<types::ReportResult>, Error<()>> {
let Self { client, body } = self;
let (body,) = match (body,) {
(Some(body),) => (body,),
(body,) => {
let mut missing = Vec::new();
if body.is_none() {
missing.push(stringify!(body));
}
return Err(super::Error::InvalidRequest(format!(
"the following parameters are required: {}",
missing.join(", "),
)));
}
};
let url = format!("{}/report/start", client.baseurl,);
let request = client.client.post(url).json(&body).build()?;
let result = client.client.execute(request).await;
let response = result?;
match response.status().as_u16() {
201u16 => ResponseValue::from_response(response).await,
_ => Err(Error::UnexpectedResponse(response)),
}
}
}
}

View File

@ -0,0 +1,418 @@
#[allow(unused_imports)]
use progenitor_client::encode_path;
pub use progenitor_client::{ByteStream, Error, ResponseValue};
pub mod types {
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct EnrolBody {
pub host: String,
pub key: String,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct GlobalJobsResult {
pub summary: Vec<ReportSummary>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct OutputRecord {
pub msg: String,
pub stream: String,
pub time: chrono::DateTime<chrono::offset::Utc>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct PingResult {
pub host: String,
pub ok: bool,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct ReportFinishBody {
pub duration_millis: i32,
pub end_time: chrono::DateTime<chrono::offset::Utc>,
pub exit_status: i32,
pub id: ReportId,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct ReportId {
pub host: String,
pub job: String,
pub pid: u64,
pub time: chrono::DateTime<chrono::offset::Utc>,
pub uuid: String,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct ReportOutputBody {
pub id: ReportId,
pub record: OutputRecord,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct ReportResult {
pub existed_already: bool,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct ReportStartBody {
pub id: ReportId,
pub script: String,
pub start_time: chrono::DateTime<chrono::offset::Utc>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct ReportSummary {
pub age_seconds: i32,
pub duration_seconds: i32,
pub host: String,
pub job: String,
pub status: i32,
pub when: chrono::DateTime<chrono::offset::Utc>,
}
}
#[derive(Clone)]
pub struct Client {
pub(crate) baseurl: String,
pub(crate) client: reqwest::Client,
}
impl Client {
pub fn new(baseurl: &str) -> Self {
let dur = std::time::Duration::from_secs(15);
let client = reqwest::ClientBuilder::new()
.connect_timeout(dur)
.timeout(dur)
.build()
.unwrap();
Self::new_with_client(baseurl, client)
}
pub fn new_with_client(baseurl: &str, client: reqwest::Client) -> Self {
Self {
baseurl: baseurl.to_string(),
client,
}
}
pub fn baseurl(&self) -> &String {
&self.baseurl
}
pub fn client(&self) -> &reqwest::Client {
&self.client
}
}
impl Client {
///Sends a `POST` request to `/enrol`
///```ignore
/// let response = client.enrol()
/// .body(body)
/// .send()
/// .await;
/// ```
pub fn enrol(&self) -> builder::Enrol {
builder::Enrol::new(self)
}
///Sends a `GET` request to `/global/jobs`
///```ignore
/// let response = client.global_jobs()
/// .send()
/// .await;
/// ```
pub fn global_jobs(&self) -> builder::GlobalJobs {
builder::GlobalJobs::new(self)
}
///Sends a `GET` request to `/ping`
///```ignore
/// let response = client.ping()
/// .send()
/// .await;
/// ```
pub fn ping(&self) -> builder::Ping {
builder::Ping::new(self)
}
///Sends a `POST` request to `/report/finish`
///```ignore
/// let response = client.report_finish()
/// .body(body)
/// .send()
/// .await;
/// ```
pub fn report_finish(&self) -> builder::ReportFinish {
builder::ReportFinish::new(self)
}
///Sends a `POST` request to `/report/output`
///```ignore
/// let response = client.report_output()
/// .body(body)
/// .send()
/// .await;
/// ```
pub fn report_output(&self) -> builder::ReportOutput {
builder::ReportOutput::new(self)
}
///Sends a `POST` request to `/report/start`
///```ignore
/// let response = client.report_start()
/// .body(body)
/// .send()
/// .await;
/// ```
pub fn report_start(&self) -> builder::ReportStart {
builder::ReportStart::new(self)
}
}
pub mod builder {
#[allow(unused_imports)]
use super::encode_path;
use super::types;
#[allow(unused_imports)]
use super::{ByteStream, Error, ResponseValue};
///Builder for [`Client::enrol`]
///
///[`Client::enrol`]: super::Client::enrol
#[derive(Clone)]
pub struct Enrol<'a> {
client: &'a super::Client,
body: Option<types::EnrolBody>,
}
impl<'a> Enrol<'a> {
pub fn new(client: &'a super::Client) -> Self {
Self { client, body: None }
}
pub fn body(mut self, value: types::EnrolBody) -> Self {
self.body = Some(value);
self
}
///Sends a `POST` request to `/enrol`
pub async fn send(self) -> Result<ResponseValue<()>, Error<()>> {
let Self { client, body } = self;
let (body,) = match (body,) {
(Some(body),) => (body,),
(body,) => {
let mut missing = Vec::new();
if body.is_none() {
missing.push(stringify!(body));
}
return Err(super::Error::InvalidRequest(format!(
"the following parameters are required: {}",
missing.join(", "),
)));
}
};
let url = format!("{}/enrol", client.baseurl,);
let request = client.client.post(url).json(&body).build()?;
let result = client.client.execute(request).await;
let response = result?;
match response.status().as_u16() {
201u16 => Ok(ResponseValue::empty(response)),
_ => Err(Error::UnexpectedResponse(response)),
}
}
}
///Builder for [`Client::global_jobs`]
///
///[`Client::global_jobs`]: super::Client::global_jobs
#[derive(Clone)]
pub struct GlobalJobs<'a> {
client: &'a super::Client,
}
impl<'a> GlobalJobs<'a> {
pub fn new(client: &'a super::Client) -> Self {
Self { client }
}
///Sends a `GET` request to `/global/jobs`
pub async fn send(self) -> Result<ResponseValue<types::GlobalJobsResult>, Error<()>> {
let Self { client } = self;
let url = format!("{}/global/jobs", client.baseurl,);
let request = client.client.get(url).build()?;
let result = client.client.execute(request).await;
let response = result?;
match response.status().as_u16() {
201u16 => ResponseValue::from_response(response).await,
_ => Err(Error::UnexpectedResponse(response)),
}
}
}
///Builder for [`Client::ping`]
///
///[`Client::ping`]: super::Client::ping
#[derive(Clone)]
pub struct Ping<'a> {
client: &'a super::Client,
}
impl<'a> Ping<'a> {
pub fn new(client: &'a super::Client) -> Self {
Self { client }
}
///Sends a `GET` request to `/ping`
pub async fn send(self) -> Result<ResponseValue<types::PingResult>, Error<()>> {
let Self { client } = self;
let url = format!("{}/ping", client.baseurl,);
let request = client.client.get(url).build()?;
let result = client.client.execute(request).await;
let response = result?;
match response.status().as_u16() {
201u16 => ResponseValue::from_response(response).await,
_ => Err(Error::UnexpectedResponse(response)),
}
}
}
///Builder for [`Client::report_finish`]
///
///[`Client::report_finish`]: super::Client::report_finish
#[derive(Clone)]
pub struct ReportFinish<'a> {
client: &'a super::Client,
body: Option<types::ReportFinishBody>,
}
impl<'a> ReportFinish<'a> {
pub fn new(client: &'a super::Client) -> Self {
Self { client, body: None }
}
pub fn body(mut self, value: types::ReportFinishBody) -> Self {
self.body = Some(value);
self
}
///Sends a `POST` request to `/report/finish`
pub async fn send(self) -> Result<ResponseValue<types::ReportResult>, Error<()>> {
let Self { client, body } = self;
let (body,) = match (body,) {
(Some(body),) => (body,),
(body,) => {
let mut missing = Vec::new();
if body.is_none() {
missing.push(stringify!(body));
}
return Err(super::Error::InvalidRequest(format!(
"the following parameters are required: {}",
missing.join(", "),
)));
}
};
let url = format!("{}/report/finish", client.baseurl,);
let request = client.client.post(url).json(&body).build()?;
let result = client.client.execute(request).await;
let response = result?;
match response.status().as_u16() {
201u16 => ResponseValue::from_response(response).await,
_ => Err(Error::UnexpectedResponse(response)),
}
}
}
///Builder for [`Client::report_output`]
///
///[`Client::report_output`]: super::Client::report_output
#[derive(Clone)]
pub struct ReportOutput<'a> {
client: &'a super::Client,
body: Option<types::ReportOutputBody>,
}
impl<'a> ReportOutput<'a> {
pub fn new(client: &'a super::Client) -> Self {
Self { client, body: None }
}
pub fn body(mut self, value: types::ReportOutputBody) -> Self {
self.body = Some(value);
self
}
///Sends a `POST` request to `/report/output`
pub async fn send(self) -> Result<ResponseValue<types::ReportResult>, Error<()>> {
let Self { client, body } = self;
let (body,) = match (body,) {
(Some(body),) => (body,),
(body,) => {
let mut missing = Vec::new();
if body.is_none() {
missing.push(stringify!(body));
}
return Err(super::Error::InvalidRequest(format!(
"the following parameters are required: {}",
missing.join(", "),
)));
}
};
let url = format!("{}/report/output", client.baseurl,);
let request = client.client.post(url).json(&body).build()?;
let result = client.client.execute(request).await;
let response = result?;
match response.status().as_u16() {
201u16 => ResponseValue::from_response(response).await,
_ => Err(Error::UnexpectedResponse(response)),
}
}
}
///Builder for [`Client::report_start`]
///
///[`Client::report_start`]: super::Client::report_start
#[derive(Clone)]
pub struct ReportStart<'a> {
client: &'a super::Client,
body: Option<types::ReportStartBody>,
}
impl<'a> ReportStart<'a> {
pub fn new(client: &'a super::Client) -> Self {
Self { client, body: None }
}
pub fn body(mut self, value: types::ReportStartBody) -> Self {
self.body = Some(value);
self
}
///Sends a `POST` request to `/report/start`
pub async fn send(self) -> Result<ResponseValue<types::ReportResult>, Error<()>> {
let Self { client, body } = self;
let (body,) = match (body,) {
(Some(body),) => (body,),
(body,) => {
let mut missing = Vec::new();
if body.is_none() {
missing.push(stringify!(body));
}
return Err(super::Error::InvalidRequest(format!(
"the following parameters are required: {}",
missing.join(", "),
)));
}
};
let url = format!("{}/report/start", client.baseurl,);
let request = client.client.post(url).json(&body).build()?;
let result = client.client.execute(request).await;
let response = result?;
match response.status().as_u16() {
201u16 => ResponseValue::from_response(response).await,
_ => Err(Error::UnexpectedResponse(response)),
}
}
}
}

View File

@ -1,3 +1,5 @@
#[allow(unused_imports)]
use progenitor_client::encode_path;
pub use progenitor_client::{ByteStream, Error, ResponseValue};
pub mod types {
use serde::{Deserialize, Serialize};
@ -73,8 +75,8 @@ pub mod types {
#[derive(Clone)]
pub struct Client {
baseurl: String,
client: reqwest::Client,
pub(crate) baseurl: String,
pub(crate) client: reqwest::Client,
}
impl Client {
@ -102,14 +104,16 @@ impl Client {
pub fn client(&self) -> &reqwest::Client {
&self.client
}
}
#[doc = "Sends a `POST` request to `/enrol`"]
impl Client {
///Sends a `POST` request to `/enrol`
pub async fn enrol<'a>(
&'a self,
body: &'a types::EnrolBody,
) -> Result<ResponseValue<()>, Error<()>> {
let url = format!("{}/enrol", self.baseurl,);
let request = self.client.post(url).json(body).build()?;
let request = self.client.post(url).json(&body).build()?;
let result = self.client.execute(request).await;
let response = result?;
match response.status().as_u16() {
@ -118,7 +122,7 @@ impl Client {
}
}
#[doc = "Sends a `GET` request to `/global/jobs`"]
///Sends a `GET` request to `/global/jobs`
pub async fn global_jobs<'a>(
&'a self,
) -> Result<ResponseValue<types::GlobalJobsResult>, Error<()>> {
@ -132,7 +136,7 @@ impl Client {
}
}
#[doc = "Sends a `GET` request to `/ping`"]
///Sends a `GET` request to `/ping`
pub async fn ping<'a>(&'a self) -> Result<ResponseValue<types::PingResult>, Error<()>> {
let url = format!("{}/ping", self.baseurl,);
let request = self.client.get(url).build()?;
@ -144,13 +148,13 @@ impl Client {
}
}
#[doc = "Sends a `POST` request to `/report/finish`"]
///Sends a `POST` request to `/report/finish`
pub async fn report_finish<'a>(
&'a self,
body: &'a types::ReportFinishBody,
) -> Result<ResponseValue<types::ReportResult>, Error<()>> {
let url = format!("{}/report/finish", self.baseurl,);
let request = self.client.post(url).json(body).build()?;
let request = self.client.post(url).json(&body).build()?;
let result = self.client.execute(request).await;
let response = result?;
match response.status().as_u16() {
@ -159,13 +163,13 @@ impl Client {
}
}
#[doc = "Sends a `POST` request to `/report/output`"]
///Sends a `POST` request to `/report/output`
pub async fn report_output<'a>(
&'a self,
body: &'a types::ReportOutputBody,
) -> Result<ResponseValue<types::ReportResult>, Error<()>> {
let url = format!("{}/report/output", self.baseurl,);
let request = self.client.post(url).json(body).build()?;
let request = self.client.post(url).json(&body).build()?;
let result = self.client.execute(request).await;
let response = result?;
match response.status().as_u16() {
@ -174,13 +178,13 @@ impl Client {
}
}
#[doc = "Sends a `POST` request to `/report/start`"]
///Sends a `POST` request to `/report/start`
pub async fn report_start<'a>(
&'a self,
body: &'a types::ReportStartBody,
) -> Result<ResponseValue<types::ReportResult>, Error<()>> {
let url = format!("{}/report/start", self.baseurl,);
let request = self.client.post(url).json(body).build()?;
let request = self.client.post(url).json(&body).build()?;
let result = self.client.execute(request).await;
let response = result?;
match response.status().as_u16() {

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,3 +1,5 @@
#[allow(unused_imports)]
use progenitor_client::encode_path;
pub use progenitor_client::{ByteStream, Error, ResponseValue};
pub mod types {
use serde::{Deserialize, Serialize};
@ -20,7 +22,7 @@ pub mod types {
pub yes: bool,
}
#[doc = "Error information from a response."]
///Error information from a response.
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Error {
#[serde(default, skip_serializing_if = "Option::is_none")]
@ -32,8 +34,8 @@ pub mod types {
#[derive(Clone)]
pub struct Client {
baseurl: String,
client: reqwest::Client,
pub(crate) baseurl: String,
pub(crate) client: reqwest::Client,
}
impl Client {
@ -61,14 +63,16 @@ impl Client {
pub fn client(&self) -> &reqwest::Client {
&self.client
}
}
#[doc = "Sends a `POST` request to ``"]
impl Client {
///Sends a `POST` request to ``
pub async fn default_params<'a>(
&'a self,
body: &'a types::BodyWithDefaults,
) -> Result<ResponseValue<ByteStream>, Error<ByteStream>> {
let url = format!("{}", self.baseurl,);
let request = self.client.post(url).json(body).build()?;
let request = self.client.post(url).json(&body).build()?;
let result = self.client.execute(request).await;
let response = result?;
match response.status().as_u16() {

View File

@ -1,7 +1,9 @@
#[allow(unused_imports)]
use progenitor_client::encode_path;
pub use progenitor_client::{ByteStream, Error, ResponseValue};
pub mod types {
use serde::{Deserialize, Serialize};
#[doc = "Error information from a response."]
///Error information from a response.
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Error {
#[serde(default, skip_serializing_if = "Option::is_none")]
@ -13,8 +15,8 @@ pub mod types {
#[derive(Clone)]
pub struct Client {
baseurl: String,
client: reqwest::Client,
pub(crate) baseurl: String,
pub(crate) client: reqwest::Client,
}
impl Client {
@ -42,8 +44,10 @@ impl Client {
pub fn client(&self) -> &reqwest::Client {
&self.client
}
}
#[doc = "Sends a `GET` request to ``"]
impl Client {
///Sends a `GET` request to ``
pub async fn freeform_response<'a>(
&'a self,
) -> Result<ResponseValue<ByteStream>, Error<ByteStream>> {

View File

@ -1,7 +1,9 @@
#[allow(unused_imports)]
use progenitor_client::encode_path;
pub use progenitor_client::{ByteStream, Error, ResponseValue};
pub mod types {
use serde::{Deserialize, Serialize};
#[doc = "Error information from a response."]
///Error information from a response.
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Error {
#[serde(default, skip_serializing_if = "Option::is_none")]
@ -13,8 +15,8 @@ pub mod types {
#[derive(Clone)]
pub struct Client {
baseurl: String,
client: reqwest::Client,
pub(crate) baseurl: String,
pub(crate) client: reqwest::Client,
}
impl Client {
@ -42,8 +44,10 @@ impl Client {
pub fn client(&self) -> &reqwest::Client {
&self.client
}
}
#[doc = "Sends a `GET` request to `/{ref}/{type}/{trait}`"]
impl Client {
///Sends a `GET` request to `/{ref}/{type}/{trait}`
pub async fn renamed_parameters<'a>(
&'a self,
ref_: &'a str,
@ -56,9 +60,9 @@ impl Client {
let url = format!(
"{}/{}/{}/{}",
self.baseurl,
progenitor_client::encode_path(&ref_.to_string()),
progenitor_client::encode_path(&type_.to_string()),
progenitor_client::encode_path(&trait_.to_string()),
encode_path(&ref_.to_string()),
encode_path(&type_.to_string()),
encode_path(&trait_.to_string()),
);
let mut query = Vec::new();
query.push(("if", if_.to_string()));

View File

@ -2,36 +2,61 @@
use std::{fs::File, path::PathBuf};
use progenitor_impl::Generator;
use progenitor_impl::{
GenerationSettings, Generator, InterfaceStyle, TagStyle,
};
#[track_caller]
fn verify_file(openapi_file: &str) {
fn verify_apis(openapi_file: &str) {
let mut in_path = PathBuf::from("../sample_openapi");
in_path.push(format!("{}.json", openapi_file));
let file = File::open(in_path).unwrap();
let spec = serde_json::from_reader(file).unwrap();
let mut generator = Generator::new();
let output = generator.generate_text(&spec).unwrap();
let mut generator = Generator::default();
let output = generator.generate_text_normalize_comments(&spec).unwrap();
expectorate::assert_contents(
format!("tests/output/{}.out", openapi_file),
format!("tests/output/{}-positional.out", openapi_file),
&output,
)
);
let mut generator = Generator::new(
GenerationSettings::default()
.with_interface(InterfaceStyle::Builder)
.with_tag(TagStyle::Merged),
);
let output = generator.generate_text_normalize_comments(&spec).unwrap();
expectorate::assert_contents(
format!("tests/output/{}-builder.out", openapi_file),
&output,
);
let mut generator = Generator::new(
GenerationSettings::default()
.with_interface(InterfaceStyle::Builder)
.with_tag(TagStyle::Separate),
);
let output = generator.generate_text_normalize_comments(&spec).unwrap();
expectorate::assert_contents(
format!("tests/output/{}-builder-tagged.out", openapi_file),
&output,
);
}
#[test]
fn test_keeper() {
verify_file("keeper");
verify_apis("keeper");
}
#[test]
fn test_buildomat() {
verify_file("buildomat");
verify_apis("buildomat");
}
#[test]
fn test_nexus() {
verify_file("nexus");
verify_apis("nexus");
}
// TODO this file is full of inconsistencies and incorrectly specified types.
@ -40,5 +65,5 @@ fn test_nexus() {
#[ignore]
#[test]
fn test_github() {
verify_file("api.github.com");
verify_apis("api.github.com");
}

View File

@ -64,8 +64,8 @@ fn test_renamed_parameters() {
let spec = serde_json::from_str::<OpenAPI>(out).unwrap();
let mut generator = Generator::new();
let output = generator.generate_text(&spec).unwrap();
let mut generator = Generator::default();
let output = generator.generate_text_normalize_comments(&spec).unwrap();
expectorate::assert_contents(
format!("tests/output/{}.out", "test_renamed_parameters"),
&output,
@ -97,8 +97,8 @@ fn test_freeform_response() {
let out = from_utf8(&out).unwrap();
let spec = serde_json::from_str::<OpenAPI>(out).unwrap();
let mut generator = Generator::new();
let output = generator.generate_text(&spec).unwrap();
let mut generator = Generator::default();
let output = generator.generate_text_normalize_comments(&spec).unwrap();
expectorate::assert_contents(
format!("tests/output/{}.out", "test_freeform_response"),
&output,
@ -145,8 +145,8 @@ fn test_default_params() {
let out = from_utf8(&out).unwrap();
let spec = serde_json::from_str::<OpenAPI>(out).unwrap();
let mut generator = Generator::new();
let output = generator.generate_text(&spec).unwrap();
let mut generator = Generator::default();
let output = generator.generate_text_normalize_comments(&spec).unwrap();
expectorate::assert_contents(
format!("tests/output/{}.out", "test_default_params"),
&output,

View File

@ -1,7 +1,7 @@
[package]
name = "progenitor-macro"
version = "0.1.2-dev"
edition = "2018"
edition = "2021"
license = "MPL-2.0"
repository = "https://github.com/oxidecomputer/progenitor.git"
description = "An OpenAPI client generator - macros"

View File

@ -4,7 +4,9 @@ use std::path::Path;
use openapiv3::OpenAPI;
use proc_macro::TokenStream;
use progenitor_impl::Generator;
use progenitor_impl::{
GenerationSettings, Generator, InterfaceStyle, TagStyle,
};
use quote::{quote, ToTokens};
use serde::Deserialize;
use serde_tokenstream::ParseWrapper;
@ -22,7 +24,8 @@ use syn::LitStr;
/// ```ignore
/// generate_api!(
/// spec = "path/to/spec.json",
/// [ inner_type = path::to:Type, ]
/// [ interface = ( Positional | Builder ), ]
/// [ tags = ( Merged | Separate ), ]
/// [ pre_hook = closure::or::path::to::function, ]
/// [ post_hook = closure::or::path::to::function, ]
/// [ derives = [ path::to::DeriveMacro ], ]
@ -32,6 +35,14 @@ use syn::LitStr;
/// The `spec` key is required; it is the OpenAPI document from which the
/// client is derived.
///
/// The optional `interface` lets you specify either a `Positional` argument or
/// `Builder` argument style; `Positional` is the default.
///
/// The optional `tags` may be `Merged` in which case all operations are
/// methods on the `Client` struct or `Separate` in which case each tag is
/// represented by an "extension trait" that `Client` implements. The default
/// is `Merged`.
///
/// The optional `inner_type` is for ancillary data, stored with the generated
/// client that can be usd by the pre and post hooks.
///
@ -58,8 +69,12 @@ pub fn generate_api(item: TokenStream) -> TokenStream {
}
#[derive(Deserialize)]
struct Settings {
struct MacroSettings {
spec: ParseWrapper<LitStr>,
#[serde(default)]
interface: InterfaceStyle,
#[serde(default)]
tags: TagStyle,
inner_type: Option<ParseWrapper<syn::Type>>,
pre_hook: Option<ParseWrapper<ClosureOrPath>>,
post_hook: Option<ParseWrapper<ClosureOrPath>>,
@ -67,6 +82,18 @@ struct Settings {
derives: Vec<ParseWrapper<syn::Path>>,
}
#[derive(Deserialize)]
enum GenerationStyle {
Positional,
Builder,
}
impl Default for GenerationStyle {
fn default() -> Self {
Self::Positional
}
}
#[derive(Debug)]
struct ClosureOrPath(proc_macro2::TokenStream);
@ -90,25 +117,35 @@ impl syn::parse::Parse for ClosureOrPath {
}
fn do_generate_api(item: TokenStream) -> Result<TokenStream, syn::Error> {
let (spec, inner_type, pre_hook, post_hook, derives) =
if let Ok(spec) = syn::parse::<LitStr>(item.clone()) {
(spec, None, None, None, Vec::new())
} else {
let Settings {
spec,
inner_type,
pre_hook,
post_hook,
derives,
} = serde_tokenstream::from_tokenstream(&item.into())?;
(
spec.into_inner(),
inner_type.map(|x| x.into_inner()),
pre_hook.map(|x| x.into_inner()),
post_hook.map(|x| x.into_inner()),
derives.into_iter().map(ParseWrapper::into_inner).collect(),
)
};
let (spec, settings) = if let Ok(spec) = syn::parse::<LitStr>(item.clone())
{
(spec, GenerationSettings::default())
} else {
let MacroSettings {
spec,
interface,
tags,
inner_type,
pre_hook,
post_hook,
derives,
} = serde_tokenstream::from_tokenstream(&item.into())?;
let mut settings = GenerationSettings::default();
settings.with_interface(interface);
settings.with_tag(tags);
inner_type.map(|inner_type| {
settings.with_inner_type(inner_type.to_token_stream())
});
pre_hook
.map(|pre_hook| settings.with_pre_hook(pre_hook.into_inner().0));
post_hook
.map(|post_hook| settings.with_post_hook(post_hook.into_inner().0));
derives.into_iter().for_each(|derive| {
settings.with_derive(derive.to_token_stream());
});
(spec.into_inner(), settings)
};
let dir = std::env::var("CARGO_MANIFEST_DIR").map_or_else(
|_| std::env::current_dir().unwrap(),
@ -122,31 +159,22 @@ fn do_generate_api(item: TokenStream) -> Result<TokenStream, syn::Error> {
serde_json::from_reader(std::fs::File::open(&path).map_err(|e| {
syn::Error::new(
spec.span(),
format!("couldn't read file {}: {}", path_str, e.to_string()),
format!("couldn't read file {}: {}", path_str, e),
)
})?)
.map_err(|e| {
syn::Error::new(
spec.span(),
format!("failed to parse {}: {}", path_str, e.to_string()),
format!("failed to parse {}: {}", path_str, e),
)
})?;
let mut builder = Generator::new();
inner_type.map(|inner_type| {
builder.with_inner_type(inner_type.to_token_stream())
});
pre_hook.map(|pre_hook| builder.with_pre_hook(pre_hook.0));
post_hook.map(|post_hook| builder.with_post_hook(post_hook.0));
derives.into_iter().for_each(|derive| {
builder.with_derive(derive.to_token_stream());
});
let mut builder = Generator::new(&settings);
let code = builder.generate_tokens(&oapi).map_err(|e| {
syn::Error::new(
spec.span(),
format!("generation error for {}: {}", spec.value(), e.to_string()),
format!("generation error for {}: {}", spec.value(), e),
)
})?;

View File

@ -1,7 +1,7 @@
[package]
name = "progenitor"
version = "0.1.2-dev"
edition = "2018"
edition = "2021"
license = "MPL-2.0"
repository = "https://github.com/oxidecomputer/progenitor.git"
description = "An OpenAPI client generator"
@ -11,10 +11,10 @@ progenitor-client = { version = "0.1.2-dev", path = "../progenitor-client" }
progenitor-impl = { version = "0.1.2-dev", path = "../progenitor-impl" }
progenitor-macro = { version = "0.1.2-dev", path = "../progenitor-macro" }
anyhow = "1.0"
getopts = "0.2"
openapiv3 = "1.0.0"
serde = { version = "1.0", features = [ "derive" ] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
clap = { version = "3.2.8", features = ["derive"] }
[dev-dependencies]
chrono = { version = "0.4", features = ["serde"] }

View File

@ -12,5 +12,8 @@
pub use progenitor_client;
pub use progenitor_impl::Error;
pub use progenitor_impl::GenerationSettings;
pub use progenitor_impl::Generator;
pub use progenitor_impl::InterfaceStyle;
pub use progenitor_impl::TagStyle;
pub use progenitor_macro::generate_api;

View File

@ -8,10 +8,64 @@ use std::{
};
use anyhow::{bail, Result};
use clap::{Parser, ValueEnum};
use openapiv3::OpenAPI;
use progenitor::Generator;
use progenitor::{GenerationSettings, Generator, InterfaceStyle, TagStyle};
use serde::Deserialize;
#[derive(Parser)]
struct Args {
/// OpenAPI definition document (JSON)
#[clap(short = 'i', long)]
input: String,
/// Output directory for Rust crate
#[clap(short = 'o', long)]
output: String,
/// Target Rust crate name
#[clap(short = 'n', long)]
name: String,
/// Target Rust crate version
#[clap(short = 'v', long)]
version: String,
/// SDK interface style
#[clap(value_enum, long, default_value_t = InterfaceArg::Positional)]
interface: InterfaceArg,
/// SDK tag style
#[clap(value_enum, long, default_value_t = TagArg::Merged)]
tags: TagArg,
}
#[derive(Copy, Clone, ValueEnum)]
enum InterfaceArg {
Positional,
Builder,
}
impl From<InterfaceArg> for InterfaceStyle {
fn from(arg: InterfaceArg) -> Self {
match arg {
InterfaceArg::Positional => InterfaceStyle::Positional,
InterfaceArg::Builder => InterfaceStyle::Builder,
}
}
}
#[derive(Copy, Clone, ValueEnum)]
enum TagArg {
Merged,
Separate,
}
impl From<TagArg> for TagStyle {
fn from(arg: TagArg) -> Self {
match arg {
TagArg::Merged => TagStyle::Merged,
TagArg::Separate => TagStyle::Separate,
}
}
}
fn save<P>(p: P, data: &str) -> Result<()>
where
P: AsRef<Path>,
@ -28,30 +82,15 @@ where
}
fn main() -> Result<()> {
let mut opts = getopts::Options::new();
opts.parsing_style(getopts::ParsingStyle::StopAtFirstFree);
opts.reqopt("i", "", "OpenAPI definition document (JSON)", "INPUT");
opts.reqopt("o", "", "Generated Rust crate directory", "OUTPUT");
opts.reqopt("n", "", "Target Rust crate name", "CRATE");
opts.reqopt("v", "", "Target Rust crate version", "VERSION");
let args = Args::parse();
let api = load_api(&args.input)?;
let args = match opts.parse(std::env::args().skip(1)) {
Ok(args) => {
if !args.free.is_empty() {
eprintln!("{}", opts.usage("progenitor"));
bail!("unexpected positional arguments");
}
args
}
Err(e) => {
eprintln!("{}", opts.usage("progenitor"));
bail!(e);
}
};
let api = load_api(&args.opt_str("i").unwrap())?;
let mut builder = Generator::new();
//let mut builder = Generator::default();
let mut builder = Generator::new(
GenerationSettings::default()
.with_interface(args.interface.into())
.with_tag(args.tags.into()),
);
match builder.generate_text(&api) {
Ok(api_code) => {
@ -67,13 +106,13 @@ fn main() -> Result<()> {
println!("-----------------------------------------------------");
println!();
let name = args.opt_str("n").unwrap();
let version = args.opt_str("v").unwrap();
let name = &args.name;
let version = &args.version;
/*
* Create the top-level crate directory:
*/
let root = PathBuf::from(args.opt_str("o").unwrap());
let root = PathBuf::from(&args.output);
std::fs::create_dir_all(&root)?;
/*

View File

@ -1,3 +1,41 @@
// Copyright 2021 Oxide Computer Company
// Copyright 2022 Oxide Computer Company
progenitor::generate_api!("../sample_openapi/buildomat.json");
mod positional {
progenitor::generate_api!("../sample_openapi/buildomat.json");
fn _ignore() {
let _ = Client::new("").worker_task_upload_chunk("task", vec![0]);
}
}
mod builder_untagged {
progenitor::generate_api!(
spec = "../sample_openapi/buildomat.json",
interface = Builder,
tags = Merged,
);
fn _ignore() {
let _ = Client::new("")
.worker_task_upload_chunk()
.task("task")
.body(vec![0])
.send();
}
}
mod builder_tagged {
progenitor::generate_api!(
spec = "../sample_openapi/buildomat.json",
interface = Builder,
tags = Separate,
);
fn _ignore() {
let _ = Client::new("")
.worker_task_upload_chunk()
.task("task")
.body(vec![0])
.send();
}
}

View File

@ -1,3 +1,48 @@
// Copyright 2021 Oxide Computer Company
// Copyright 2022 Oxide Computer Company
progenitor::generate_api!("../sample_openapi/keeper.json");
mod positional {
progenitor::generate_api!("../sample_openapi/keeper.json");
fn _ignore() {
let _ = Client::new("").enrol(&types::EnrolBody {
host: "".to_string(),
key: "".to_string(),
});
}
}
mod builder_untagged {
progenitor::generate_api!(
spec = "../sample_openapi/keeper.json",
interface = Builder,
tags = Merged,
);
fn _ignore() {
let _ = Client::new("")
.enrol()
.body(types::EnrolBody {
host: "".to_string(),
key: "".to_string(),
})
.send();
}
}
mod builder_tagged {
progenitor::generate_api!(
spec = "../sample_openapi/keeper.json",
interface = Builder,
tags = Separate,
);
fn _ignore() {
let _ = Client::new("")
.enrol()
.body(types::EnrolBody {
host: "".to_string(),
key: "".to_string(),
})
.send();
}
}

View File

@ -1,23 +1,74 @@
// Copyright 2021 Oxide Computer Company
progenitor::generate_api!("../sample_openapi/nexus.json");
mod positional {
use futures::StreamExt;
pub async fn iteration_example() {
let client = Client::new("xxx");
mod nexus_client {
progenitor::generate_api!("../sample_openapi/nexus.json");
}
let bod = types::LoginParams {
username: "ahl".to_string(),
};
use nexus_client::{types, Client};
let mut stream = client.spoof_login(&bod).await.unwrap();
loop {
use futures::TryStreamExt;
match stream.try_next().await {
Ok(Some(bytes)) => println!("bytes: {:?}", bytes),
Ok(None) => break,
Err(e) => panic!("{}", e),
}
fn _ignore() {
let _ = async {
let client = Client::new("");
let org = types::Name("org".to_string());
let project = types::Name("project".to_string());
let instance = types::Name("instance".to_string());
let stream = client.instance_disks_get_stream(
&org, &project, &instance, None, None,
);
let _ = stream.collect::<Vec<_>>();
};
}
}
mod builder_untagged {
use futures::StreamExt;
mod nexus_client {
progenitor::generate_api!(
spec = "../sample_openapi/nexus.json",
interface = Builder,
tags = Merged,
);
}
use nexus_client::{types, Client};
pub fn _ignore() {
let client = Client::new("");
let stream = client
.instance_disks_get()
.organization_name(types::Name("org".to_string()))
.project_name(types::Name("project".to_string()))
.instance_name(types::Name("instance".to_string()))
.stream();
let _ = stream.collect::<Vec<_>>();
}
}
mod builder_tagged {
use futures::StreamExt;
mod nexus_client {
progenitor::generate_api!(
spec = "../sample_openapi/nexus.json",
interface = Builder,
tags = Separate,
);
}
use nexus_client::{types, Client, ClientInstancesExt};
fn _ignore() {
let client = Client::new("");
let stream = client
.instance_disks_get()
.organization_name(types::Name("org".to_string()))
.project_name(types::Name("project".to_string()))
.instance_name(types::Name("instance".to_string()))
.stream();
let _ = stream.collect::<Vec<_>>();
}
}