handlers

package
v0.35.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 21, 2026 License: Apache-2.0 Imports: 11 Imported by: 0

README

handlers — maintainer notes

This document is the long-form companion to the handlers package code.

The source files keep godoc concise; complex invariants, design trade-offs, and intentionally-deferred follow-ups live here.

The handlers package ships shared grammar Walker callbacks for the SimpleSchema family of OAS v2 dispatchers (parameter level-0 and items chain, response-header level-0 and items chain) as well as the full-Schema dispatchers used by the schema builder.


Table of contents


§dispatch-surface — SimpleSchema vs full-Schema dispatch

The package exports two dispatcher families:

  • SimpleSchemaDispatchParamLevel0, DispatchHeaderLevel0, DispatchItemsLevel. These fan a single switch-on-Keyword.Name out across consumers that write through an ifaces.ValidationBuilder or ifaces.OperationValidationBuilder adapter (paramValidations, headerValidations, items.Validations).
  • full-SchemaDispatchSchemaLevel0, DispatchSchemaItemsLevel. These add a checkShape gate that emits CodeShapeMismatch on keyword-vs-resolved-type mismatches, and the Bool handler does cross-target writes (required:enclosing.Required keyed by name, discriminator: likewise).

The shape-gate and cross-target writes are full-Schema-specific concerns and don't share the SimpleSchema seam. The SchemaOptions struct carries a SimpleSchemaMode flag that the full-Schema dispatchers use to gate full-Schema-only keywords (readOnly, discriminator) with CodeUnsupportedInSimpleSchema and to silently skip required: (parameters handle required: at the parameter level via SimpleSchema dispatch; headers don't carry required: at all).

§walker-payloads — Walker callback payload conventions

  • Number / Integer / Bool callbacks fire with a zero-value payload when the lexer rejected the source value; the parser has already emitted a CodeInvalid{Number,Integer,Boolean} diagnostic. Consumers must gate on pr.IsTyped() before writing — the helpers in this package do that internally.
  • String fires with the raw value alongside pr.Value; for pattern: consumers should read pr.Value (the regex source) rather than the formatted string, so the regex reaches SetPattern verbatim.
  • Raw fires for ShapeRawValue keywords (default:, example:, enum:) and reads pr.Value for the raw text.

§raw-errsink — errSink contract on Raw

