Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Rust Generator Configuration

All three Rust backends (rust-reqwest, rust-ureq, rust-aioduct) share the same configuration options.

The rust-aioduct backend has an additional [aioduct] section for controlling aioduct-specific features.

Generated Error Handling

Every operation keeps its generated error enum, so callers can match a specific status and use its typed body:

#![allow(unused)]
fn main() {
match api.create_resource(&request).await {
    Err(CreateResourceError::NotFound(error)) => {
        if let Ok(body) = error.body() {
            println!("missing resource: {}", body.message);
        }
    }
    Err(error) => return Err(error.into()),
    Ok(response) => use_response(response),
}
}

Each operation error also implements Into<ApiCallError>. Use the common type when a workflow calls multiple operations and does not need their distinct body types:

#![allow(unused)]
fn main() {
use generated_sdk::runtime::error::ApiCallError;

async fn run_workflow(
    api: &ResourcesApi<'_>,
    request: &CreateResourceRequest,
    resource_id: &str,
) -> Result<(), ApiCallError> {
    api.create_resource(request).await?;
    api.refresh_resource(resource_id).await?;
    Ok(())
}
}

ApiCallError exposes the operation identifier (generated from the HTTP method and path if the OpenAPI document omits one) and optional HTTP status, native headers, and raw response body. Transport errors and error-body decoding failures remain available through std::error::Error::source().

Conversion consumes the operation-specific error, performs one heap allocation for the pointer-sized erased representation, and erases its decoded payload type. Operation-specific errors remain inline until conversion. Match the operation error before conversion when its decoded payload is needed.

thiserror

thiserror does not chain two From conversions for ?. A single generic adapter on the application error converts every generated operation error directly:

#![allow(unused)]
fn main() {
#[derive(Debug, thiserror::Error)]
#[error("SDK request failed: {source}")]
struct AppError {
    #[source]
    source: ApiCallError,
}

impl<E> From<E> for AppError
where
    E: Into<ApiCallError>,
{
    fn from(error: E) -> Self {
        Self {
            source: error.into(),
        }
    }
}
}

Do not also put #[from] on the ApiCallError field; the generic implementation already includes ApiCallError itself.

SNAFU

SNAFU 0.9’s generic source conversion provides the same direct ? behavior:

#![allow(unused)]
fn main() {
#[derive(Debug, snafu::Snafu)]
#[snafu(context(false))]
struct AppError {
    #[snafu(source(from(generic)))]
    source: ApiCallError,
}
}

Full Example

[generators.rust-reqwest]
crate_name = "my-api-client"
workspace_mode = true
workspace_deps = "workspace_version"

[generators.rust-reqwest.extra_derives.structs]
derives = ["PartialEq", "Eq"]

[generators.rust-reqwest.extra_derives.enums]
derives = ["Hash"]

[generators.rust-reqwest.extra_derives.unions]
derives = ["PartialEq"]

[generators.rust-reqwest.extra_derives.response_structs]
derives = ["PartialEq"]

[generators.rust-reqwest.extra_derives.per_type.MySpecialSchema]
derives = ["Default"]

[generators.rust-reqwest.utoipa]
enabled = true
dependency = '{ version = "5" }'

Options Reference

crate_name / package_name

Override the generated crate name. Defaults to the spec title converted to kebab-case.

crate_name = "my-api-client"

workspace_mode

When true, the generated Cargo.toml uses version.workspace = true and edition.workspace = true instead of inline values. Also emits [lints] workspace = true.

workspace_mode = true

workspace_deps

Controls how dependencies are declared in the generated Cargo.toml.

ModeBehavior
"explicit" (default)Inline version specs: serde = { version = "1", features = ["derive"] }
"workspace_version"Workspace with features: serde = { workspace = true, features = ["derive"] }
"full"Fully delegated: serde.workspace = true
workspace_deps = "workspace_version"

extra_derives

Add custom derive macros to generated types. Each category targets a different schema kind:

  • structs — object schemas
  • enums — string and integer enums
  • unions — tagged unions (external tagging)
  • response_structs — per-operation response type wrappers
[generators.rust-reqwest.extra_derives.structs]
derives = ["PartialEq", "Eq"]
dependencies = { fake = '"2"' }

The dependencies field adds entries to the generated Cargo.toml.

extra_derives.per_type

Target a specific schema by name:

[generators.rust-reqwest.extra_derives.per_type.UserProfile]
derives = ["Default", "Hash"]

utoipa

Native utoipa integration for OpenAPI schema generation at runtime.

[generators.rust-reqwest.utoipa]
enabled = true
dependency = '{ version = "5" }'

When enabled:

  • Structs, string enums, integer enums, intersections, and aliases get #[derive(utoipa::ToSchema)]
  • Tagged unions (internal/adjacent) and untagged unions get manual impl utoipa::PartialSchema + ToSchema using OneOfBuilder (the derive macro doesn’t support these patterns)
  • The utoipa crate is added to generated Cargo.toml using the dependency spec
  • Variant schemas for internal/adjacent tagged unions are emitted as standalone files (not inlined) so they can be referenced by PartialSchema

The dependency field accepts any valid TOML inline table or string that would appear after utoipa = in Cargo.toml. If omitted, defaults to "*".

You do NOT need to add utoipa::ToSchema to extra_derives when using this config. The [utoipa] section handles everything, including the cases where the derive macro cannot be used.

aioduct (rust-aioduct only)

Configure aioduct-specific dependency features for the generated crate. This section only applies to the rust-aioduct generator.

[generators.rust-aioduct.aioduct]
version = "0.2"
runtime = "tokio"
tls = "rustls-ring"
compression = ["gzip", "brotli", "zstd"]
features = ["tracing", "http3"]

All fields are optional. Defaults: runtime = "tokio", tls = "rustls-ring", no compression, no extra features. The json feature is always included.

version

Override the aioduct version requirement. Defaults to "0.2".

runtime

Which async runtime to use. One of:

ValueDescription
"tokio" (default)Tokio runtime
"smol"smol runtime
"compio"compio (io_uring) runtime

tls

TLS backend selection:

ValueDescription
"rustls-ring" (default)rustls with ring crypto
"rustls-aws-lc-rs"rustls with AWS-LC crypto
"false"Disable TLS (HTTP-only)

compression

List of decompression codecs to enable. Valid values: "gzip", "brotli", "zstd", "deflate".

compression = ["gzip", "zstd"]

features

Pass-through feature flags appended to the aioduct dependency. Use this for features not covered by the structured fields above (e.g., "tracing", "otel", "http3", "hickory-dns", "doh", "dot", "blocking", "tower").

features = ["tracing", "http3", "blocking"]