|
||
---|---|---|
.github | ||
docs | ||
example-build | ||
example-macro | ||
progenitor | ||
progenitor-client | ||
progenitor-impl | ||
progenitor-macro | ||
sample_openapi | ||
.gitignore | ||
CHANGELOG.adoc | ||
Cargo.lock | ||
Cargo.toml | ||
README.md | ||
release.toml | ||
rustfmt.toml |
README.md
Progenitor
Progenitor is a Rust crate for generating opinionated clients from API
descriptions specified in the OpenAPI 3.0.x format. It makes use of Rust
futures for async API calls and Streams
for paginated interfaces.
It generates a type called Client
with methods that correspond to the
operations specified in the OpenAPI document.
Using Progenitor
There are three different ways of using the progenitor
crate. The one you
choose will depend on your use case and preferences.
Macro
The simplest way to use Progenitor is via its generate_api!
macro.
In a source file (often main.rs
, lib.rs
, or mod.rs
) simply invoke the
macro:
generate_api!("path/to/openapi_document.json");
You'll need to add add the following to Cargo.toml
:
[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
[dependencies]
+chrono = { version = "0.4", features = ["serde"] }
Similarly if there is a format
field set to uuid
:
[dependencies]
+uuid = { version = "1.0.0", features = ["serde", "v4"] }
The macro has some additional fancy options to control the generated code:
generate_api!(
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
);
Note that the macro will be re-evaluated when the OpenAPI json document changes (when its mtime is updated).
Builder
Progenitor includes an interface appropriate for use in a
build.rs
file. While slightly more onerous than the macro, a builder has the advantage of making the generated code visible.
The build.rs
file should look something like this:
fn main() {
let src = "../sample_openapi/keeper.json";
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::default();
let content = generator.generate_text(&spec).unwrap();
let mut out_file = Path::new(&env::var("OUT_DIR").unwrap()).to_path_buf();
out_file.push("codegen.rs");
fs::write(out_file, content).unwrap();
}
In a source file (often main.rs
, lib.rs
, or mod.rs
) include the generated
code:
include!(concat!(env!("OUT_DIR"), "/codegen.rs"));
You'll need to add add the following to Cargo.toml
:
[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. This ensures no unexpected changes (e.g. from updates to progenitor). It is however, the most manual way to use Progenitor.
Usage:
progenitor
Options:
-i INPUT OpenAPI definition document (JSON)
-o OUTPUT Generated Rust crate directory
-n CRATE Target Rust crate name
-v VERSION Target Rust crate version
For example:
cargo run --bin progenitor -- -i sample_openapi/keeper.json -o keeper -n keeper -v 0.1.0
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
:
[dependencies]
chrono = { version = "0.4", features = ["serde"] }
futures = "0.3"
percent-encoding = "2.1"
reqwest = { version = "0.11", features = ["json", "stream"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
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
.