Raw accepts an errSink func(error) bool argument that controls how coercion errors from default: / example: propagate:

  • errSink == nil → swallow silently. The response-header path uses this posture: a malformed default/example on a header does not fail the build. Both DispatchHeaderLevel0 and DispatchItemsLevel wire errSink=nil.
  • errSink != nil → invoked with the first ParseValueFromSchema error. Returning true short-circuits subsequent Raw callbacks within the same Walker invocation (the closure's stopped flag); returning false continues.

DispatchParamLevel0 wires a sink that captures the first error and returns true, so DispatchParamLevel0 bubbles a malformed parameter default: / example: up to the caller as a hard failure. See TestMalformed_DefaultInt / TestMalformed_ExampleInt in the integration suite for the end-to-end behaviour.

§collection-format-fallback — collectionFormat: accepts arbitrary strings

CollectionFormatString tries the Walker-supplied typed string first. When that is empty (the grammar's closed-vocab string-enum rejected the source), it falls back to strings.TrimSpace(pr.Value) and writes the raw value through.

The OAS v2 spec defines a closed vocabulary (csv/ssv/tsv/pipes/multi), but the codescan grammar is intentionally permissive at this site: a typo such as pipe instead of pipes round-trips verbatim onto the parameter or items object. This preserves the source author's intent for downstream tools that may surface validation errors against the spec text directly.

SimpleSchema-only — the full-Schema Validations adapter does not expose SetCollectionFormat because collectionFormat: is not a full-Schema keyword.

§simple-schema-keywords — allow-list and required: carve-out

simpleSchemaAllowed in keywords.go enumerates the grammar keyword names legal on an OAS v2 SimpleSchema site (parameter with in != body, response header, and the items chain within either). Source of truth: the OAS v2 Parameter Object and Header Object allowed-keyword tables.

Vendor extensions (x-*) are not listed in the table — they are gated by classify.IsAllowedExtension, which runs by name-prefix.

required: is included in the SimpleSchema allow-list because it is valid on the parameter site (as a parameter-level boolean), but it is NOT valid on response headers. Two consequences:

  • The parameters walker writes required: to param.Required directly via paramRequiredBool — the value lands on the parameter object, not on a schema.
  • The full-Schema walker (schemaBoolHandler) silently skips required: under SimpleSchemaMode because its full-Schema target is enclosing.Required[name] — the object-level required-array — which doesn't fit the SimpleSchema shape.

IsSimpleSchemaKeyword returns false for full-Schema-only keywords (readOnly, discriminator, $ref, allOf, ...) and for unknown names. Consumers wired in SimpleSchema mode use this predicate to gate writes and emit CodeUnsupportedInSimpleSchema diagnostics on miss.

§extensions — vendor extensions land via AddExtension

ExtensionTarget is the minimal surface a Walker.Extension consumer needs to write a vendor extension. It is implemented by every oaispec object that embeds VendorExtensible (Schema, Parameter, Header, Response, Operation, ...) via the AddExtension method promoted from the embed.

Extension returns a callback that filters non-x-* names via classify.IsAllowedExtension and writes the typed extension value onto the target.

User-authored extensions are not gated by the SkipExtensions option — that flag suppresses scanner-derived x-go-* keys, not author intent. Consumers that need additional side effects on a successful write (e.g. the schema builder's refOverrideCollector marking the collector) wrap this with their own callback rather than reusing this helper.

§stale-enum-desc — field-level enum: strips inherited x-go-enum-desc

SchemaValidations.SetEnum writes the parsed enum onto the schema, then calls clearStaleEnumDesc to strip the x-go-enum-desc extension (and the matching suffix from Description) when present.

The extension is set by the type-level swagger:enum TypeName pass and carries per-value documentation text. When a field-level enum: annotation overrides the inherited values, the per-value text describes values that are no longer in the field-level enum — it is stale and must be dropped to avoid misleading documentation.

§quirks-open — deferred follow-ups

  • collectionFormat: lax acceptance. The fallback to the raw string preserves typos by design. A future strict-mode option could emit a diagnostic when the value is outside the OAS v2 closed vocabulary, leaving the lax default in place for compatibility.
  • SchemaOptions.SimpleSchemaMode on items dispatch. The option is accepted on DispatchSchemaItemsLevel for symmetry but currently does not alter items-level behaviour. Worth revisiting if items dispatch ever needs the same gating as level-0 dispatch.

Documentation

Overview

Package handlers ships shared grammar Walker callbacks for the SimpleSchema and full-Schema families of OAS v2 dispatchers.

Details

See [§dispatch-surface](./README.md#dispatch-surface) for the split between SimpleSchema and full-Schema dispatch and [§walker-payloads](./README.md#walker-payloads) for the payload conventions on each Walker callback.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ApplyPattern

func ApplyPattern(p grammar.Property, valid SchemaValidations, val string, diag func(grammar.Diagnostic))

ApplyPattern stores a regex pattern on valid and runs a best-effort RE2 hygiene check, emitting CodeInvalidAnnotation when the regex does not compile in Go's RE2 engine. The pattern is kept on the schema regardless — downstream tools may use JSON Schema's wider regex dialect.

Exported because the schema package's refOverrideCollector applies the same pattern semantics when building $ref-override allOf compounds.

diag may be nil; when nil, the RE2 mismatch warning is dropped.

func ApplyPatternProperties added in v0.35.0

func ApplyPatternProperties(p grammar.Property, valid SchemaValidations, val string, diag func(grammar.Diagnostic))

ApplyPatternProperties adds a regex → empty-schema entry to the schema's patternProperties map and runs the same best-effort RE2 hygiene check as ApplyPattern: a regex that does not compile in Go's RE2 engine emits CodeInvalidAnnotation but is preserved on the schema (downstream tools may use JSON Schema's wider regex dialect).

Exported for symmetry with ApplyPattern; the schema package's refOverrideCollector applies the same semantics on $ref overrides.

diag may be nil; when nil, the RE2 mismatch warning is dropped.

func CollectionFormatString

func CollectionFormatString(v ifaces.OperationValidationBuilder) func(grammar.Property, string)

CollectionFormatString returns a Walker.String callback for the `collectionFormat:` keyword. Tries the Walker-supplied typed string first and falls back to strings.TrimSpace(pr.Value) when the grammar's closed-vocab string-enum rejected the source, so values outside the OAS v2 vocabulary round-trip verbatim.

SimpleSchema-only — schema-level Validations don't expose SetCollectionFormat.

Details

See [§collection-format-fallback](./README.md#collection-format-fallback) for the rationale behind the lax fallback.

func ComposeBool

func ComposeBool(hs ...func(grammar.Property, bool)) func(grammar.Property, bool)

ComposeBool returns a Walker.Bool callback that fans the payload out to every non-nil handler in order. Useful when a consumer wants UniqueBool plus a context-specific extra (e.g. parameters' `required:` writes to param.Required directly).

func ComposeString

func ComposeString(hs ...func(grammar.Property, string)) func(grammar.Property, string)

ComposeString returns a Walker.String callback that fans the payload out to every non-nil handler in order. The canonical use is to combine PatternString + CollectionFormatString in one Walker.String slot.

func DispatchHeaderLevel0

func DispatchHeaderLevel0(block grammar.Block, header *oaispec.Header, diag func(grammar.Diagnostic))

DispatchHeaderLevel0 routes every level-0 Property in block onto header via the grammar Walker. Mirrors DispatchParamLevel0 minus `required:` (headers don't carry it) and wires errSink=nil so malformed default/example values on a header don't fail the build — see [§raw-errsink](./README.md#raw-errsink).

diag may be nil; when nil, parser diagnostics are dropped.

func DispatchItemsLevel

func DispatchItemsLevel(block grammar.Block, target *oaispec.Items, depth int, diag func(grammar.Diagnostic))

DispatchItemsLevel dispatches Property entries at the given items depth onto target via the resolvers.ItemsValidations adapter. The shape is identical between parameter and response-header items chains — both write to *oaispec.Items via the items adapter — so a single function serves both consumers.

func DispatchParamLevel0

func DispatchParamLevel0(block grammar.Block, param *oaispec.Parameter, diag func(grammar.Diagnostic)) error

DispatchParamLevel0 routes every level-0 Property in block onto param via the grammar Walker. Handler wiring is the SimpleSchema surface: Number/Integer/UniqueBool+RequiredBool/Pattern+CollectionFmt/ Raw-with-errSink/Extension.

firstErr captures the first parse error from default/example coercion; callers surface it as a build error. diag may be nil (parser diagnostics dropped when so).

func DispatchSchemaItemsLevel

func DispatchSchemaItemsLevel(block grammar.Block, target *oaispec.Schema, depth int,
	diag func(grammar.Diagnostic), opts SchemaOptions,
)

DispatchSchemaItemsLevel dispatches items-depth Property entries onto target. Items elements don't carry vendor extensions and don't participate in cross-target writes (required/discriminator), so the dispatcher is narrower than the level-0 entry — only number/ integer/bool(unique)/string/raw handlers fire.

opts.SimpleSchemaMode is threaded to the raw handler so the full-Schema-only externalDocs keyword is gated consistently; the other items-level handlers are unaffected by it.

diag may be nil; when nil, all diagnostics are dropped.

func DispatchSchemaLevel0

func DispatchSchemaLevel0(block grammar.Block, enclosing, ps *oaispec.Schema, name string,
	diag func(grammar.Diagnostic), opts SchemaOptions,
)

DispatchSchemaLevel0 routes every level-0 Property in block into ps. The enclosing schema receives required/discriminator writes keyed by name (name == "" silently skips those cross-target writes, for the top-level model case where there is no enclosing).

opts.SimpleSchemaMode gates full-Schema-only keywords with CodeUnsupportedInSimpleSchema (for the parameters/headers paths that drive the schema builder under SimpleSchema constraints).

diag may be nil; when nil, all diagnostics are dropped.

func Extension

func Extension(target ExtensionTarget) func(grammar.Extension)

Extension returns a Walker.Extension callback that filters non-`x-*` names via classify.IsAllowedExtension and writes the typed extension value onto target.

Details

See [§extensions](./README.md#extensions) for the SkipExtensions interaction and the wrap-for-side-effects pattern.

func Integer

func Integer(v ifaces.ValidationBuilder, diag func(grammar.Diagnostic)) func(grammar.Property, int64)

Integer returns a Walker.Integer callback for the SimpleSchema surface. It routes `min/maxLength:` and `min/maxItems:` onto v and, when diag is non-nil, emits CodeUnsupportedInSimpleSchema for any other integer keyword — the full-Schema-only object keywords `minProperties:` / `maxProperties:`, which have no SimpleSchema form.

diag may be nil; when nil, the unsupported-keyword warning is dropped.

func IsSimpleSchemaKeyword

func IsSimpleSchemaKeyword(keyword string) bool

IsSimpleSchemaKeyword reports whether keyword is legal on an OAS v2 SimpleSchema site. Returns false for full-Schema-only keywords (`readOnly`, `discriminator`, `$ref`, `allOf`, ...) and for unknown names.

Consumers wired in SimpleSchema mode (the schema builder under WithSimpleSchema, the parameters dispatcher, the responses dispatcher) use this predicate to gate writes and emit CodeUnsupportedInSimpleSchema diagnostics on miss.

func Number

Number returns a Walker.Number callback that routes `maximum:` / `minimum:` / `multipleOf:` onto v.

func ParseExternalDocs added in v0.35.0

func ParseExternalDocs(body string) (*oaispec.ExternalDocumentation, error)

ParseExternalDocs unmarshals an `externalDocs:` block body (a YAML map with description/url keys) into a *spec.ExternalDocumentation. Returns (nil, nil) for an empty/blank block so callers don't emit a useless `externalDocs: {}` (the OAS object requires url). Shared by the schema raw handler and the routes builder.

func PatternString

func PatternString(v ifaces.ValidationBuilder) func(grammar.Property, string)

PatternString returns a Walker.String callback for the `pattern:` keyword. The pattern is read from `pr.Value` so the regex source reaches v.SetPattern verbatim.

func Raw

func Raw(v ifaces.ValidationBuilder, scheme *oaispec.SimpleSchema, errSink func(error) bool,
	diag func(grammar.Diagnostic),
) func(grammar.Property)

Raw returns a Walker.Raw callback for `default:` / `example:` / `enum:` (Shape=ShapeRawValue per the lexer table). `default` and `example` coerce against scheme via validations.CoerceValue; `enum` is delegated to v.SetEnum which routes through validations.CoerceEnum inside the adapter.

errSink controls coercion-error semantics:

  • errSink == nil → swallow silently. The response-header path uses this posture so that a malformed default/example on a header doesn't fail the build.
  • errSink != nil → invoked with the first ParseValueFromSchema error. Returning true short-circuits subsequent Raw callbacks within this Walker (the closure's `stopped` flag); returning false continues. Parameters use this to bubble the error up so the build surfaces a malformed default/example as a hard failure.

diag receives a CodeUnsupportedInSimpleSchema warning when a full-Schema-only raw keyword (e.g. externalDocs:) appears on this SimpleSchema site; the keyword is dropped. diag may be nil.

Details

See [§raw-errsink](./README.md#raw-errsink) for the per-dispatcher wiring and the integration tests that exercise the parameter-path hard-failure behaviour.

func RecheckSchemaShape added in v0.35.0

func RecheckSchemaShape(sch *oaispec.Schema, pos token.Position, diag func(grammar.Diagnostic))

RecheckSchemaShape re-validates the shape-constrained validations already written onto sch against sch's now-resolved type, stripping any that are illegal for that type and emitting CodeShapeMismatch for each (at pos).

This exists for the top-level model decl path only: there the doc-comment block is dispatched (via DispatchSchemaLevel0) before the Go type has been built onto the schema, so the inline checkShape sees an empty type and cannot gate. Field- and items-level dispatch already run after their target's type is set, so they don't need it.

uniqueItems is intentionally not rechecked: its grammar keyword (`unique`) carries no type-domain rule, matching the field/items paths which likewise never shape-gate it. diag may be nil.

func SchemaTypeOf

func SchemaTypeOf(ps *oaispec.Schema) string

SchemaTypeOf returns the resolved Swagger type of ps, or "" when the schema has no Type set (typically a $ref-only schema, where type-aware coercion is not possible at this level).

func SetDiscriminator

func SetDiscriminator(enclosing *oaispec.Schema, name string, required bool)

SetDiscriminator writes name to enclosing.Discriminator when required=true, or clears it when required=false and the current value matches.

func SetRequired

func SetRequired(enclosing *oaispec.Schema, name string, required bool)

SetRequired adds or removes name from the enclosing schema's Required slice.

Exported because the schema package's refOverrideCollector reuses this when handling `required:` on a $ref-override field.

func UniqueBool

func UniqueBool(v ifaces.ValidationBuilder) func(grammar.Property, bool)

UniqueBool returns a Walker.Bool callback that handles only the `unique:` keyword. Consumers that also need to dispatch `required:` (parameter level) wrap this with a second callback via ComposeBool, or write their own narrow handler that adds the parameter-target write next to a call into UniqueBool.

func UnsupportedSimpleSchemaString added in v0.35.0

func UnsupportedSimpleSchemaString(diag func(grammar.Diagnostic)) func(grammar.Property, string)

UnsupportedSimpleSchemaString returns a Walker.String callback that emits CodeUnsupportedInSimpleSchema for full-Schema-only string keywords (`patternProperties:`) that reach a SimpleSchema site. The SimpleSchema-legal string keywords (`pattern:`, `collectionFormat:`) are handled by their own callbacks and ignored here.

Composed alongside PatternString / CollectionFormatString in the parameter / header / items dispatchers. diag may be nil.

Types

type ExtensionTarget

type ExtensionTarget interface {
	AddExtension(key string, value any)
}

ExtensionTarget is the minimal surface a Walker.Extension consumer needs to write a vendor extension. Implemented by every oaispec object that embeds VendorExtensible (Schema, Parameter, Header, Response, Operation, …) via the AddExtension method promoted from the embed.

type SchemaOptions

type SchemaOptions struct {
	SimpleSchemaMode bool
}

SchemaOptions configures the full-Schema dispatch.

SimpleSchemaMode enables the SimpleSchema gating used by the parameter/header drivers that pass through the schema builder: full-Schema-only keywords (readOnly, discriminator, ...) emit CodeUnsupportedInSimpleSchema and skip the write; `required:` is silently skipped.

See [§simple-schema-keywords](./README.md#simple-schema-keywords).

type SchemaValidations

type SchemaValidations struct {
	// contains filtered or unexported fields
}

SchemaValidations adapts *oaispec.Schema to ifaces.ValidationBuilder so the full-Schema handler family can write level-0 and items-level validations through a uniform target.

Exported because the schema package's refOverrideCollector also uses it directly when building $ref-override allOf compounds.

func NewSchemaValidations

func NewSchemaValidations(ps *oaispec.Schema) SchemaValidations

NewSchemaValidations builds an adapter around ps.

func (SchemaValidations) SetDefault

func (sv SchemaValidations) SetDefault(val any)

func (SchemaValidations) SetEnum

func (sv SchemaValidations) SetEnum(val string)

SetEnum writes the parsed enum onto the schema and strips any inherited x-go-enum-desc that the field-level override has made stale — see [§stale-enum-desc](./README.md#stale-enum-desc).

func (SchemaValidations) SetExample

func (sv SchemaValidations) SetExample(val any)

func (SchemaValidations) SetMaxItems

func (sv SchemaValidations) SetMaxItems(val int64)

func (SchemaValidations) SetMaxLength

func (sv SchemaValidations) SetMaxLength(val int64)

func (SchemaValidations) SetMaxProperties added in v0.35.0

func (sv SchemaValidations) SetMaxProperties(val int64)

func (SchemaValidations) SetMaximum

func (sv SchemaValidations) SetMaximum(val float64, exclusive bool)

func (SchemaValidations) SetMinItems

func (sv SchemaValidations) SetMinItems(val int64)

func (SchemaValidations) SetMinLength

func (sv SchemaValidations) SetMinLength(val int64)

func (SchemaValidations) SetMinProperties added in v0.35.0

func (sv SchemaValidations) SetMinProperties(val int64)

func (SchemaValidations) SetMinimum

func (sv SchemaValidations) SetMinimum(val float64, exclusive bool)

func (SchemaValidations) SetMultipleOf

func (sv SchemaValidations) SetMultipleOf(val float64)

func (SchemaValidations) SetPattern

func (sv SchemaValidations) SetPattern(val string)

func (SchemaValidations) SetPatternProperties added in v0.35.0

func (sv SchemaValidations) SetPatternProperties(pattern string)

SetPatternProperties adds one regex → empty-schema entry to the schema's patternProperties map. The empty value schema (`{}`) means "properties whose name matches pattern are allowed (any value)" — the annotation surface carries only the regex, not a per-pattern value schema. Repeated patternProperties lines accumulate distinct entries.

func (SchemaValidations) SetUnique

func (sv SchemaValidations) SetUnique(val bool)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL