Go to file
Adam H. Leventhal 24aa57a243 typo 2022-02-08 09:02:00 -08:00
.github buildomat: add basic build/test job mimicking the Actions workflow 2021-11-19 12:14:31 -08:00
docs Add a wrapper types for success and error responses (#26) 2022-02-08 08:59:38 -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 Add a wrapper types for success and error responses (#26) 2022-02-08 08:59:38 -08:00
progenitor-client Add a wrapper types for success and error responses (#26) 2022-02-08 08:59:38 -08:00
progenitor-impl Add a wrapper types for success and error responses (#26) 2022-02-08 08:59:38 -08:00
progenitor-macro Add a wrapper types for success and error responses (#26) 2022-02-08 08:59:38 -08:00
sample_openapi Add a wrapper types for success and error responses (#26) 2022-02-08 08:59:38 -08:00
.gitignore initial commit 2021-06-19 07:36:13 +00:00
Cargo.lock Add a wrapper types for success and error responses (#26) 2022-02-08 08:59:38 -08:00
Cargo.toml Allow consumers to specify additional derive macros for generated types (#21) 2022-01-14 18:01:59 -08:00
README.md typo 2022-02-08 09:02:00 -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_penapi/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.