Skip to content

karencfv/progenitor

 
 

Repository files navigation

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
    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::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.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.

About

An OpenAPI client generator

Resources

Stars

Watchers

Forks

Packages

No packages published

Languages

  • Rust 99.8%
  • Shell 0.2%