-
Notifications
You must be signed in to change notification settings - Fork 19
Description
Hawaii doesn't natively generate F# discriminated unions from OpenAPI discriminator
schemas (oneOf
with mapping
). These are semantically discriminated unions but Hawaii generates them as separate types without a union type.
OpenAPI Source
# From Cloudflare Workers OpenAPI spec
workersbindingitem:
oneOf:
- $ref: "#/components/schemas/workersbindingkindassets"
- $ref: "#/components/schemas/workersbindingkindbrowser"
- $ref: "#/components/schemas/workersbindingkindd1"
- $ref: "#/components/schemas/workersbindingkindr2bucket"
# ... 27 total binding types
discriminator:
propertyName: type
mapping:
assets: "#/components/schemas/workersbindingkindassets"
browser: "#/components/schemas/workersbindingkindbrowser"
# ... etc
Current Hawaii Output
Hawaii generates individual types but no discriminated union:
type workersbindingkindassets = { ... }
type workersbindingkindbrowser = { ... }
type workersbindingkindd1 = { ... }
// ... 27 more types
// But no:
// type workersbindingitem =
// | Assets of workersbindingkindassets
// | Browser of workersbindingkindbrowser
// | D1 of workersbindingkindd1
CloudflareFS Workaround
We created a post-processing script that analyzes the generated code and automatically creates discriminated unions:
File: generators/hawaii/post-process-discriminators.fsx
// Auto-detects binding kind types and generates DU
let getBindingKindTypes (typesContent: string) =
let pattern = @"type (workersbindingkind[a-z0-9]+)\s*="
Regex.Matches(typesContent, pattern)
|> Seq.cast<Match>
|> Seq.map (fun m -> m.Groups.[1].Value)
|> Seq.toList
let generateDiscriminatedUnion (typeName: string) (cases: (string * string) list) =
let typeDecl = sprintf "type %s =" typeName
let unionCases =
cases
|> List.map (fun (caseName, schemaType) ->
sprintf " | %s of %s" caseName schemaType)
|> String.concat "\n"
sprintf "%s\n%s\n" typeDecl unionCases
Generated Output:
// Discriminated union (auto-generated by post-processor)
type workersbindingitem =
| Assets of workersbindingkindassets
| Browser of workersbindingkindbrowser
| D1 of workersbindingkindd1
| R2bucket of workersbindingkindr2bucket
// ... 27 cases total
Integration: generators/hawaii/generate-workers.ps1
# Step 1: Run Hawaii
dotnet run --project D:\repos\Hawaii\src\Hawaii.fsproj -- --config workers-hawaii.json
# Step 2: Post-process discriminated unions
dotnet fsi post-process-discriminators.fsx Generated-Workers\Types.fs
Proposed Enhancement
Hawaii should detect OpenAPI discriminator
schemas and automatically generate F# discriminated unions:
- Detect
oneOf
withdiscriminator
in OpenAPI schema - Generate individual case types (current behavior)
- Additionally generate a discriminated union type
- Use the
discriminator.propertyName
for JSON serialization - Handle
discriminator.mapping
for case name mapping
Benefits
- Type safety: Single union type instead of loose collection of types
- Pattern matching: F# exhaustiveness checking works
- Semantic correctness: OpenAPI discriminators ARE discriminated unions
- No post-processing: Works out of the box
Reference Implementation
CloudflareFS has a working post-processor that demonstrates the pattern. We're happy to contribute this logic to Hawaii if it would be helpful.