GitHub - gpu-cli/openapi-to-rust: Generate strongly-typed Rust structs, HTTP clients, and SSE streaming clients from OpenAPI 3.1 specs
A Rust code generator that turns OpenAPI 3.1 (and 3.2-experimental) specifications into strongly-typed structs, async HTTP clients, SSE streaming clients, and opt-in Axum server scaffolding — including for the messy, real-world specs everyone actually ships.
We originally built this internally at GPU CLI to generate typed Rust clients for OpenAI, Anthropic, Cloudflare, and other large APIs. After battle-testing it against real-world specs with complex union types, discriminated enums, streaming endpoints, and the occasional spec/API drift, we decided to open source it.
It currently compiles cleanly against 54 real-world specs in specs/ (Stripe, OpenAI, Anthropic, Cloudflare's 14k-schema spec, GitHub, Discord, Microsoft Graph, Spotify, Twilio, …), guarded by CI.
What's new in 0.5
- Server codegen (Axum) — opt-in
[server]section emits a trait per tag, a status-code-typed response enum (withIntoResponse), an SSE-aware variant, and aRouterfactory. Pick operations one-by-one or--all-tag. Two end-to-end examples ship inexamples/server-{openai-responses,anthropic-messages}/. - OpenAPI 3.1 + 3.2 conformance — strict spec-extension parsing (typed
Extensionsnewtype rejects unknown non-x-keys), full Components family (Server/SecurityScheme/Header/Example/Link/Callback/Tag/ExternalDocs/Encoding), JSON Schema 2020-12 keywords (prefixItems,patternProperties,propertyNames,unevaluatedItems/Properties,dependentRequired/Schemas,contains/min/maxContains,contentEncoding/MediaType/Schema,if/then/else,$dynamicRef/$dynamicAnchor,$defs), and 3.2 deltas (queryHTTP method,additionalOperations, OAuthdeviceAuthorization,Server.name,Tag.parent/kind/summary,Discriminator.defaultMapping,mediaTypescomponents, item-level encoding). - Real-world spec compile guarantee — fixed a long tail of generator bugs surfaced by 54 specs in CI (
r#selfpanics, operationId collisions, exclusiveMinimum bool-vs-number, signed enum variants,</>Twilio param sanitization, self-referential union sizing, $ref-typed params, optional request bodies, and more). The full corpus runs locally; CI gates on the gold list. - Codegen quality fixes — header params wired through the signature, HEAD/OPTIONS/PATCH/TRACE methods emitted, $ref-typed parameters resolved to their referenced enum types, path-templating percent-encoded per RFC 3986, range status codes (
2XX/4XX/5XX) matched correctly, auth scheme (Bearer/ApiKey/Custom) honored at runtime, optional request bodies wrapped inOption<T>, operationId collisions detected loudly. - Webhook ingest — operations declared under top-level
webhooks:flow through analysis like any other operation. - SSE auto-detect — responses with
text/event-streamautomatically mark the operation as streaming; the streaming config still wins when present.
See the full release notes at the bottom of this README for the per-area breakdown.
Highlights
- OpenAPI 3.1 first, 3.2 experimental — handles
type: ["X", "null"],anyOf/oneOf/allOf, discriminated unions,const, inline objects, and accepts paths-less specs (components-only or webhooks-only). - Generates clients and servers — pick what you want via
[features]and[server]. Both share the sametypes.rs. - Typed scalars —
format: date-time→chrono::DateTime<chrono::Utc>,uri→url::Url,binary→bytes::Bytes,uuid→uuid::Uuid,byte→Vec<u8>+ base64 codec, unsigned-int formats →u32/u64. All opt-out per-format in TOML. - Async HTTP client — typed methods per operation, retry/backoff via
reqwest-retry, distributed tracing viareqwest-tracing, Bearer / API-key / custom auth (honored at runtime), default headers, path-template percent-encoding. - Axum server scaffolding — trait per tag, status-code-typed response enum, SSE-ready
OkStreamvariant, required-param HTTP 400 short-circuit at the handler boundary, combinedbuild_router(...)factory for multi-tag selections. - SSE streaming clients — first-class Server-Sent Events with reconnection.
- Smart discriminated unions — auto-detects implicit discriminators from
constproperties, falls back to#[serde(untagged)]when a union mixes scalar and object branches (e.g."auto"or a tagged object). - Per-operation typed errors — each operation gets its own error enum with
Status4xx(...)typed bodies; you can match on the exact API error shape. - Typed
additionalProperties— extra keys becomeBTreeMap<String, T>instead of falling toserde_json::Valuewhen the spec gives a value-type schema. - Constraint-as-doc —
minLength/maxLength/minimum/patternetc. are emitted as/// Constraint: …doc comments. No runtime validation is added, so generated code stays free of validator-crate dependencies. - TOML configuration with overrides for spec quirks (nullable, extensible enums, type aliases).
- Snapshot testing —
instasnapshots for generated output. - Optional
specta::Typederives for cross-language type sharing.
Install
[dependencies] openapi-to-rust = "0.5"
Or as a CLI:
cargo install openapi-to-rust
Quick start — client
openapi-to-rust.toml:
[generator] spec_path = "openapi.json" output_dir = "src/generated" module_name = "api" [features] enable_async_client = true [http_client] base_url = "https://api.example.com" timeout_seconds = 30 [http_client.retry] max_retries = 3 [http_client.auth] type = "Bearer" header_name = "Authorization"
Then:
openapi-to-rust generate --config openapi-to-rust.toml
Quick start — Axum server
Pick the operations you want to host. The generator emits a trait, a typed response enum, and a router factory. You implement the trait; axum does the rest.
[generator] spec_path = "openai.yaml" output_dir = "src/gen" module_name = "openai" [features] enable_async_client = false # server-only [server] framework = "axum" operations = ["createResponse", "listInputItems"] # Drop schemas not reachable from the picked operations. Safe when # you're not also generating the HTTP client (the client would lose types). prune_models = true
Discover and scaffold operations from the CLI:
# List operations in the spec (filter by tag / method / substring) openapi-to-rust server list --tag Responses # Add an operation to [server].operations (preserves TOML formatting) openapi-to-rust server add createResponse openapi-to-rust server add --all-tag Responses openapi-to-rust server add createResponse --regenerate # re-run codegen # Remove openapi-to-rust server remove createResponse
Then implement the trait:
use gen::server::{ResponsesApi, CreateResponseResponse, build_router, sse_response}; use gen::CreateResponse; use axum::response::sse::Event; use futures_util::stream; #[derive(Clone)] struct AppState; #[axum::async_trait] impl ResponsesApi for AppState { async fn create_response(&self, body: CreateResponse) -> CreateResponseResponse { if body.stream == Some(true) { // SSE branch — the generated `sse_response` helper takes any // Stream<Item = Result<Event, Infallible>> and returns the // exact payload the OkStream variant expects. CreateResponseResponse::OkStream(sse_response(stream::iter(vec![ Ok(Event::default().event("response.created").data("{}")), Ok(Event::default().event("response.completed").data("{}")), ]))) } else { // Unary branch — typed body, typed response. CreateResponseResponse::Ok(/* construct gen::Response */ todo!()) } } } #[tokio::main] async fn main() { let app = build_router(AppState); let lis = tokio::net::TcpListener::bind("127.0.0.1:3000").await.unwrap(); axum::serve(lis, app).await.unwrap(); }
Two complete examples are in the repo:
examples/server-openai-responses—createResponse+listInputItems+usage-costs(multi-tag, body + SSE, four query params, required param 400)examples/server-anthropic-messages—messages_postwith a small overlay that declarestext/event-streamon the 200 (Anthropic's published spec omits it)
Generated Output
| File | Description |
|---|---|
types.rs |
All struct/enum definitions from OpenAPI schemas |
client.rs |
Async HTTP client with typed methods per operation (when enable_async_client) |
streaming.rs |
SSE streaming client with event parsing (when configured) |
server/mod.rs |
Module re-exports for the server (when [server] is set) |
server/api.rs |
trait <Tag>Api { async fn <op>(&self, …) -> <Op>Response; } per tag |
server/errors.rs |
enum <Op>Response { Ok(T), BadRequest(E), …, OkStream(Sse<…>) } with IntoResponse |
server/router.rs |
Per-tag Router factory; combined build_router<…>(…) for multi-tag selections |
mod.rs |
Module declarations + re-exports |
REQUIRED_DEPS.toml |
Optional crates the generated code references (chrono, uuid, url, bytes, base64) — copy into your consuming crate's Cargo.toml |
Generated client usage
use crate::generated::client::HttpClient; use crate::generated::types::*; #[tokio::main] async fn main() -> Result<(), Box<dyn std::error::Error>> { let client = HttpClient::new() .with_base_url("https://api.example.com") .with_api_key(std::env::var("API_KEY")?); let req = CreateResourceRequest { /* … */ }; let resource = client.create_resource(req).await?; Ok(()) }
What the generated types look like
A tour of patterns the generator emits, from real outputs.
Typed scalars
// format: date-time → chrono::DateTime<chrono::Utc> pub created_at: chrono::DateTime<chrono::Utc>, pub archived_at: Option<chrono::DateTime<chrono::Utc>>, // format: uri → url::Url pub url: url::Url, pub callback_url: Option<url::Url>, // format: binary (multipart) → bytes::Bytes Binary(bytes::Bytes), // format: uuid → uuid::Uuid pub request_id: uuid::Uuid,
Typed additionalProperties
pub additional_properties: std::collections::BTreeMap<String, f64>, // usage maps pub additional_properties: std::collections::BTreeMap<String, String>, // labels
Constraints as doc comments
///Constraint: minLength=1, maxLength=64, pattern=`^[a-zA-Z0-9_-]{1,64}$` pub custom_id: String, ///Constraint: minimum=0, maximum=1 pub temperature: Option<f64>,
No runtime validation is generated. The generator never adds the
validatorcrate or#[validate(...)]attributes — constraints are documentation only. Validate at boundaries you control.
Discriminated unions (tagged enums)
#[derive(Debug, Clone, Deserialize, Serialize)] #[serde(tag = "type", rename_all = "snake_case")] pub enum MessageContent { Text(TextContent), Image(ImageContent), }
Hybrid string-or-object unions
When an anyOf/oneOf mixes a string-enum branch with tagged-object branches (a common OpenAI pattern), the generator emits an untagged enum so both forms deserialize:
#[derive(Debug, Clone, Deserialize, Serialize)] #[serde(untagged)] // not #[serde(tag="type")] pub enum ToolChoiceParam { ToolChoiceOptions(ToolChoiceOptions), // string-enum: "none"|"auto"|"required" ToolChoiceFunction(ToolChoiceFunction), ToolChoiceMCP(ToolChoiceMCP), // … }
Extensible enums (with Custom(String) fallback)
When the spec declares an anyOf of const strings plus an open string branch (or you opt in via [extensible_enums], see below), the enum has a Custom(String) arm so unknown values still deserialize:
pub enum Model { ClaudeSonnet46, ClaudeOpus46, ClaudeHaiku45, Claude3Haiku20240307, Custom(String), // ← anything not in the known set }
Per-operation typed errors
Each operation has its own error enum that wraps the typed body of each documented response code:
let resp = client.create_response(req).await; match resp { Ok(body) => { /* … */ } Err(ApiOpError::Api(err)) => match err.typed { Some(CreateResponseApiError::Status4xx(typed)) => { // typed is the spec's typed 4xx body, e.g. ResponseInfo { // code: 10042, // message: "Please enable R2 through the Cloudflare Dashboard.", // } } _ => eprintln!("raw body: {}", err.body), }, Err(ApiOpError::Transport(e)) => eprintln!("transport: {}", e), }
Streaming (SSE)
use openapi_to_rust::streaming::*; let streaming_config = StreamingConfig { endpoints: vec![StreamingEndpoint { operation_id: "createChatCompletion".to_string(), path: "chat/completions".to_string(), stream_parameter: "stream".to_string(), event_union_type: "ChatCompletionStreamEvent".to_string(), event_flow: EventFlow::StartDeltaStop { start_events: vec!["response.created".to_string()], delta_events: vec!["response.output_text.delta".to_string()], stop_events: vec!["response.completed".to_string()], }, ..Default::default() }], reconnection_config: Some(ReconnectionConfig { max_retries: 5, initial_delay_ms: 500, max_delay_ms: 16000, backoff_multiplier: 2.0, }), ..Default::default() };
Generated event types are tagged enums you can match on directly:
match serde_json::from_str::<ResponseStreamEvent>(&data)? { ResponseStreamEvent::TextDelta(d) => out.push_str(&d.delta), ResponseStreamEvent::Completed(_) => break, _ => {} }
The generator also auto-detects streaming endpoints: any response declaring content: text/event-stream flips supports_streaming = true automatically. Explicit [[streaming.endpoints]] config still wins when present.
Spec-quirk overrides
Real specs lie. These TOML knobs let you patch quirks without forking the spec.
schema_extensions — overlay JSON/YAML fragments onto the spec
A list of files whose top-level objects are deep-merged into the main spec before analysis. Use it to add a text/event-stream content entry, add a missing operation, or tweak a schema without forking the upstream spec. Example: the Anthropic server example overlays sse-overlay.json so messages_post gets an SSE response variant.
[generator] schema_extensions = ["sse-overlay.json"]
nullable_overrides — force a field to Option<T>
When a spec marks a field as required + non-nullable but the API actually returns null. Format: "SchemaName.fieldName" = true.
[nullable_overrides] # OpenAI's spec uses a bare $ref for `error`; the API actually returns null on success. "Response.error" = true
extensible_enums — force a closed enum to accept unknown values
When the spec declares a fixed enum but the API actually returns values outside the set (real-world drift). Renders the enum with a Custom(String) fallback variant. Accepts either the raw spec name or the rendered Rust type name.
[extensible_enums] # CF spec declares lowercase ["apac", ..., "wnam"] but the API returns "WNAM". "R2BucketLocation" = true # OpenAI spec declares ["in-memory", "24h"] but the API returns "in_memory". "ModelResponsePropertiesPromptCacheRetention" = true
type_mappings — override a primitive's Rust type
[type_mappings] "DateTime" = "chrono::DateTime<chrono::Utc>"
[generator.types] — typed-scalar strategy per format
Opt out of any individual typed scalar (e.g. fall back to String for date-times if you don't want chrono):
[generator.types.strategies] "date-time" = "string" # default: "chrono" "uri" = "string" # default: "url" "binary" = "string" # default: "bytes"
The CLI also supports --types-conservative, which collapses every typed scalar to String/i64/etc. Use it when you want zero optional-crate dependencies.
OpenAPI 3.1 / 3.2 support
The generator accepts 3.0.x and 3.1.x specs; 3.2.x parses with an experimental warning. Unknown OpenAPI extensions (anything not prefixed with x-) surface as a hard error at parse time — silent drops are not a thing here.
3.1 (JSON Schema 2020-12) keywords now modeled and read from typed fields:
| Keyword group | Status |
|---|---|
type as array (e.g. ["string", "null"]) |
typed + used for nullability |
prefixItems, unevaluatedItems, contains / minContains / maxContains |
typed |
patternProperties, propertyNames, unevaluatedProperties |
typed |
dependentRequired, dependentSchemas, if / then / else |
typed |
contentEncoding, contentMediaType, contentSchema |
typed |
$dynamicRef, $dynamicAnchor, $defs, $id, $schema, $comment |
typed (anchor-scope resolution is a follow-up) |
Path Item $ref resolution |
resolved at analysis time |
Webhooks (webhooks:) |
ingested as operations |
examples, example, title, deprecated, readOnly/writeOnly |
typed |
3.2 deltas (experimental):
| Delta | Status |
|---|---|
query HTTP method + PathItem.additionalOperations |
parses + emits client methods via reqwest::Method::from_bytes(...) |
OAuth deviceAuthorization flow + oauth2MetadataUrl |
typed |
Server.name, Tag.parent/kind/summary |
typed |
Discriminator.defaultMapping |
typed (captured; _Other(Value) fallback emission is a follow-up) |
MediaType.itemSchema, prefixEncoding, itemEncoding |
typed |
mediaTypes in Components |
typed |
Components family (typed end-to-end): Server, ServerVariable, SecurityScheme (apiKey / http / mutualTLS / oauth2 / openIdConnect with all flows), OAuthFlows, Encoding, Header, Example, Link, Callback, Tag, ExternalDocs, Discriminator. Everything is Extensions-strict: unknown non-x-* fields fail loudly at deserialize time.
A full conformance harness lives under tests/conformance/ with fixture files and a status.toml recording what's L0-typed vs. L3-flowing-through-codegen. The JSON-Schema-Test-Suite (git submodule) runs as a separate test.
CLI
openapi-to-rust generate --config openapi-to-rust.toml openapi-to-rust generate --config openapi-to-rust.toml --types-conservative openapi-to-rust validate --config openapi-to-rust.toml # Server scope management — preserves TOML formatting (toml_edit) openapi-to-rust server list # all operations openapi-to-rust server list --tag Responses # filter by tag openapi-to-rust server list --method POST --grep response openapi-to-rust server list --json # JSON output openapi-to-rust server add createResponse # add one openapi-to-rust server add --all-tag Responses # expand a tag openapi-to-rust server add createResponse --dry-run # preview openapi-to-rust server add createResponse --regenerate # add + regenerate openapi-to-rust server remove createResponse # remove
Selectors are forgiving — operationId, METHOD /path, or tag:<name>. Typos surface Levenshtein-based "Did you mean …?" suggestions.
TOML reference
[generator] spec_path = "openapi.json" # required output_dir = "src/generated" # required module_name = "types" # informational label, not a directory schema_extensions = [] # optional list of JSON/YAML overlays merged into the spec [features] enable_sse_client = false # generate SSE streaming client (requires [[streaming.endpoints]]) enable_async_client = true # generate HTTP REST client enable_specta = false # add specta::Type derives enable_registry = false # generate static operation registry (CLI/proxy routing) registry_only = false # only generate the registry (skip types/client/streaming) [http_client] base_url = "https://api.example.com" timeout_seconds = 30 # 1-3600 [http_client.retry] max_retries = 3 # 0-10 initial_delay_ms = 500 # 100-10000 max_delay_ms = 16000 # 1000-300000 [http_client.tracing] enabled = true [http_client.auth] type = "Bearer" # Bearer | ApiKey | Custom (honored at runtime) header_name = "Authorization" [[http_client.headers]] name = "content-type" value = "application/json" [[streaming.endpoints]] operation_id = "createChatCompletion" path = "chat/completions" http_method = "POST" stream_parameter = "stream" event_union_type = "ChatCompletionStreamEvent" content_type = "text/event-stream" [streaming.endpoints.event_flow] type = "StartDeltaStop" # or "Continuous" start_events = ["response.created"] delta_events = ["response.output_text.delta"] stop_events = ["response.completed"] [server] framework = "axum" # only axum supported today operations = [ # selectors: operationId | "METHOD /path" | "tag:<name>" "createResponse", "POST /v1/messages", "tag:Responses", ] prune_models = false # drop schemas unreachable from picked ops # (safe only when not also generating the HTTP client) [nullable_overrides] "Response.error" = true # see "Spec-quirk overrides" above [extensible_enums] "R2BucketLocation" = true # see "Spec-quirk overrides" above [type_mappings] "DateTime" = "chrono::DateTime<chrono::Utc>" [generator.types.strategies] "date-time" = "chrono" # chrono (default) | string "uri" = "url" # url (default) | string "binary" = "bytes" # bytes (default) | string "uuid" = "uuid" # uuid (default) | string "byte" = "vec_u8_base64" # default; encodes/decodes via base64
Testing
cargo test # unit + integration tests cargo insta test # snapshot tests cargo insta review # review snapshot diffs scripts/spec-compile.sh # generate + cargo-check every spec in specs/ (full corpus)
CI runs the gold list (Anthropic, OpenAI, and a curated set covering the worst-behaved real-world specs) as a regression guard. Local scripts/spec-compile.sh runs the full 54-spec corpus.
Examples
# Library / generator examples cargo run --example basic_generation cargo run --example client_generation_example cargo run --example discriminated_unions cargo run --example anyof_unions cargo run --example allof_composition cargo run --example openai_patterns cargo run --example toml_config_example # End-to-end Axum server examples (generate + run) cargo run -p openapi-to-rust -- generate --config examples/server-openai-responses/openapi-to-rust.toml cargo run --manifest-path examples/server-openai-responses/Cargo.toml cargo run -p openapi-to-rust -- generate --config examples/server-anthropic-messages/openapi-to-rust.toml cargo run --manifest-path examples/server-anthropic-messages/Cargo.toml
Release notes
0.5 (this release)
Server codegen (Axum)
[server]TOML section with selector grammar (operationId /METHOD /path/tag:<name>).openapi-to-rust server list / add / removewith--all-tag,--dry-run,--regenerate,--json. Edits preserve TOML formatting viatoml_edit. Typos get Levenshtein "Did you mean …?" suggestions.- Emits
server/{mod,api,errors,router}.rs: trait per tag, status-code-typed response enum withIntoResponse, conditionalOkStream(Sse<…>)variant, per-tag and combinedbuild_router<…>(…)factory. - Query + header parameters wired through the trait. Required query/header params arrive unwrapped; missing-required short-circuits to HTTP 400 +
{"error": "missing required …"}JSON. - Generated
sse_response(stream)helper — wraps anyStream<Item = Result<Event, Infallible>>so the SSE branch stays two lines. - End-of-generate hint prints a paste-ready impl skeleton.
[server].prune_models = true(opt-in) trimstypes.rsto schemas reachable from picked ops (OpenAI example: drops 280 of 2154 schemas, ~3300 fewer lines).- Two end-to-end examples:
server-openai-responses(multi-tag, body + SSE, four query params, required-param 400) andserver-anthropic-messages(SSE overlay).
OpenAPI 3.1 + 3.2 conformance (38 of 51 beads from #14)
- Strict spec extensions (
Extensionsnewtype rejects unknown non-x-keys) + version gate (3.0 / 3.1 accepted, 3.2 experimental, others hard error). - Full Components family typed end-to-end:
Server,ServerVariable,SecurityScheme(apiKey/http/mutualTLS/oauth2/openIdConnect),OAuthFlows,Encoding,Header,Example,Link,Callback,Tag,ExternalDocs,Discriminator. - JSON Schema 2020-12 keywords typed:
prefixItems,patternProperties,propertyNames,unevaluatedItems/Properties,dependentRequired/Schemas,contains/min/maxContains,contentEncoding/MediaType/Schema,if/then/else,$dynamicRef/$dynamicAnchor,$defs,$id,$schema,$comment. - 3.2 deltas:
queryHTTP method,additionalOperations(arbitrary verbs), OAuthdeviceAuthorization,Server.name,Tag.parent/kind/summary,Discriminator.defaultMapping,MediaType.itemSchema/prefixEncoding/itemEncoding,mediaTypescomponents. typeas array (3.1 canonical nullability —type: ["string", "null"]) merges cleanly with the 3.0nullable: truefield.- Webhooks (
webhooks:) ingested as operations. - Path Item
$refresolution ($ref: "#/components/pathItems/X"). - Conformance harness with fixtures, layered
fails_at:markers,status.toml, and the JSON-Schema-Test-Suite as a git submodule.
Real-world spec compile guarantee (54 specs in CI)
r#self/r#super/r#crate/r#Selfpanic → rename to<keyword>_field/<keyword>_param(proc_macro2 doesn't accept those as raw idents).- OperationId collisions → auto-disambiguate by HTTP method (
opId_post) with a stderr warning, instead of rejecting the whole document. exclusiveMinimummodeled asbool | f64(3.0/Swagger used bool; 3.1 uses number).- Signed enum variants disambiguated (
Variant1vsVariantNeg1). - Twilio-style
</>/<=/>=filter params sanitized to_lt/_gt/_lte/_gte. - Self-referential union variants boxed to break infinite-size enums.
- Nullable-anyOf wrapper collisions resolved (don't synthesize a wrapper that overwrites the inner
$ref's schema). $refshape variants accepted (#/definitions/XSwagger carry-over,#/components/parameters/X/schemafalls back toValue).- Per-method parameter ident collisions resolved at analysis time (
exclude_ids+exclude-idsget uniquerust_idents). - Empty/non-string enum values coerced via Display (gitpod's numeric values on a string-typed schema).
- Optional request bodies (
required: false) wrapped inOption<T>and chained withif let Some(...)so the request builder typechecks. $ref-typed parameter types resolved to the referenced enum (instead of silently falling back toString).- String enums emit
as_str(),Display, andAsRef<str>impls so the wire form drops cleanly into headers, query strings, and path segments. cargo clippy --all-features -- -D warningsis clean.
Codegen quality fixes
- T1: header request parameters wired through codegen.
- T2: HEAD/OPTIONS/PATCH/TRACE methods emitted explicitly (was a silent
_ => getfallback). - T3:
auth_configApiKey/Customvariants honored at runtime — README's multi-scheme-auth claim is now actually true. - T4: webhooks walked in analysis.
- T5: path templating percent-encoded per RFC 3986 §3.3 via an emitted
__pct_encode_path_segmenthelper. - T6: operationId collisions detected loudly at analysis time.
- T8: range status codes (
1XX/2XX/3XX/4XX/5XX) get guarded match arms instead of falling through to the generic default. - T10:
$ref-typed parameter types resolved. - T11: optional request bodies →
Option<T>. - T13: spec
description/summarysurfaced as rustdoc on each operation method. - T15: SSE auto-detection from
text/event-streamresponses. - F1: strict spec extensions + version gate.
- F2:
typeas array (3.1 nullability). - F4: paths-less specs (components-only / webhooks-only) accepted.
Typed scalars (Q2 series)
TypeMapperchokepoint for format-driven type mapping.format: date-time→chrono::DateTime<Utc>,uri→url::Url,binary→bytes::Bytes,uuid→uuid::Uuid,byte→Vec<u8>+ base64 codec.- Unsigned-int formats (
uint32,uint64, etc.) →u32/u64. - Typed
additionalProperties→BTreeMap<String, T>. x-enum-varnameshonored.- Constraint doc comments (
minLength/maxLength/minimum/pattern). - Per-format strategy opt-out via
[generator.types.strategies];--types-conservativeflag. REQUIRED_DEPS.tomlemitted listing optional crates the generated code references (+ stderr advisory).- Clean primitive variants for
anyOfunions.
Other
- Hybrid string-or-object unions,
allOf-nullable handling,[extensible_enums]override.
Contributing
- Fork the repo
- Add your OpenAPI spec or pattern to
specs/ortests/fixtures/ - Write a snapshot test (
insta) - Run
cargo insta testand review output - Open a PR
License
MIT