Skip to content

Commit

Permalink
Use re-exports to make depending on propolis-client less cumbersome
Browse files Browse the repository at this point in the history
  • Loading branch information
lif committed Sep 17, 2022
1 parent f449793 commit 13bde6d
Show file tree
Hide file tree
Showing 25 changed files with 167 additions and 95 deletions.
21 changes: 8 additions & 13 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

33 changes: 8 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,23 +35,6 @@ You'll need to add add the following to `Cargo.toml`:
```diff
[dependencies]
+progenitor = { git = "https://github.com/oxidecomputer/progenitor" }
+reqwest = { version = "0.11", features = ["json", "stream"] }
+serde = { version = "1.0", features = ["derive"] }
```

In addition, if the OpenAPI document contains string types with the `format`
field set to `date` or `date-time`, include

```diff
[dependencies]
+chrono = { version = "0.4", features = ["serde"] }
```

Similarly if there is a `format` field set to `uuid`:

```diff
[dependencies]
+uuid = { version = "1.0.0", features = ["serde", "v4"] }
```

The macro has some additional fancy options to control the generated code:
Expand Down Expand Up @@ -108,20 +91,15 @@ You'll need to add add the following to `Cargo.toml`:
```diff
[dependencies]
+progenitor-client = { git = "https://github.com/oxidecomputer/progenitor" }
+reqwest = { version = "0.11", features = ["json", "stream"] }
+serde = { version = "1.0", features = ["derive"] }

[build-dependencies]
+progenitor = { git = "https://github.com/oxidecomputer/progenitor" }
+serde_json = "1.0"
```

(`chrono` and `uuid` as above)

Note that `progenitor` is used by `build.rs`, but the generated code required
`progenitor-client`.


### Static Crate

Progenitor can be run to emit a stand-alone crate for the generated client.
Expand All @@ -146,14 +124,14 @@ For example:

This will produce a package in the specified directory. The output has no
persistent dependency on Progenitor including the `progenitor-client` crate.
Here's a excerpt from the emitted `Cargo.toml`:
Here's an excerpt from the emitted `Cargo.toml`:

```toml
[dependencies]
chrono = { version = "0.4", features = ["serde"] }
futures = "0.3"
percent-encoding = "2.1"
reqwest = { version = "0.11", features = ["json", "stream"] }
reqwest = { git = "https://github.com/seanmonstar/reqwest", rev = "6ceb23958c8b6d7f1d8ee093f0ad73184d133d40", features = ["json", "stream"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
uuid = { version = ">=0.8.0, <2.0.0", features = ["serde", "v4"] }
Expand All @@ -162,6 +140,11 @@ uuid = { version = ">=0.8.0, <2.0.0", features = ["serde", "v4"] }
Note that there is a dependency on `percent-encoding` which macro- and
build.rs-generated clients is included from `progenitor-client`.

Additionally, note that `chrono` is only required if the OpenAPI document
contains string types with the `format` field set to `date` or `date-time`,
and `uuid` is only required if there is a `format` field set to `uuid`.
(Similarly, `base64` and `rand` would've been included if the API included
any endpoints using the nonstandard Dropshot extension for Websockets.)

## Generation Styles

Expand Down Expand Up @@ -290,4 +273,4 @@ let result = client
```

Consumers do not need to specify parameters and struct properties that are not
required or for which the API specifies defaults. Neat!
required or for which the API specifies defaults. Neat!
5 changes: 0 additions & 5 deletions example-macro/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,5 @@ authors = ["Adam H. Leventhal <ahl@oxidecomputer.com>"]
edition = "2021"

[dependencies]
chrono = { version = "0.4", features = ["serde"] }
progenitor = { path = "../progenitor" }
reqwest = { git = "https://github.com/seanmonstar/reqwest", rev = "6ceb23958c8b6d7f1d8ee093f0ad73184d133d40", features = ["json", "stream"] }
#reqwest = { version = "0.11", features = ["json", "stream"] }
schemars = { version = "0.8.10", features = ["uuid1"] }
serde = { version = "1.0", features = ["derive"] }
uuid = { version = "1.0", features = ["serde", "v4"] }
5 changes: 3 additions & 2 deletions progenitor-client/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@ bytes = "1.2.1"
futures-core = "0.3.24"
percent-encoding = "2.2"
reqwest = { git = "https://github.com/seanmonstar/reqwest", rev = "6ceb23958c8b6d7f1d8ee093f0ad73184d133d40", features = ["json", "stream"] }
#reqwest = { version = "0.11", default-features = false, features = ["json", "stream"] }
serde = "1.0"
chrono = { version = "0.4", features = ["serde"] }
uuid = { version = "1.0.0", features = ["serde", "v4"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
serde_urlencoded = "0.7.1"
base64 = "0.13"
Expand Down
9 changes: 9 additions & 0 deletions progenitor-client/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,15 @@ mod progenitor_client;

pub use crate::progenitor_client::*;

pub mod deps {
pub use ::base64;
pub use ::chrono;
pub use ::rand;
pub use ::reqwest;
pub use ::serde;
pub use ::uuid;
}

// For stand-alone crates, rather than adding a dependency on
// progenitor-client, we simply dump the code right in. This means we don't
// need to determine the provenance of progenitor (crates.io, github, etc.)
Expand Down
5 changes: 0 additions & 5 deletions progenitor-client/src/progenitor_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -401,8 +401,3 @@ impl<E> RequestBuilderExt<E> for RequestBuilder {
})?))
}
}

