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.
- 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.
- 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.
[dependencies]
openapi-to-rust = "0.5"Or as a CLI:
cargo install openapi-to-rustopenapi-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.tomlPick 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 = trueDiscover 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 createResponseThen 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)
| 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 |
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(())
}A tour of patterns the generator emits, from real outputs.
// 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,pub additional_properties: std::collections::BTreeMap<String, f64>, // usage maps
pub additional_properties: std::collections::BTreeMap<String, String>, // labels///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.
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum MessageContent {
Text(TextContent),
Image(ImageContent),
}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),
// …
}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
}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),
}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.
Real specs lie. These TOML knobs let you patch quirks without forking 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"]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" = trueWhen 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]
"DateTime" = "chrono::DateTime<chrono::Utc>"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.
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.
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 # removeSelectors are forgiving — operationId, METHOD /path, or tag:<name>. Typos surface Levenshtein-based "Did you mean …?" suggestions.
[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 base64cargo 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.
# 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.tomlServer 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.
- 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
MIT