Go to file
Adam Leventhal eac966effc
move format checking from the command to the library (#179)
2022-08-29 11:16:34 -07:00
.github update actions (#141) 2022-07-26 21:06:31 -07:00
docs Update error documentation (#121) 2022-07-14 18:07:49 +12:00
example-build add an optional builder pattern as well as extension traits for tags when using the builder interface (#86) 2022-07-02 19:09:38 -07:00
example-macro add an optional builder pattern as well as extension traits for tags when using the builder interface (#86) 2022-07-02 19:09:38 -07:00
progenitor move format checking from the command to the library (#179) 2022-08-29 11:16:34 -07:00
progenitor-client Bump futures-core from 0.3.21 to 0.3.23 (#174) 2022-08-22 09:34:40 -07:00
progenitor-impl move format checking from the command to the library (#179) 2022-08-29 11:16:34 -07:00
progenitor-macro add an optional builder pattern as well as extension traits for tags when using the builder interface (#86) 2022-07-02 19:09:38 -07:00
sample_openapi update nexus and typify (#142) 2022-07-26 21:23:01 -07:00
.gitignore Show how to use Client::new_with_client (#119) 2022-07-12 11:19:25 -07:00
CHANGELOG.adoc Add prelude for Client and extension traits (when they exist) (#176) 2022-08-27 08:23:02 -07:00
Cargo.lock sync w/ typify and add a test (#178) 2022-08-27 09:31:35 -07:00
Cargo.toml Typify update for builder structs (#171) 2022-08-18 11:58:55 -07:00
README.md add an optional builder pattern as well as extension traits for tags when using the builder interface (#86) 2022-07-02 19:09:38 -07:00
release.toml update for new cargo release 2022-05-12 23:14:58 -07:00
rustfmt.toml move format checking from the command to the library (#179) 2022-08-29 11:16:34 -07: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 = "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.