#[doc(hidden)]
pub fn generate_websocket_key() -> String {
base64::encode(rand::random::<[u8; 16]>())
}
4 changes: 2 additions & 2 deletions progenitor-impl/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ serde_json = "1.0"
syn = { version = "1.0", features = ["parsing"] }
thiserror = "1.0"
# To publish, use a numbered version
typify = "0.0.10"
#typify = { git = "https://github.com/oxidecomputer/typify" }
#typify = "0.0.10"
typify = { git = "https://github.com/oxidecomputer/typify" }
unicode-ident = "1.0.3"

[dev-dependencies]
Expand Down
69 changes: 64 additions & 5 deletions progenitor-impl/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ pub struct Generator {
type_space: TypeSpace,
settings: GenerationSettings,
uses_futures: bool,
uses_websockets: bool,
}

#[derive(Default, Clone)]
Expand All @@ -48,6 +49,7 @@ pub struct GenerationSettings {
pre_hook: Option<TokenStream>,
post_hook: Option<TokenStream>,
extra_derives: Vec<String>,
serde_crate_location: Option<String>,
}

#[derive(Clone, Deserialize, PartialEq, Eq)]
Expand Down Expand Up @@ -108,6 +110,19 @@ impl GenerationSettings {
self.extra_derives.push(derive.to_string());
self
}

pub fn with_serde_crate_location(
&mut self,
crate_location: impl ToString,
) -> &mut Self {
self.serde_crate_location = Some(crate_location.to_string());
self
}

pub fn without_serde_crate_location(&mut self) -> &mut Self {
self.serde_crate_location = None;
self
}
}

impl Default for Generator {
Expand All @@ -118,6 +133,7 @@ impl Default for Generator {
),
settings: Default::default(),
uses_futures: Default::default(),
uses_websockets: Default::default(),
}
}
}
Expand All @@ -128,13 +144,17 @@ impl Generator {
type_settings
.with_type_mod("types")
.with_struct_builder(settings.interface == InterfaceStyle::Builder);
if let Some(crate_location) = &settings.serde_crate_location {
type_settings.with_serde_crate_location(crate_location.to_owned());
}
settings.extra_derives.iter().for_each(|derive| {
let _ = type_settings.with_derive(derive.clone());
});
Self {
type_space: TypeSpace::new(&type_settings),
settings: settings.clone(),
uses_futures: false,
uses_websockets: false,
}
}

Expand Down Expand Up @@ -219,12 +239,13 @@ impl Generator {
ByteStream,
Error,
ResponseValue,
generate_websocket_key
};
#[allow(unused_imports)]
use progenitor_client::{encode_path, RequestBuilderExt};

pub mod types {
#[allow(unused_imports)]
use super::deps::*;
use serde::{Deserialize, Serialize};

// This may be used by some impl Deserialize, but not all.
Expand Down Expand Up @@ -325,8 +346,8 @@ impl Generator {
use super::types;
#[allow(unused_imports)]
use super::{
deps::*,
encode_path,
generate_websocket_key,
ByteStream,
Error,
RequestBuilderExt,
Expand Down Expand Up @@ -363,8 +384,8 @@ impl Generator {
use super::types;
#[allow(unused_imports)]
use super::{
deps::*,
encode_path,
generate_websocket_key,
ByteStream,
Error,
RequestBuilderExt,
Expand All @@ -388,9 +409,25 @@ impl Generator {

/// Render text output.
pub fn generate_text(&mut self, spec: &OpenAPI) -> Result<String> {
self.settings
.with_serde_crate_location("progenitor_client::deps::serde");
self.generate_text_impl(
spec,
rustfmt_wrapper::config::Config::default(),
true,
)
}

/// Render text output without re-exports (for stand-alone crate output)
pub fn generate_text_standalone(
&mut self,
spec: &OpenAPI,
) -> Result<String> {
self.settings.without_serde_crate_location();
self.generate_text_impl(
spec,
rustfmt_wrapper::config::Config::default(),
false,
)
}

Expand All @@ -409,15 +446,33 @@ impl Generator {
wrap_comments: Some(true),
..Default::default()
},
true,
)
}

fn generate_text_impl(
&mut self,
spec: &OpenAPI,
config: rustfmt_wrapper::config::Config,
reexport: bool,
) -> Result<String> {
let output = self.generate_tokens(spec)?;
let mut output = self.generate_tokens(spec)?;
if reexport {
// Add re-exports from progenitor-client such that build.rs users
// don't have to maintain a matching dependency on the same reqwest
// version we use.
output = quote! {
use progenitor_client::deps;
#output
};
} else {
// the generated output has a couple instances of (in this case
// unnecessary) `use super::deps::*;` which we account for with:
output = quote! {
mod deps { /* this space intentionally left blank */ }
#output
};
}

// Format the file with rustfmt.
let content = rustfmt_wrapper::rustfmt_config(config, output).unwrap();
Expand All @@ -437,7 +492,7 @@ impl Generator {
"bytes = \"1.1\"",
"futures-core = \"0.3\"",
"percent-encoding = \"2.1\"",
"reqwest = { version = \"0.11\", features = [\"json\", \"stream\"] }",
"reqwest = { git = \"https://github.com/seanmonstar/reqwest\", rev = \"6ceb23958c8b6d7f1d8ee093f0ad73184d133d40\", features = [\"json\", \"stream\"] }",
"serde = { version = \"1.0\", features = [\"derive\"] }",
"serde_urlencoded = \"0.7\"",
];
Expand All @@ -455,6 +510,10 @@ impl Generator {
if self.uses_futures {
deps.push("futures = \"0.3\"")
}
if self.uses_websockets {
deps.push("base64 = \"0.13\"");
deps.push("rand = \"0.8\"");
}
if self.type_space.uses_serde_json() {
deps.push("serde_json = \"1.0\"")
}
Expand Down

0 comments on commit 13bde6d

Please sign in to comment.