Go to file
Adam H. Leventhal fea6396042 dependabot 2022-05-10 23:02:12 -07:00
.github dependabot 2022-05-10 23:02:12 -07:00
docs add helper functions to Error and ResponseValue (#28) 2022-02-23 21:07:30 -08:00
example-build Add a wrapper types for success and error responses (#26) 2022-02-08 08:59:38 -08:00
example-macro Add a wrapper types for success and error responses (#26) 2022-02-08 08:59:38 -08:00
progenitor pull in the most recent typify (#44) 2022-05-09 17:30:08 -07:00
progenitor-client Fix reqwest feature causing a compilation error (#42) 2022-05-09 17:34:25 -07:00
progenitor-impl pull in the most recent typify (#44) 2022-05-09 17:30:08 -07:00
progenitor-macro Add a wrapper types for success and error responses (#26) 2022-02-08 08:59:38 -08:00
sample_openapi improve rustdoc output for generated crates (#37) 2022-03-17 23:54:10 -07:00
.gitignore initial commit 2021-06-19 07:36:13 +00:00
Cargo.lock pull in the most recent typify (#44) 2022-05-09 17:30:08 -07:00
Cargo.toml Handle byte streams for both success and error responses (#35) 2022-03-15 16:11:47 -07:00
README.md Fix typo (#27) 2022-03-07 22:44:51 -08:00
rustfmt.toml rustfmt 2021-06-19 08:08:18 +00:00

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 = "0.8", features = ["serde", "v4"] }

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::new();

    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", features = ["serde", "v4"] }

Note that there is a dependency on percent-encoding which macro- and build.rs-generated clients is included from progenitor-client.