From 2072c8e0ac0dbff846268c68a349214f131ee310 Mon Sep 17 00:00:00 2001 From: Frederic BIDON Date: Sat, 27 Jun 2026 16:12:34 +0200 Subject: [PATCH 1/5] docs(doc-site): one page per swagger:* annotation; alpha-sort the index MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Split the monolithic maintainers/annotations.md reference into a section landing page plus one leaf page per annotation. The landing page keeps the shared intro (how annotations attach, argument shapes, the keyword compatibility matrix); each of the 17 leaf pages carries the EBNF-like syntax, its supported keywords, and at least one worked example, with deprecated/legacy forms named (no dedicated example) where a legacy syntax is still accepted — swagger:alias, swagger:type array, swagger:name vs the name: keyword, and the + name: chunk sigil vs the - name: YAML alias. Sort the at-a-glance annotation index alphabetically and repoint its Reference column, plus 28 cross-references across keywords, options, grammar, the shaping-the-output how-tos and the tutorials, at the new per-annotation leaf pages. Pure documentation move; no scanner or golden changes. Co-Authored-By: Claude Opus 4.8 Signed-off-by: Frederic BIDON --- docs/doc-site/annotation-index/_index.md | 34 +- docs/doc-site/maintainers/annotations.md | 1171 ----------------- .../maintainers/annotations/_index.md | 146 ++ .../swagger-additionalproperties.md | 63 + .../maintainers/annotations/swagger-alias.md | 46 + .../maintainers/annotations/swagger-allof.md | 77 ++ .../annotations/swagger-default.md | 46 + .../maintainers/annotations/swagger-enum.md | 103 ++ .../maintainers/annotations/swagger-file.md | 47 + .../maintainers/annotations/swagger-ignore.md | 60 + .../maintainers/annotations/swagger-meta.md | 65 + .../maintainers/annotations/swagger-model.md | 77 ++ .../maintainers/annotations/swagger-name.md | 66 + .../annotations/swagger-operation.md | 91 ++ .../annotations/swagger-parameters.md | 87 ++ .../annotations/swagger-patternproperties.md | 62 + .../annotations/swagger-response.md | 83 ++ .../maintainers/annotations/swagger-route.md | 105 ++ .../maintainers/annotations/swagger-strfmt.md | 67 + .../maintainers/annotations/swagger-type.md | 99 ++ docs/doc-site/maintainers/keywords.md | 8 +- .../forcing-a-format.md | 2 +- .../names-and-refs/alias-rendering.md | 2 +- docs/doc-site/tutorials/document-metadata.md | 2 +- .../tutorials/maps-and-free-form-objects.md | 2 +- docs/doc-site/tutorials/model-definitions.md | 2 +- docs/doc-site/tutorials/polymorphic-models.md | 2 +- .../tutorials/routes-and-operations.md | 2 +- 28 files changed, 1418 insertions(+), 1199 deletions(-) delete mode 100644 docs/doc-site/maintainers/annotations.md create mode 100644 docs/doc-site/maintainers/annotations/_index.md create mode 100644 docs/doc-site/maintainers/annotations/swagger-additionalproperties.md create mode 100644 docs/doc-site/maintainers/annotations/swagger-alias.md create mode 100644 docs/doc-site/maintainers/annotations/swagger-allof.md create mode 100644 docs/doc-site/maintainers/annotations/swagger-default.md create mode 100644 docs/doc-site/maintainers/annotations/swagger-enum.md create mode 100644 docs/doc-site/maintainers/annotations/swagger-file.md create mode 100644 docs/doc-site/maintainers/annotations/swagger-ignore.md create mode 100644 docs/doc-site/maintainers/annotations/swagger-meta.md create mode 100644 docs/doc-site/maintainers/annotations/swagger-model.md create mode 100644 docs/doc-site/maintainers/annotations/swagger-name.md create mode 100644 docs/doc-site/maintainers/annotations/swagger-operation.md create mode 100644 docs/doc-site/maintainers/annotations/swagger-parameters.md create mode 100644 docs/doc-site/maintainers/annotations/swagger-patternproperties.md create mode 100644 docs/doc-site/maintainers/annotations/swagger-response.md create mode 100644 docs/doc-site/maintainers/annotations/swagger-route.md create mode 100644 docs/doc-site/maintainers/annotations/swagger-strfmt.md create mode 100644 docs/doc-site/maintainers/annotations/swagger-type.md diff --git a/docs/doc-site/annotation-index/_index.md b/docs/doc-site/annotation-index/_index.md index b7616b7..adca711 100644 --- a/docs/doc-site/annotation-index/_index.md +++ b/docs/doc-site/annotation-index/_index.md @@ -12,23 +12,23 @@ tutorial that shows the annotation as runnable Go next to the spec it produces; | Annotation | Attaches to | Produces | By example | Reference | |---|---|---|---|---| -| `swagger:meta` | package doc | top-level `info`, `host`, `basePath`, `schemes`, … | [example]({{% relref "/tutorials/document-metadata#swaggermeta" %}}) | [reference]({{% relref "/maintainers/annotations#swaggermeta" %}}) | -| `swagger:model` | type declaration | a `definitions` entry | [example]({{% relref "/tutorials/model-definitions#swaggermodel" %}}) | [reference]({{% relref "/maintainers/annotations#swaggermodel" %}}) | -| `swagger:strfmt` | type declaration | `{type: string, format: …}` at every use | [example]({{% relref "/tutorials/model-definitions#swaggerstrfmt" %}}) | [reference]({{% relref "/maintainers/annotations#swaggerstrfmt" %}}) | -| `swagger:enum` | named type | an `enum` array (+ `x-go-enum-desc`) | [example]({{% relref "/tutorials/model-definitions#swaggerenum" %}}) | [reference]({{% relref "/maintainers/annotations#swaggerenum" %}}) | -| `swagger:allOf` | embedded field / struct | an `allOf` composition | [example]({{% relref "/tutorials/model-definitions#swaggerallof" %}}) | [reference]({{% relref "/maintainers/annotations#swaggerallof" %}}) | -| `swagger:alias` *(deprecated)* | type alias | **no effect** — alias rendering is controlled by Go aliases + options | [how-to]({{% relref "alias-rendering" %}}) | [reference]({{% relref "/maintainers/annotations#swaggeralias--deprecated" %}}) | -| `swagger:route` | func / var doc | a `paths` entry + operation | [example]({{% relref "/tutorials/routes-and-operations#swaggerroute" %}}) | [reference]({{% relref "/maintainers/annotations#swaggerroute" %}}) | -| `swagger:operation` | func / var doc | a `paths` entry (YAML body) | [example]({{% relref "/tutorials/routes-and-operations#swaggeroperation" %}}) | [reference]({{% relref "/maintainers/annotations#swaggeroperation" %}}) | -| `swagger:parameters` | struct declaration | parameters on the named operation(s) | [example]({{% relref "/tutorials/routes-and-operations#swaggerparameters" %}}) | [reference]({{% relref "/maintainers/annotations#swaggerparameters" %}}) | -| `swagger:response` | struct declaration | a `responses` entry | [example]({{% relref "/tutorials/routes-and-operations#swaggerresponse" %}}) | [reference]({{% relref "/maintainers/annotations#swaggerresponse" %}}) | -| `swagger:ignore` | type / field doc | excludes the declaration | [example]({{% relref "/tutorials/model-definitions#swaggerignore" %}}) | [reference]({{% relref "/maintainers/annotations#swaggerignore" %}}) | -| `swagger:name` | field / method doc | renames a JSON property | [example]({{% relref "/tutorials/model-definitions#swaggername" %}}) | [reference]({{% relref "/maintainers/annotations#swaggername" %}}) | -| `swagger:type` | type / field doc | overrides the inferred Swagger type | [example]({{% relref "/tutorials/model-definitions#swaggertype" %}}) | [reference]({{% relref "/maintainers/annotations#swaggertype" %}}) | -| `swagger:additionalProperties` | type doc | object `additionalProperties` (open / closed / typed) | [example]({{% relref "/tutorials/maps-and-free-form-objects#open--closed-objects" %}}) | [reference]({{% relref "/maintainers/annotations#swaggeradditionalproperties" %}}) | -| `swagger:patternProperties` | type doc | typed `patternProperties` (regex → value) | [example]({{% relref "/tutorials/maps-and-free-form-objects#pattern-properties" %}}) | [reference]({{% relref "/maintainers/annotations#swaggerpatternproperties" %}}) | -| `swagger:file` | param / response field | `{type: file}` | [example]({{% relref "/tutorials/routes-and-operations#swaggerfile" %}}) | [reference]({{% relref "/maintainers/annotations#swaggerfile" %}}) | -| `swagger:default` | value / field doc | a default-value anchor | [example]({{% relref "/tutorials/examples-and-defaults#swaggerdefault" %}}) | [reference]({{% relref "/maintainers/annotations#swaggerdefault" %}}) | +| `swagger:additionalProperties` | type doc | object `additionalProperties` (open / closed / typed) | [example]({{% relref "/tutorials/maps-and-free-form-objects#open--closed-objects" %}}) | [reference]({{% relref "/maintainers/annotations/swagger-additionalproperties" %}}) | +| `swagger:alias` *(deprecated)* | type alias | **no effect** — alias rendering is controlled by Go aliases + options | [how-to]({{% relref "alias-rendering" %}}) | [reference]({{% relref "/maintainers/annotations/swagger-alias" %}}) | +| `swagger:allOf` | embedded field / struct | an `allOf` composition | [example]({{% relref "/tutorials/model-definitions#swaggerallof" %}}) | [reference]({{% relref "/maintainers/annotations/swagger-allof" %}}) | +| `swagger:default` | value / field doc | a default-value anchor | [example]({{% relref "/tutorials/examples-and-defaults#swaggerdefault" %}}) | [reference]({{% relref "/maintainers/annotations/swagger-default" %}}) | +| `swagger:enum` | named type | an `enum` array (+ `x-go-enum-desc`) | [example]({{% relref "/tutorials/model-definitions#swaggerenum" %}}) | [reference]({{% relref "/maintainers/annotations/swagger-enum" %}}) | +| `swagger:file` | param / response field | `{type: file}` | [example]({{% relref "/tutorials/routes-and-operations#swaggerfile" %}}) | [reference]({{% relref "/maintainers/annotations/swagger-file" %}}) | +| `swagger:ignore` | type / field doc | excludes the declaration | [example]({{% relref "/tutorials/model-definitions#swaggerignore" %}}) | [reference]({{% relref "/maintainers/annotations/swagger-ignore" %}}) | +| `swagger:meta` | package doc | top-level `info`, `host`, `basePath`, `schemes`, … | [example]({{% relref "/tutorials/document-metadata#swaggermeta" %}}) | [reference]({{% relref "/maintainers/annotations/swagger-meta" %}}) | +| `swagger:model` | type declaration | a `definitions` entry | [example]({{% relref "/tutorials/model-definitions#swaggermodel" %}}) | [reference]({{% relref "/maintainers/annotations/swagger-model" %}}) | +| `swagger:name` | field / method doc | renames a JSON property | [example]({{% relref "/tutorials/model-definitions#swaggername" %}}) | [reference]({{% relref "/maintainers/annotations/swagger-name" %}}) | +| `swagger:operation` | func / var doc | a `paths` entry (YAML body) | [example]({{% relref "/tutorials/routes-and-operations#swaggeroperation" %}}) | [reference]({{% relref "/maintainers/annotations/swagger-operation" %}}) | +| `swagger:parameters` | struct declaration | parameters on the named operation(s) | [example]({{% relref "/tutorials/routes-and-operations#swaggerparameters" %}}) | [reference]({{% relref "/maintainers/annotations/swagger-parameters" %}}) | +| `swagger:patternProperties` | type doc | typed `patternProperties` (regex → value) | [example]({{% relref "/tutorials/maps-and-free-form-objects#pattern-properties" %}}) | [reference]({{% relref "/maintainers/annotations/swagger-patternproperties" %}}) | +| `swagger:response` | struct declaration | a `responses` entry | [example]({{% relref "/tutorials/routes-and-operations#swaggerresponse" %}}) | [reference]({{% relref "/maintainers/annotations/swagger-response" %}}) | +| `swagger:route` | func / var doc | a `paths` entry + operation | [example]({{% relref "/tutorials/routes-and-operations#swaggerroute" %}}) | [reference]({{% relref "/maintainers/annotations/swagger-route" %}}) | +| `swagger:strfmt` | type declaration | `{type: string, format: …}` at every use | [example]({{% relref "/tutorials/model-definitions#swaggerstrfmt" %}}) | [reference]({{% relref "/maintainers/annotations/swagger-strfmt" %}}) | +| `swagger:type` | type / field doc | overrides the inferred Swagger type | [example]({{% relref "/tutorials/model-definitions#swaggertype" %}}) | [reference]({{% relref "/maintainers/annotations/swagger-type" %}}) | ## Keywords, not annotations diff --git a/docs/doc-site/maintainers/annotations.md b/docs/doc-site/maintainers/annotations.md deleted file mode 100644 index e6f5d70..0000000 --- a/docs/doc-site/maintainers/annotations.md +++ /dev/null @@ -1,1171 +0,0 @@ ---- -title: "Annotations" -weight: 10 -description: "The swagger:* annotation vocabulary: what each produces, where it attaches, and the keywords it admits." ---- - - -Annotations are the `swagger:` markers the scanner recognises in -Go doc comments. Each annotation classifies the surrounding -declaration — telling the scanner "this is a model definition", "this -is a route handler", "this is meta-information about the API" — and -opens the door for [keywords]({{% relref "keywords" %}}) inside the same comment -block. - -There are twelve annotations. They divide cleanly by what they -attach to: - -- **Spec-level**: `swagger:meta`. -- **Model declarations**: `swagger:model`, `swagger:strfmt`, - `swagger:enum`, `swagger:allOf`, `swagger:alias`, - `swagger:additionalProperties`, `swagger:patternProperties`. -- **Operation declarations**: `swagger:route`, `swagger:operation`. -- **Companion declarations**: `swagger:parameters`, `swagger:response`. -- **Local hints**: `swagger:ignore`, `swagger:name`, `swagger:type`, - `swagger:file`, `swagger:default`. - -This file is the **author-first reference**. Each entry covers: - -- What the annotation does and what it produces in the spec. -- Where in the Go source it goes (package doc, type doc, field doc). -- The shape of any argument the annotation accepts. -- A short Go sample. -- A pointer to the keywords that are legal inside the block. -- A pointer to a real fixture in this repo for the full executable - example. - -For the per-keyword reference, see [keywords.md]({{% relref "keywords" %}}). -For the embedded sub-languages (`Parameters:` and `Responses:` body -grammars, YAML extensions, etc.), see -[sub-languages.md]({{% relref "sub-languages" %}}). For the formal grammar, -see [grammar.md]({{% relref "grammar" %}}). - ---- - -## Table of contents - -- [How annotations attach](#how-annotations-attach) -- [Annotation argument shapes](#annotation-argument-shapes) -- [`swagger:meta`](#swaggermeta) -- [`swagger:model`](#swaggermodel) -- [`swagger:strfmt`](#swaggerstrfmt) -- [`swagger:enum`](#swaggerenum) -- [`swagger:allOf`](#swaggerallof) -- [`swagger:alias`](#swaggeralias) -- [`swagger:route`](#swaggerroute) -- [`swagger:operation`](#swaggeroperation) -- [`swagger:parameters`](#swaggerparameters) -- [`swagger:response`](#swaggerresponse) -- [`swagger:ignore`](#swaggerignore) -- [`swagger:name`](#swaggername) -- [`swagger:type`](#swaggertype) -- [`swagger:additionalProperties`](#swaggeradditionalproperties) -- [`swagger:patternProperties`](#swaggerpatternproperties) -- [`swagger:file`](#swaggerfile) -- [`swagger:default`](#swaggerdefault) - ---- - -## How annotations attach - -An annotation is recognised when it appears at the start of a comment -line in a doc comment. Leading whitespace, the `//` marker, and any -`/* */` block-comment continuation noise are stripped — the lexer -applies the same content-prefix-trim that every other godoc-aware -tool does. - -Annotations attach to whichever Go declaration owns the comment -group: - -- **Package doc** (`// Package foo …` followed by `package foo`) — - carries `swagger:meta`. -- **Type declaration** (`type T struct { … }`, `type T int`, - `type T = Other`) — carries `swagger:model`, `swagger:strfmt`, - `swagger:enum`, `swagger:allOf`, `swagger:alias`, `swagger:ignore`, - `swagger:type`. Inside a grouped declaration (`type ( A …; B … )`) - the comment on each individual spec is honoured independently — the - annotation attaches to its own `TypeSpec`, not to the enclosing group — - so two types in one group can carry distinct docs and annotations. -- **Function or variable declaration** (`func ServeAPI() { … }`, - `var DoIt = func() { … }`) — carries `swagger:route`, - `swagger:operation`. These two are recognised whether the annotation - sits in the function's doc comment or **inside the function body**. - A `swagger:model` or `swagger:parameters` declared on a type **local - to a function body** is likewise discovered. -- **Struct field doc** — carries `swagger:name`, `swagger:type`, - `swagger:ignore`, plus any of the [keyword reference]({{% relref "keywords" %}}) - entries legal in `schema` / `param` / `header` context. - -One comment group may carry MORE than one annotation when the -combinations are semantically compatible — e.g. `swagger:model` + -`swagger:type` together overrides the auto-detected Go type while -still publishing the model. The grammar parses both and the builder -honours both. - -The **first** annotation in source order wins as the "primary" -classifier — for example, a comment carrying `swagger:model` followed -by `swagger:ignore` produces a model (the ignore is silently -overridden because only the source-order-first annotation drives the -short-circuit). Subsequent annotations are still parsed and visible -via `Block.AnnotationKind()`-iteration, but the primary classifier -determines which builder owns the decl. - -{{% notice style="warning" %}} -Recognition is purely positional: **any** comment line that begins with a -`swagger:` token is treated as that annotation — even when you meant it as -prose. A description line like `swagger:type controls the emitted type` on a -type's doc comment is parsed as a `swagger:type` annotation. Keep annotation -names mid-sentence in descriptions (`The swagger:type directive …`) or wrap them -in backticks so the line does not *start* with the token. -{{% /notice %}} - -## Annotation argument shapes - -After the `swagger:` head, an annotation may carry positional -arguments. The shapes: - -- **No args**: `swagger:meta`, `swagger:ignore`, `swagger:enum`, - `swagger:allOf`, `swagger:file`, `swagger:default` — bare - annotation, the surrounding decl supplies the entity name. -- **One IDENT arg**: `swagger:model Pet`, `swagger:response - errorResponse`, `swagger:strfmt uuid`, `swagger:name fullName`, - `swagger:type integer`, `swagger:alias TimestampAlias` — the - argument overrides or names the entity. -- **One IDENT arg, optional**: `swagger:model` (bare — derives the - name from the Go decl) vs `swagger:model Pet` (overrides). -- **List of IDENT args**: `swagger:parameters listItems createItem` - — declares the parameters group as legal for multiple operations. -- **Header line**: `swagger:route GET /pets pets users listPets` and - `swagger:operation GET /pets users listPets` — a structured header - carrying method, path, tags, and operation ID. See the - per-annotation entries for the exact rules. - ---- - -## `swagger:meta` - -**What it does.** Declares the package as the OpenAPI spec -container. The scanner reads the package doc comment for top-level -spec fields: title (via [stripPackagePrefix]({{% relref "grammar#prose" %}}) of -the doc's first line), description, license, contact, host, -basePath, version, schemes, consumes, produces, securityDefinitions, -extensions, and the rest of the meta keyword surface. - -**Where it goes.** On the package doc comment. - -**Argument shape.** No args. Bare annotation. - -**Sample.** - -```go -// Package petstore Petstore API. -// -// The purpose of this application is to provide an application -// that is using plain Go code to define an API. -// -// Schemes: http, https -// Host: petstore.swagger.io -// BasePath: /v2 -// Version: 1.0.0 -// -// Consumes: -// - application/json -// -// Produces: -// - application/json -// -// swagger:meta -package petstore -``` - -**Legal keywords.** All [meta single-line keywords]({{% relref "keywords#meta-single-line-keywords" %}}) -(`schemes`, `version`, `host`, `basePath`, `license`, `contact`) plus -the meta-scope [body keywords]({{% relref "keywords#body-keywords" %}}) -(`consumes`, `produces`, `security`, `securityDefinitions`, -`extensions`, `infoExtensions`, `tos`, `externalDocs`, `tags`). A -`Tags:` block declares the spec's top-level `tags` (name, description, -nested `externalDocs`, `x-*` extensions per tag). - -**Full example.** `fixtures/goparsing/spec/api.go`. - ---- - -## `swagger:model` - -**What it does.** Declares a Go type as a published model. The -scanner walks the type, emits a schema into the spec's `definitions` -map, and resolves cross-references between models. - -**Where it goes.** On a type declaration (`type T struct { … }`, -`type T int`, `type T = Other`, …). - -**Argument shape.** Optional IDENT — the name the model takes in -`definitions`. Default: the Go type's name. The name must be a plain -identifier (a JSON label), not a Go-qualified name — a dotted name such -as `utils.Error` is rejected with a warning and dropped. Cross-package -types are resolved automatically, so reference a model by its bare name. - -**Ordering.** The descriptive prose must come **before** the -`swagger:model` line. The title/description split follows a heuristic: -a single-line comment **ending in a period** becomes the `title`; a -single-line comment **without** a trailing period becomes the -`description`; a **multi-line** comment uses the first line as `title` -and the remaining paragraphs as `description`. An annotation-first block -(the `swagger:model` line ahead of the prose) still publishes the model -but drops its title and description. - -**Sample.** - -```go -// Pet is the petstore's primary entity. -// -// swagger:model -type Pet struct { - // ID is the unique identifier. - ID int64 `json:"id"` - - // Name is the pet's display name. - Name string `json:"name"` - - // Tags categorise the pet. - Tags []string `json:"tags,omitempty"` -} -``` - -With a name override: - -```go -// swagger:model PetWithExtras -type DetailedPet struct { … } -``` - -The type is published as `#/definitions/PetWithExtras`. - -**Multiple names on one line.** A field group declaring several names -(`R, G, B, A uint8`) emits **one property per name**. A `json:` tag on -such a group cannot rename the individual fields — each keeps its own -name — though tag options still apply. - -**Legal keywords.** All [schema]({{% relref "keywords#schema-decorators" %}}) -keywords plus the -[length / array / numeric validations]({{% relref "keywords#numeric-validations" %}}) -on field doc comments. - -**Full example.** `fixtures/enhancements/named-struct-tags-ref/types.go`. - ---- - -## `swagger:strfmt` - -**What it does.** Marks a named type as a custom string format. -Wherever the type appears as a field, the emitted schema is -`{type: string, format: }`. Useful for `UUID`, `Email`, -`URL`-style types that have a Go type but should serialise as a -JSON string with a known format. - -**Where it goes.** On a type declaration whose underlying form is a -string-marshalable type (typically implementing `encoding.TextMarshaler` -or `encoding.TextUnmarshaler`). - -**Argument shape.** Required IDENT — the format name (`uuid`, `email`, -`mac`, etc.). - -**Sample.** - -```go -// MAC is a hardware address rendered as a colon-separated hex string. -// -// swagger:strfmt mac -type MAC string - -func (m MAC) MarshalText() ([]byte, error) { return []byte(m), nil } -func (m *MAC) UnmarshalText(b []byte) error { *m = MAC(b); return nil } -``` - -A field typed `MAC` emits as `{type: string, format: mac}`. The -underlying `MAC` type does NOT appear as a top-level model definition -(strfmt-tagged structs are replaced by their format at every -reference). A slice of the type (`[]MAC`) carries the format onto its -items: `{type: array, items: {type: string, format: mac}}`. - -**With `swagger:model`.** Adding `swagger:model` to the strfmt type opts -it into a **first-class definition** carrying the full -`{type: string, format: …}` schema, and referencing fields point at it -via `$ref` — the general `swagger:model ⇒ definition + $ref` rule. Without -`swagger:model`, the format inlines at every reference as above. - -**Field-level override.** `swagger:strfmt` may also sit on a struct -**field** doc to override just that field's published format — e.g. -`// swagger:strfmt int64` on a `uint64` field emits -`{type: string, format: int64}`, a precision-safe, JSON-conformant -string encoding. (By default codescan emits Go-specific integer formats -for unsized/large ints — `uint64` → `{integer, format: uint64}`, -`uint32` → `{integer, format: uint32}`. These vendor formats round-trip -back to Go but are not part of the Swagger 2.0 format set; the -`swagger:strfmt int64` string override is the conformant alternative.) - -**Legal keywords.** None at the type level beyond `swagger:strfmt` -itself; the format name is the entire surface. - -**Full example.** `fixtures/enhancements/text-marshal/types.go`. - ---- - -## `swagger:enum` - -**What it does.** Marks a string-typed (or integer-typed) named type -as an enum and collects the type's `const` declarations. - -- **Without `swagger:model`** (the default): the values are applied - **inline on each model field that references the type** — the - property gets an `enum` array plus an `x-go-enum-desc` extension - carrying the per-value godoc descriptions in ` ` - shape. The enum type itself is not a standalone definition; the - values travel with each referencing property. -- **With `swagger:model`**: the enum becomes a **first-class - definition** carrying the `enum` array (+ `x-go-enum-desc`), and - referencing fields point at it via `$ref` — the general - `swagger:model ⇒ definition + $ref` rule applied to enums. - -(Edge case: if `swagger:enum` names a type for which no matching -`const` values are found, the enum semantics are dropped and the type -falls through to ordinary type resolution — typically a plain -definition referenced by `$ref`, with no `enum` array.) - -**Where it goes.** On a named type declaration. The type's `const` -values are discovered via Go's type-system traversal; they do not -need to live in the same file. The values surface only when a model -reaches the enum type through a field. - -**Argument shape.** Optional IDENT naming the type whose `const` -values to collect. On a type declaration the name is redundant, so the -**bare `swagger:enum`** form is accepted and infers the name from the -declared type. `swagger:enum Priority` and a bare `swagger:enum` on -`type Priority …` are equivalent. - -**Sample.** - -```go -// Priority is the urgency level on a task. -// -// swagger:enum Priority -type Priority string - -const ( - // PriorityLow is for tasks that can wait. - PriorityLow Priority = "low" - - // PriorityMedium is the default. - PriorityMedium Priority = "medium" - - // PriorityHigh is for tasks that must run soon. - PriorityHigh Priority = "high" -) - -// Task references Priority, which is what makes the enum reachable. -// -// swagger:model -type Task struct { - Priority Priority `json:"priority"` -} -``` - -Produces (extract) — the values land on `Task`'s `priority` property, -not on a `Priority` definition: - -```json -{ - "Task": { - "type": "object", - "properties": { - "priority": { - "description": "Priority is the task's urgency.\nlow PriorityLow is for tasks that can wait.\nmedium PriorityMedium is the default.\nhigh PriorityHigh is for tasks that must run soon.", - "type": "string", - "enum": ["low", "medium", "high"], - "x-go-enum-desc": "low PriorityLow is for tasks that can wait.\nmedium PriorityMedium is the default.\nhigh PriorityHigh is for tasks that must run soon." - } - } - } -} -``` - -By default the const→value mapping is folded into the property's -`description` (as above) **and** duplicated in `x-go-enum-desc`. Set the -scanner option `SkipEnumDescriptions: true` to keep the authored prose as -the description; the mapping then rides `x-go-enum-desc` only. See -[Vendor extensions]({{% relref "vendor-extensions" %}}). - -**Legal keywords.** Schema-context keywords. The `enum:` keyword can -ALSO be used inline on the type doc to force a value set; when present, -it overrides the const-derived values and the `x-go-enum-desc` is -recomputed (or dropped) accordingly. - -**Full example.** `fixtures/enhancements/enum-overrides/types.go`. - ---- - -## `swagger:allOf` - -**What it does.** Marks a struct as participating in an `allOf` -composition. The struct's fields plus any embedded -`swagger:model`-tagged base produce an `allOf: [$ref base, {inline -fields}]` schema. The companion convention is to embed the base -type as an anonymous field with this annotation on the embedding's -doc comment (or on the embedded type itself). - -**Where it goes.** On a struct field that embeds another type, or on -a struct type that has at least one embedded base. - -**Argument shape.** No args. - -**Sample.** - -```go -// Animal is the abstract base. -// -// swagger:model -type Animal struct { - Kind string `json:"kind"` -} - -// Dog is an Animal with a breed. -// -// swagger:model -type Dog struct { - // swagger:allOf - Animal - - Breed string `json:"breed"` -} -``` - -Produces: - -```json -"Dog": { - "allOf": [ - {"$ref": "#/definitions/Animal"}, - { - "type": "object", - "properties": { - "breed": {"type": "string", "x-go-name": "Breed"} - } - } - ] -} -``` - -**Legal keywords.** Schema-context keywords on the inline-object -member (the second `allOf` element). - -**Inside a response body.** The same composition applies when the -embedding struct is a `swagger:response` body. The embedded base emits -an `allOf: [{$ref}, …]` arm **only when it is a `swagger:model`** — i.e. -a definition exists to point at. If the embedded type is itself a -`swagger:response` (which has no definition), its fields are inlined -instead of producing a `$ref`. - -**Full example.** `fixtures/enhancements/allof-edges/types.go`. - ---- - -## `swagger:alias` — DEPRECATED - -**Deprecated.** `swagger:alias` is deprecated and no longer affects the -emitted spec — it is an empty sink that only raises a `validate.deprecated` -diagnostic. (Earlier documentation claimed it published a `$ref` to the -alias target; that was never accurate. Its only real effect was to -force a named **primitive** type to inline its scalar — e.g. `{type: -string}` — instead of producing the `$ref` a named type otherwise gets. -That force-inline behaviour has been removed.) - -**Migration.** - -- To **inline** a type at a use site, use `swagger:type inline` on the - field (see [`swagger:type`](#swaggertype)). -- To publish a type as a **first-class definition** that fields `$ref`, - use `swagger:model`. -- To control alias rendering **globally**, use the `RefAliases` / - `TransparentAliases` options. A plain (unannotated) Go alias `type T = - Other` dissolves to its target by default. - -**Where it went.** On a type alias / named-type declaration. - -**Argument shape.** Optional IDENT (ignored — the annotation has no effect). - ---- - -## `swagger:route` - -**What it does.** Declares an HTTP route + operation in one -annotation. The header line carries the method, path, optional tags, -and the operation ID; the comment body carries the operation's -metadata (consumes / produces / schemes / security / parameters / -responses / extensions). - -This is the **terser of the two operation-declaration annotations**. -Most go-swagger projects use `swagger:route` for hand-written -operations. - -**Where it goes.** On a function or variable declaration whose doc -comment carries the annotation. The Go entity itself doesn't have to -be a handler — the annotation publishes a path/operation independent -of the carrier. - -**Argument shape.** Header line: - -``` -swagger:route [tag1 tag2 …] -``` - -- `` — `GET`, `POST`, `PUT`, `DELETE`, `PATCH`, `HEAD`, - `OPTIONS`. Case insensitive. -- `` — starts with `/`. Supports path-parameter braces: - `/items/{id}`. Path templating follows - [RFC 6570](https://www.rfc-editor.org/rfc/rfc6570) URI Template - **Level-1 expansion** only (simple `{name}` substitution), as - required by OpenAPI 2.0. An inline regex constraint written in the - gorilla/chi style (`/items/{id:[0-9]+}`) is **stripped** to the bare - `/items/{id}` form and a warning is emitted — OpenAPI 2.0 cannot - express the constraint, so it is dropped rather than silently failing - the whole route. The same applies to `swagger:operation`. -- `[tag1 tag2 …]` — optional whitespace-separated list of tags. At - least two characters each. -- `` — the unique operation identifier. - -A godoc-style identifier may precede the annotation on the same -comment line: - -```go -// ListPets swagger:route GET /pets pets users listPets -``` - -That leading identifier is recognised as a godoc convention and is -not part of the annotation surface. - -**Sample.** - -```go -// ListPets swagger:route GET /pets pets users listPets -// -// List pets filtered by some parameters. -// -// Consumes: -// - application/json -// -// Produces: -// - application/json -// -// Schemes: http, https -// -// Security: -// api_key: -// oauth: read, write -// -// Parameters: -// + name: limit -// in: query -// type: integer -// minimum: 1 -// maximum: 100 -// -// Responses: -// 200: body:[]Pet the pet list -// default: response:genericError -func ListPets() {} -``` - -**Legal keywords.** All -[body keywords]({{% relref "keywords#body-keywords" %}}) legal in route context -(`consumes`, `produces`, `schemes`, `security`, `parameters`, -`responses`, `extensions`, `externalDocs`) plus inline `deprecated:` and a -body `tags:` list (a string list, unioned and deduplicated with the -header-line tags). The same applies to `swagger:operation`. - -The `Parameters:` and `Responses:` sub-languages are documented in -[sub-languages.md §parameters]({{% relref "sub-languages#parameters" %}}) and -[sub-languages.md §responses]({{% relref "sub-languages#responses" %}}). - -**Full example.** `fixtures/enhancements/routes-full-petstore-shape/handlers.go`. - ---- - -## `swagger:operation` - -**What it does.** Same payload as `swagger:route` but with a -different body shape: instead of the structured `Parameters:` / -`Responses:` keyword surface, `swagger:operation`'s body is a -single YAML document spelling out the OpenAPI operation object -directly. - -Use `swagger:operation` when you want to author the operation in -YAML (closer to the OpenAPI spec text) or when the operation has -shapes the keyword surface doesn't cover. - -**Where it goes.** Same as `swagger:route` — function or variable -doc comment. - -**Argument shape.** Same header shape as `swagger:route`: - -``` -swagger:operation [tag1 tag2 …] -``` - -**Sample.** - -```go -// swagger:operation GET /items/{id} items getItem -// -// --- -// summary: Get item by ID -// parameters: -// - name: id -// in: path -// required: true -// type: integer -// responses: -// '200': -// description: the requested item -// schema: -// $ref: '#/definitions/Item' -// default: -// $ref: '#/responses/genericError' -func GetItem() {} -``` - -The `---` delimits the YAML body; everything between the fences is -parsed as an OpenAPI 2.0 operation object. - -**Legal keywords.** None inside the YAML body (it's structurally -YAML, not the keyword grammar). The header line is the entire -annotation surface. - -**Full example.** `fixtures/enhancements/parameters-map-postdecl/api.go`. - ---- - -## `swagger:parameters` - -**What it does.** Declares a Go struct as the parameters set for one -or more operations. Each field of the struct becomes one parameter -on the named operation(s). The field's doc comment carries the -parameter's `in:`, `required:`, validation, and description. - -Each parameter's **name** comes from the field's `json:` tag, falling -back to the Go field name when there is no tag. The `form:` tag is not -consulted — add a `json:` tag to control the parameter name (a -`form:"sort_key"` tag alone leaves the name as the Go identifier). A -`name:` keyword in the field doc takes precedence over both, setting the -parameter name explicitly — it is the [universal field-naming -keyword]({{% relref "/maintainers/keywords#name" %}}) and is canonical -here. (The `swagger:name` *annotation* is the legacy form for model -properties and interface methods; in a parameter context it is inert and -now emits a `context-invalid` diagnostic pointing you at `name:`.) - -**Where it goes.** On a struct declaration. A bare slice variable -(`var Filters []string`) carries no `in:`/`type:`/`required:` per -field, so it cannot drive parameter generation — parameters must be a -struct. - -**Argument shape.** Required IDENTs — the operation IDs this -parameters set applies to. At least one. The same operation ID may -appear in multiple `swagger:parameters` annotations to compose a -parameter set from several structs. Conversely, **one struct may carry -several `swagger:parameters` lines**, each listing a different subset of -operation IDs — the lists accumulate, so a long operation-ID list can be -split across multiple annotation lines for readability. - -**Across packages.** The struct need not sit in the same package as the -`swagger:route` or `swagger:operation` it serves. `swagger:parameters` -(and `swagger:response`) declarations are collected across **all scanned -packages** and matched to operations by operation ID — so a shared -parameter set can live in its own package, as long as that package is in -the scan set. - -**Shared & path-item targets.** The first argument need not be an -operation ID. A `*` wildcard or a `/path` switches the annotation to the -spec-level **shared namespace**: - -- `swagger:parameters *` registers the struct's fields at the spec top - level (`#/parameters/{name}`) for reuse; `swagger:parameters * …` - also `$ref`s them into the listed operations. -- `swagger:parameters /path` inlines the fields into that **exact** - path-item (OAS2 has no path hierarchy). -- A standalone `swagger:parameters …` (or `/path …`) - marker on a **function** `$ref`s an already-registered shared parameter - into that operation / path-item. - -See [Sharing parameters & responses]({{% relref "/tutorials/sharing-parameters-and-responses" %}}) -for the walkthrough and the keep-first / dangling-ref diagnostics. - -**Sample.** - -```go -// ListItemsParams declares pagination + filter parameters for the -// listItems operation. -// -// swagger:parameters listItems -type ListItemsParams struct { - // Offset is the page offset. - // - // in: query - // minimum: 0 - // default: 0 - Offset int `json:"offset"` - - // Limit is the page size. - // - // in: query - // minimum: 1 - // maximum: 100 - // default: 20 - Limit int `json:"limit"` - - // Tag is the filter tag. - // - // in: query - // required: false - Tag string `json:"tag,omitempty"` -} -``` - -**Legal keywords on fields.** [param-context keywords]({{% relref "keywords#parameter-location" %}}) -(`in`, `required`, the numeric / length / format validations, -`default`, `example`, `enum`, `allowEmptyValue`, `collectionFormat`). - -**Full example.** `fixtures/enhancements/simple-schema-violation/api.go`. - ---- - -## `swagger:response` - -**What it does.** Declares a Go struct as a named response object, -emitted into the spec's top-level `responses` map. Routes / operations -reference it by name via the response sub-language (`Responses:` -body in `swagger:route`, or the YAML `$ref` form in -`swagger:operation`). - -The struct's fields contribute the response shape: - -- A field named `Body` (or carrying `in: body`) becomes the response - body schema. The body may be a struct, a `$ref`'d model, **or a - primitive** — e.g. `Body string` emits `schema: {type: string}` and - `Body []int` emits `schema: {type: array, items: {type: integer}}`. -- Other fields carrying `in: header` become response headers. A field - with **neither** `Body`/`in: body` nor `in: header` is treated as a - response **header** by default, not as a body property — so for a body - schema, name the field `Body` or mark it `in: body`. The header's key - comes from the `json:` tag / Go field name, or a `name:` keyword in the - field doc (e.g. `name: X-Rate-Limit`) to set it explicitly. -- An **anonymously embedded** struct marked `in: body` *is* the body — - the embedded type becomes the body schema (a `$ref` to the model), - exactly like a named `Body Foo` field, rather than promoting its fields. - (The same holds for `swagger:parameters`: an `in: body` embed yields the - single body parameter.) - -An `interface{}` / `any`-typed field (or a slice `[]any`) emits an empty -schema — `{}` for a scalar field, `{type: array, items: {}}` for a -slice. An empty schema means "any type" and is valid OpenAPI 2.0; this is -intentional, not a missing type. - -**Where it goes.** On a struct declaration. - -**Argument shape.** Optional IDENT — the published response name. -Default: the Go type's name. A `*` wildcard (`swagger:response *`) is a -synonym for the bare form that explicitly marks the response as a -**shared** one, registered at `#/responses/{name}` for operations to -`$ref` by name — see [Sharing parameters & responses]({{% relref "/tutorials/sharing-parameters-and-responses" %}}). - -**Sample.** - -```go -// GenericError is the catch-all error response. -// -// swagger:response genericError -type GenericError struct { - // in: body - Body struct { - // Message is the human-readable error message. - Message string `json:"message"` - - // Code is the machine-readable error category. - Code string `json:"code,omitempty"` - } - - // X-Request-ID echoes the request correlation header. - // - // in: header - XRequestID string `json:"X-Request-ID"` -} -``` - -Routes can then reference it via `response:genericError` in their -`Responses:` body. - -**Legal keywords on body field.** Schema-context keywords. -**Legal keywords on header field.** Header-context keywords — -numeric / length / format validations, `pattern`, `enum`, `default`, -`example`, `collectionFormat`. `required:` is silently dropped on -headers (the OAS v2 Header object does not carry a `required` field). - -**Full example.** `fixtures/enhancements/routes-full-petstore-shape/handlers.go`. - ---- - -## `swagger:ignore` - -**What it does.** Excludes the surrounding declaration from the -generated spec. The scanner sees the decl and the doc, classifies -it, then drops it. - -**Where it goes.** On a type declaration to exclude the whole type, -or on a struct field doc to exclude that one field. - -**Argument shape.** No args. - -**Sample (type):** - -```go -// Internal is not exposed. -// -// swagger:ignore -type Internal struct { - SecretField string -} -``` - -**Sample (field):** - -```go -type User struct { - Name string `json:"name"` - - // PasswordHash is internal. - // - // swagger:ignore - PasswordHash string `json:"-"` -} -``` - -**Interaction:** when `swagger:ignore` appears AFTER another -classifier on the same comment block (e.g., `swagger:model` first, -then `swagger:ignore`), the first annotation wins and the ignore is -silently overridden. Place `swagger:ignore` first if you genuinely -want the decl excluded. - -**Full example.** `fixtures/enhancements/top-level-kinds/types.go`. - ---- - -## `swagger:name` - -**What it does.** Overrides the JSON property name that a struct -field or interface method renders as. By default the scanner derives -names from `json:"…"` struct tags (or the Go identifier for fields / -methods with no tag); `swagger:name` overrides that derivation when -the tag-based shape isn't appropriate — typically on **interface -methods**, which cannot carry struct tags. - -{{% notice style="note" %}} -`swagger:name` is the **legacy** annotation form. The canonical, -universal field-naming mechanism is the -[`name:` keyword]({{% relref "/maintainers/keywords#name" %}}), which -works at *every* field site — model properties, interface methods, -parameters, and response headers — with the precedence `name:` > -`swagger:name` > `json:` tag > Go field name. `swagger:name` remains -honoured (and idiomatic on interface methods, shown below), but reach -for `name:` in new code; it is the only form that works on parameters -and headers. -{{% /notice %}} - -**Where it goes.** On a struct field doc OR an interface method doc. - -**Argument shape.** Required IDENT — the JSON property name to use. - -**Sample (interface method):** - -```go -// UserProfile is the user's profile interface. -// -// swagger:model -type UserProfile interface { - // ID is the user identifier. - ID() string - - // FullName is the user's display name. - // - // swagger:name fullName - FullName() string -} -``` - -Without `swagger:name`, the method `FullName()` would publish as -property `FullName` (PascalCase). The annotation renames it to -`fullName`. - -**Legal keywords.** None — the override name is the entire surface. - -**Full example.** `fixtures/enhancements/interface-methods/types.go`. - ---- - -## `swagger:type` - -**What it does.** Replaces a field's (or named type's) inferred Swagger -type with an **inlined** type. `swagger:type` is an inline directive — it -never emits a `$ref`; the chosen type is rendered directly in place -(the default `$ref`-for-named-types is the *no-annotation* behaviour). - -**Where it goes.** On a type declaration, a struct field doc, OR a -`swagger:parameters` field doc. - -{{% notice style="note" %}} -**On a parameter field** the override collapses the field to a simple -parameter — useful when a struct- or defined-typed field would otherwise -come out typeless (invalid Swagger 2.0). The argument is restricted to a -**scalar** or a **`[]`-wrapped scalar** there: the `inline` and -type-name forms are rejected with a diagnostic, since a non-body -parameter has no schema to inline a type into. A compatible -`swagger:strfmt` on the same field still rides as a supplementary -format. -{{% /notice %}} - -**Argument shape.** Required token, one of: - -- a **scalar type** — `string`, `integer`, `number`, `boolean`, `object` - (or a Go-builtin spelling such as `int64`, `uint32`); -- **`[]T`** — an array whose items are the inlined `T` (recursive: - `[][]int64`, `[]Custom`); -- **`inline`** — expand the field's own Go type in place, instead of the - `$ref` a named type would otherwise produce; -- a **known type name** — inline that type's schema (again, no `$ref`). - -`array` is **deprecated** — use `inline`, or `[]T` for an explicit element -type; it still works, with a `validate.deprecated` warning. `file` is -rejected with a diagnostic (use [`swagger:file`](#swaggerfile)). An unknown -name falls back to inlining the field's Go type, with a -`validate.unsupported-type` diagnostic. - -**Sample (type-level override):** - -```go -// ULID is a Crockford-base32 unique identifier rendered as a string. -// -// swagger:type string -type ULID [16]byte -``` - -Fields typed `ULID` emit as `{type: string}` regardless of the -underlying `[16]byte` shape. - -**Sample (field-level override):** - -```go -type Document struct { - // Body is an opaque payload published as a string blob. - // - // swagger:type string - Body json.RawMessage `json:"body"` -} -``` - -**Interaction with `swagger:strfmt`.** `swagger:type` wins on the type -axis; a `swagger:strfmt` format on the same field is kept only when -**compatible** with the resolved type — a `string` accepts any format, -the numeric types accept the numeric width formats — otherwise it is -dropped with a shape-mismatch diagnostic. `swagger:strfmt` *alone* is -unchanged: it still forces the string-encoded `{type: string, format: …}`. - -**Interaction with `swagger:model`.** On a *type declaration* that also -carries `swagger:model`, the override shapes the type's **first-class -definition** (e.g. `swagger:type string` + `swagger:model` → a -`{type: string}` definition) and referencing fields `$ref` it — the -`swagger:model ⇒ definition + $ref` rule. The field-level inline form -above is the behaviour *without* `swagger:model`. - -**Full example.** `fixtures/enhancements/named-struct-tags-ref/types.go`. - ---- - -## `swagger:additionalProperties` - -**What it does.** Sets a schema's `additionalProperties` — the policy for -keys beyond the named properties. On a struct it **complements** the -named properties; on a map type it **overrides** the element-derived -value schema; on a type that resolved to a bare `$ref` it **defines** a -clean object. See the -[Maps & free-form objects]({{% relref "/tutorials/maps-and-free-form-objects" %}}) -tutorial. - -**Where it goes.** On a type declaration (alongside `swagger:model`). A -field-level equivalent exists as the -[`additionalProperties:` keyword]({{% relref "/maintainers/keywords#additionalproperties" %}}). - -**Argument shape.** Required token, one of: - -- **`true`** — allow arbitrary extra keys (`additionalProperties: true`); -- **`false`** — forbid extra keys, closing the object - (`additionalProperties: false`); -- a **value type** — a primitive / Go-builtin / `[]T`, or a **known type - name** (which resolves to a `$ref`, and is registered for discovery). - This reuses the [`swagger:type`](#swaggertype) value grammar, except a - type name becomes a `$ref` rather than an inline expansion. - -**Sample.** - -```go -// Settings is an open object: named properties plus typed extra values. -// -// swagger:model -// swagger:additionalProperties integer -type Settings struct { - Name string `json:"name"` -} -``` - -**Precedence — lowest priority.** `additionalProperties` only rides on an -`object`. If a prior rule fixed a non-object type (a `swagger:type` -scalar, `swagger:strfmt`, a special type), the marker is dropped with a -`CodeShapeMismatch` diagnostic. It composes with `maxProperties` / -`minProperties` / `patternProperties`. It has no OAS-2 SimpleSchema form, -so it never applies on a non-body parameter or response header. - -**Full example.** `fixtures/enhancements/additional-properties/api.go`. - ---- - -## `swagger:patternProperties` - -**What it does.** Adds **typed** `patternProperties` entries — each maps a -property-name regex to a value schema. It is the typed counterpart of the -regex-only [`patternProperties:` keyword]({{% relref "/maintainers/keywords#patternproperties" %}}) -(which uses an empty, any-value schema). - -{{% notice style="note" %}} -`patternProperties` is a JSON-Schema (draft-4) keyword, **beyond the -Swagger 2.0 subset**. codescan emits it ungated — your downstream tooling -must understand it. -{{% /notice %}} - -**Where it goes.** On a type declaration (alongside `swagger:model`). - -**Argument shape.** A comma-separated list of `"": ` pairs. -The regex is **double-quoted** (it may contain spaces, colons, commas; -only `\"` is an escape inside it — other backslashes like `\d` are -preserved). Each `` reuses the value grammar above (primitive / -`[]T` / type-name → `$ref`). - -**Sample.** - -```go -// Headers carries x-prefixed string values and numeric-keyed counters. -// -// swagger:model -// swagger:patternProperties "^x-": string, "^\d+$": integer -type Headers struct { - Known string `json:"known"` -} -``` - -**Precedence.** Same lowest-priority, object-only rule as -`swagger:additionalProperties`. Each regex is RE2-hygiene-checked: one -that does not compile raises a `CodeInvalidAnnotation` warning but is -**preserved**; a structurally malformed pair list is dropped with a -diagnostic. - -**Full example.** `fixtures/enhancements/pattern-properties-typed/api.go`. - ---- - -## `swagger:file` - -**What it does.** Marks a parameter or response body as a binary file -(`{type: file}`). The scanner emits the file-type marker without -further introspection of the Go type. - -**Where it goes.** On a struct field doc inside a -`swagger:parameters` (multipart file upload) or `swagger:response` -(file download) struct. - -**Argument shape.** No args. - -**Sample.** - -```go -// UploadParams declares a multipart file upload. -// -// swagger:parameters uploadFile -type UploadParams struct { - // File is the uploaded asset. - // - // in: formData - // swagger:file - File io.ReadCloser `json:"file"` -} -``` - -**Legal keywords.** Standard parameter / response keywords; the file -marker stacks with `in:` and other parameter shape keywords. - ---- - -## `swagger:default` - -**What it does.** Marks the surrounding declaration as the spec's -default value for the corresponding shape. Used in narrow contexts -where the scanner expects an explicit anchor for a default. - -This annotation is **value-only** — there's no exported entity it -publishes; it's a classifier hint the scanner consumes during -discovery. - -**Where it goes.** On a value declaration (`var`, `const`) or a -struct field. - -**Argument shape.** No args. - -**Sample.** - -```go -// DefaultLimit is the default page size used wherever Limit is not -// supplied by the caller. -// -// swagger:default -var DefaultLimit = 20 -``` - -This annotation has a narrow surface and is not commonly authored -directly. Most spec defaults are carried by the `default:` keyword on -the relevant field. - ---- - -## Annotation × keyword compatibility matrix - -A quick orientation for which annotations can carry which keyword -families. See [keywords.md]({{% relref "keywords" %}}) for the per-keyword -contracts. - -| Annotation | Numeric/length validations | Schema decorators | `in:` | Meta keywords | `Parameters:` body | `Responses:` body | YAML body | -|------------|----------------------------|-------------------|-------|---------------|--------------------|-------------------|-----------| -| `swagger:meta` | — | — | — | ✅ | — | — | ✅ (security defs, extensions) | -| `swagger:model` | ✅ (on fields) | ✅ | — | — | — | — | — | -| `swagger:strfmt` | — | — | — | — | — | — | — | -| `swagger:enum` | — | (enum keyword via const) | — | — | — | — | — | -| `swagger:allOf` | ✅ (on member fields) | ✅ | — | — | — | — | — | -| `swagger:alias` | — | — | — | — | — | — | — | -| `swagger:route` | — | (deprecated only) | — | (schemes/consumes/produces/security) | ✅ | ✅ | (extensions) | -| `swagger:operation` | — | — | — | — | — | — | ✅ (full op as YAML) | -| `swagger:parameters` | ✅ (on fields) | ✅ (on fields) | ✅ | — | — | — | — | -| `swagger:response` | ✅ (on header fields) | ✅ (on body field) | ✅ (body/header) | — | — | — | — | -| `swagger:ignore` | — | — | — | — | — | — | — | -| `swagger:name` | — | — | — | — | — | — | — | -| `swagger:type` | — | — | — | — | — | — | — | -| `swagger:additionalProperties` | — | ✅ (object schema) | — | — | — | — | — | -| `swagger:patternProperties` | — | ✅ (object schema) | — | — | — | — | — | -| `swagger:file` | — | — | — | — | — | — | — | -| `swagger:default` | — | — | — | — | — | — | — | - -A blank cell means the keyword family is not legal in that context; -attempting to use it emits `CodeContextInvalid` and the keyword is -dropped. diff --git a/docs/doc-site/maintainers/annotations/_index.md b/docs/doc-site/maintainers/annotations/_index.md new file mode 100644 index 0000000..78a1c8c --- /dev/null +++ b/docs/doc-site/maintainers/annotations/_index.md @@ -0,0 +1,146 @@ +--- +title: "Annotations" +weight: 10 +description: "The swagger:* annotation vocabulary: what each produces, where it attaches, and the keywords it admits." +--- + + +Annotations are the `swagger:` markers the scanner recognises in +Go doc comments. Each annotation classifies the surrounding +declaration — telling the scanner "this is a model definition", "this +is a route handler", "this is meta-information about the API" — and +opens the door for [keywords]({{% relref "keywords" %}}) inside the same comment +block. + +There are seventeen annotations. They divide cleanly by what they +attach to: + +- **Spec-level**: `swagger:meta`. +- **Model declarations**: `swagger:model`, `swagger:strfmt`, + `swagger:enum`, `swagger:allOf`, `swagger:alias`, + `swagger:additionalProperties`, `swagger:patternProperties`. +- **Operation declarations**: `swagger:route`, `swagger:operation`. +- **Companion declarations**: `swagger:parameters`, `swagger:response`. +- **Local hints**: `swagger:ignore`, `swagger:name`, `swagger:type`, + `swagger:file`, `swagger:default`. + +This section is the **author-first reference**. Each annotation has its +own page covering what it produces, where it goes, its EBNF-like +syntax, the keywords legal inside its block, and at least one worked +example. Browse them below (sorted alphabetically), or start from the +[Annotation index]({{% relref "/annotation-index" %}}) for the one-row-each +overview. + +{{< children type="card" description="true" >}} + +For the per-keyword reference, see [keywords.md]({{% relref "keywords" %}}). +For the embedded sub-languages (`Parameters:` and `Responses:` body +grammars, YAML extensions, etc.), see +[sub-languages.md]({{% relref "sub-languages" %}}). For the formal grammar, +see [grammar.md]({{% relref "grammar" %}}). + +--- + +## How annotations attach + +An annotation is recognised when it appears at the start of a comment +line in a doc comment. Leading whitespace, the `//` marker, and any +`/* */` block-comment continuation noise are stripped — the lexer +applies the same content-prefix-trim that every other godoc-aware +tool does. + +Annotations attach to whichever Go declaration owns the comment +group: + +- **Package doc** (`// Package foo …` followed by `package foo`) — + carries `swagger:meta`. +- **Type declaration** (`type T struct { … }`, `type T int`, + `type T = Other`) — carries `swagger:model`, `swagger:strfmt`, + `swagger:enum`, `swagger:allOf`, `swagger:alias`, `swagger:ignore`, + `swagger:type`. Inside a grouped declaration (`type ( A …; B … )`) + the comment on each individual spec is honoured independently — the + annotation attaches to its own `TypeSpec`, not to the enclosing group — + so two types in one group can carry distinct docs and annotations. +- **Function or variable declaration** (`func ServeAPI() { … }`, + `var DoIt = func() { … }`) — carries `swagger:route`, + `swagger:operation`. These two are recognised whether the annotation + sits in the function's doc comment or **inside the function body**. + A `swagger:model` or `swagger:parameters` declared on a type **local + to a function body** is likewise discovered. +- **Struct field doc** — carries `swagger:name`, `swagger:type`, + `swagger:ignore`, plus any of the [keyword reference]({{% relref "keywords" %}}) + entries legal in `schema` / `param` / `header` context. + +One comment group may carry MORE than one annotation when the +combinations are semantically compatible — e.g. `swagger:model` + +`swagger:type` together overrides the auto-detected Go type while +still publishing the model. The grammar parses both and the builder +honours both. + +The **first** annotation in source order wins as the "primary" +classifier — for example, a comment carrying `swagger:model` followed +by `swagger:ignore` produces a model (the ignore is silently +overridden because only the source-order-first annotation drives the +short-circuit). Subsequent annotations are still parsed and visible +via `Block.AnnotationKind()`-iteration, but the primary classifier +determines which builder owns the decl. + +{{% notice style="warning" %}} +Recognition is purely positional: **any** comment line that begins with a +`swagger:` token is treated as that annotation — even when you meant it as +prose. A description line like `swagger:type controls the emitted type` on a +type's doc comment is parsed as a `swagger:type` annotation. Keep annotation +names mid-sentence in descriptions (`The swagger:type directive …`) or wrap them +in backticks so the line does not *start* with the token. +{{% /notice %}} + +## Annotation argument shapes + +After the `swagger:` head, an annotation may carry positional +arguments. The shapes: + +- **No args**: `swagger:meta`, `swagger:ignore`, `swagger:enum`, + `swagger:allOf`, `swagger:file`, `swagger:default` — bare + annotation, the surrounding decl supplies the entity name. +- **One IDENT arg**: `swagger:model Pet`, `swagger:response + errorResponse`, `swagger:strfmt uuid`, `swagger:name fullName`, + `swagger:type integer`, `swagger:alias TimestampAlias` — the + argument overrides or names the entity. +- **One IDENT arg, optional**: `swagger:model` (bare — derives the + name from the Go decl) vs `swagger:model Pet` (overrides). +- **List of IDENT args**: `swagger:parameters listItems createItem` + — declares the parameters group as legal for multiple operations. +- **Header line**: `swagger:route GET /pets pets users listPets` and + `swagger:operation GET /pets users listPets` — a structured header + carrying method, path, tags, and operation ID. See the + per-annotation pages for the exact rules. + +## Annotation × keyword compatibility matrix + +A quick orientation for which annotations can carry which keyword +families. See [keywords.md]({{% relref "keywords" %}}) for the per-keyword +contracts, and each annotation's own page for the detail. + +| Annotation | Numeric/length validations | Schema decorators | `in:` | Meta keywords | `Parameters:` body | `Responses:` body | YAML body | +|------------|----------------------------|-------------------|-------|---------------|--------------------|-------------------|-----------| +| `swagger:meta` | — | — | — | ✅ | — | — | ✅ (security defs, extensions) | +| `swagger:model` | ✅ (on fields) | ✅ | — | — | — | — | — | +| `swagger:strfmt` | — | — | — | — | — | — | — | +| `swagger:enum` | — | (enum keyword via const) | — | — | — | — | — | +| `swagger:allOf` | ✅ (on member fields) | ✅ | — | — | — | — | — | +| `swagger:alias` | — | — | — | — | — | — | — | +| `swagger:route` | — | (deprecated only) | — | (schemes/consumes/produces/security) | ✅ | ✅ | (extensions) | +| `swagger:operation` | — | — | — | — | — | — | ✅ (full op as YAML) | +| `swagger:parameters` | ✅ (on fields) | ✅ (on fields) | ✅ | — | — | — | — | +| `swagger:response` | ✅ (on header fields) | ✅ (on body field) | ✅ (body/header) | — | — | — | — | +| `swagger:ignore` | — | — | — | — | — | — | — | +| `swagger:name` | — | — | — | — | — | — | — | +| `swagger:type` | — | — | — | — | — | — | — | +| `swagger:additionalProperties` | — | ✅ (object schema) | — | — | — | — | — | +| `swagger:patternProperties` | — | ✅ (object schema) | — | — | — | — | — | +| `swagger:file` | — | — | — | — | — | — | — | +| `swagger:default` | — | — | — | — | — | — | — | + +A blank cell means the keyword family is not legal in that context; +attempting to use it emits `CodeContextInvalid` and the keyword is +dropped. diff --git a/docs/doc-site/maintainers/annotations/swagger-additionalproperties.md b/docs/doc-site/maintainers/annotations/swagger-additionalproperties.md new file mode 100644 index 0000000..d40ddf8 --- /dev/null +++ b/docs/doc-site/maintainers/annotations/swagger-additionalproperties.md @@ -0,0 +1,63 @@ +--- +title: "swagger:additionalProperties" +weight: 10 +description: "Sets a schema's additionalProperties policy for keys beyond the named properties." +--- + + +## What it does + +Sets a schema's `additionalProperties` — the policy for keys beyond the +named properties. On a struct it **complements** the named properties; on +a map type it **overrides** the element-derived value schema; on a type +that resolved to a bare `$ref` it **defines** a clean object. See the +[Maps & free-form objects]({{% relref "/tutorials/maps-and-free-form-objects" %}}) +tutorial. + +## Where it goes + +On a type declaration (alongside `swagger:model`). A field-level +equivalent exists as the +[`additionalProperties:` keyword]({{% relref "/maintainers/keywords#additionalproperties" %}}). + +## Syntax + +```ebnf +AdditionalPropertiesAnnotation = ANN_ADDITIONAL_PROPERTIES , ( BOOL_VALUE | ValueType ) ; +ValueType = TYPE_REF | IDENT_NAME | "[]" , ValueType ; +``` + +The required token is one of: + +- **`true`** — allow arbitrary extra keys (`additionalProperties: true`); +- **`false`** — forbid extra keys, closing the object + (`additionalProperties: false`); +- a **value type** — a primitive / Go-builtin / `[]T`, or a **known type + name** (which resolves to a `$ref`, and is registered for discovery). + This reuses the {{% relref "swagger-type" %}} value grammar, except a + type name becomes a `$ref` rather than an inline expansion. + +## Supported keywords + +None of its own. It composes with `maxProperties` / `minProperties` / +[`patternProperties`]({{% relref "swagger-patternproperties" %}}). + +## Example + +```go +// Settings is an open object: named properties plus typed extra values. +// +// swagger:model +// swagger:additionalProperties integer +type Settings struct { + Name string `json:"name"` +} +``` + +**Precedence — lowest priority.** `additionalProperties` only rides on an +`object`. If a prior rule fixed a non-object type (a `swagger:type` +scalar, `swagger:strfmt`, a special type), the marker is dropped with a +`CodeShapeMismatch` diagnostic. It has no OAS-2 SimpleSchema form, so it +never applies on a non-body parameter or response header. + +**Full example.** `fixtures/enhancements/additional-properties/api.go`. diff --git a/docs/doc-site/maintainers/annotations/swagger-alias.md b/docs/doc-site/maintainers/annotations/swagger-alias.md new file mode 100644 index 0000000..551a079 --- /dev/null +++ b/docs/doc-site/maintainers/annotations/swagger-alias.md @@ -0,0 +1,46 @@ +--- +title: "swagger:alias" +weight: 20 +description: "Deprecated no-op — alias rendering is controlled by Go aliases + options." +--- + + +{{% notice style="warning" %}} +**Deprecated.** `swagger:alias` no longer affects the emitted spec. It is an +empty sink that only raises a `validate.deprecated` diagnostic. +{{% /notice %}} + +## What it does + +Nothing, today. Earlier documentation claimed it published a `$ref` to +the alias target; that was never accurate. Its only real effect was to +force a named **primitive** type to inline its scalar (e.g. +`{type: string}`) instead of producing the `$ref` a named type +otherwise gets — and that force-inline behaviour has been removed. + +## Where it went + +On a type alias / named-type declaration. + +## Syntax + +```ebnf +AliasBlock = ANN_ALIAS , [ IDENT_NAME ] , [ Title ] , [ Description ] ; +``` + +The optional `IDENT_NAME` is ignored — the annotation has no effect. + +## Migration + +- To **inline** a type at a use site, use `swagger:type inline` on the + field (see [`swagger:type`]({{% relref "swagger-type" %}})). +- To publish a type as a **first-class definition** that fields `$ref`, + use [`swagger:model`]({{% relref "swagger-model" %}}). +- To control alias rendering **globally**, use the `RefAliases` / + `TransparentAliases` options. A plain (unannotated) Go alias + `type T = Other` dissolves to its target by default. See + [Alias rendering]({{% relref "/shaping-the-output/names-and-refs/alias-rendering" %}}). + +## Supported keywords + +None — the annotation is inert. diff --git a/docs/doc-site/maintainers/annotations/swagger-allof.md b/docs/doc-site/maintainers/annotations/swagger-allof.md new file mode 100644 index 0000000..cd032e6 --- /dev/null +++ b/docs/doc-site/maintainers/annotations/swagger-allof.md @@ -0,0 +1,77 @@ +--- +title: "swagger:allOf" +weight: 30 +description: "Marks a struct as participating in an allOf composition." +--- + + +## What it does + +Marks a struct as participating in an `allOf` composition. The struct's +fields plus any embedded `swagger:model`-tagged base produce an +`allOf: [$ref base, {inline fields}]` schema. The companion convention is +to embed the base type as an anonymous field with this annotation on the +embedding's doc comment (or on the embedded type itself). + +## Where it goes + +On a struct field that embeds another type, or on a struct type that has +at least one embedded base. + +## Syntax + +```ebnf +AllOfBlock = ANN_ALLOF , [ Title ] , [ Description ] ; +``` + +The annotation takes no arguments; an optional title/description may +follow on the doc comment. + +## Supported keywords + +[Schema-context keywords]({{% relref "keywords#schema-decorators" %}}) on +the inline-object member (the second `allOf` element). + +## Example + +```go +// Animal is the abstract base. +// +// swagger:model +type Animal struct { + Kind string `json:"kind"` +} + +// Dog is an Animal with a breed. +// +// swagger:model +type Dog struct { + // swagger:allOf + Animal + + Breed string `json:"breed"` +} +``` + +Produces: + +```json +"Dog": { + "allOf": [ + {"$ref": "#/definitions/Animal"}, + { + "type": "object", + "properties": { + "breed": {"type": "string", "x-go-name": "Breed"} + } + } + ] +} +``` + +The same composition applies when the embedding struct is a +`swagger:response` body: the embedded base emits an `allOf: [{$ref}, …]` +arm only when it is a `swagger:model` (a definition exists to point at); +an embedded `swagger:response` has its fields inlined instead. + +**Full example.** `fixtures/enhancements/allof-edges/types.go`. diff --git a/docs/doc-site/maintainers/annotations/swagger-default.md b/docs/doc-site/maintainers/annotations/swagger-default.md new file mode 100644 index 0000000..9947872 --- /dev/null +++ b/docs/doc-site/maintainers/annotations/swagger-default.md @@ -0,0 +1,46 @@ +--- +title: "swagger:default" +weight: 40 +description: "Classifier hint marking a value declaration as a spec default anchor." +--- + + +## What it does + +Marks the surrounding declaration as the spec's default value for the +corresponding shape. Used in narrow contexts where the scanner expects +an explicit anchor for a default. + +This annotation is **value-only** — there's no exported entity it +publishes; it's a classifier hint the scanner consumes during +discovery. + +## Where it goes + +On a value declaration (`var`, `const`) or a struct field. + +## Syntax + +```ebnf +DefaultClassifierBlock = ANN_DEFAULT , [ Title ] , [ Description ] ; +``` + +Takes no argument — an optional title/description may follow on the +doc comment. + +## Supported keywords + +None of its own. Most spec defaults are instead carried by the +[`default:` keyword]({{% relref "keywords#default" %}}) on the relevant +field; this annotation has a narrow surface and is not commonly authored +directly. + +## Example + +```go +// DefaultLimit is the default page size used wherever Limit is not +// supplied by the caller. +// +// swagger:default +var DefaultLimit = 20 +``` diff --git a/docs/doc-site/maintainers/annotations/swagger-enum.md b/docs/doc-site/maintainers/annotations/swagger-enum.md new file mode 100644 index 0000000..29187bb --- /dev/null +++ b/docs/doc-site/maintainers/annotations/swagger-enum.md @@ -0,0 +1,103 @@ +--- +title: "swagger:enum" +weight: 50 +description: "Marks a named type as an enum and collects its const values." +--- + + +## What it does + +Marks a string-typed (or integer-typed) named type as an enum and +collects the type's `const` declarations. + +- **Without `swagger:model`** (the default): the values are applied + **inline on each model field that references the type** — the property + gets an `enum` array plus an `x-go-enum-desc` extension carrying the + per-value godoc descriptions in ` ` shape. The enum + type itself is not a standalone definition. +- **With `swagger:model`**: the enum becomes a **first-class definition** + carrying the `enum` array (+ `x-go-enum-desc`), and referencing fields + point at it via `$ref` — the general `swagger:model ⇒ definition + $ref` + rule applied to enums. + +If `swagger:enum` names a type for which no matching `const` values are +found, the enum semantics are dropped and the type falls through to +ordinary type resolution (typically a plain `$ref`, no `enum` array). + +## Where it goes + +On a named type declaration. The type's `const` values are discovered via +Go's type-system traversal; they do not need to live in the same file. +The values surface only when a model reaches the enum type through a field. + +## Syntax + +```ebnf +EnumBlock = ANN_ENUM , [ IDENT_NAME ] , [ Title ] , [ Description ] ; +``` + +The optional `IDENT_NAME` names the type whose `const` values to collect. +On a type declaration the name is redundant, so the bare `swagger:enum` +form is accepted and infers the name from the declared type: +`swagger:enum Priority` and a bare `swagger:enum` on `type Priority …` +are equivalent. + +## Supported keywords + +[Schema-context keywords]({{% relref "keywords#schema-decorators" %}}). The +`enum:` keyword can ALSO be used inline on the type doc to force a value +set; when present, it overrides the const-derived values and the +`x-go-enum-desc` is recomputed (or dropped) accordingly. + +## Example + +```go +// Priority is the urgency level on a task. +// +// swagger:enum Priority +type Priority string + +const ( + // PriorityLow is for tasks that can wait. + PriorityLow Priority = "low" + + // PriorityMedium is the default. + PriorityMedium Priority = "medium" + + // PriorityHigh is for tasks that must run soon. + PriorityHigh Priority = "high" +) + +// Task references Priority, which is what makes the enum reachable. +// +// swagger:model +type Task struct { + Priority Priority `json:"priority"` +} +``` + +Produces (extract) — the values land on `Task`'s `priority` property, not +on a `Priority` definition: + +```json +{ + "Task": { + "type": "object", + "properties": { + "priority": { + "type": "string", + "enum": ["low", "medium", "high"], + "x-go-enum-desc": "low PriorityLow is for tasks that can wait.\nmedium PriorityMedium is the default.\nhigh PriorityHigh is for tasks that must run soon." + } + } + } +} +``` + +By default the const→value mapping is folded into the property's +`description` **and** duplicated in `x-go-enum-desc`. Set the scanner +option `SkipEnumDescriptions: true` to keep the authored prose as the +description; the mapping then rides `x-go-enum-desc` only. See +[Vendor extensions]({{% relref "vendor-extensions" %}}). + +**Full example.** `fixtures/enhancements/enum-overrides/types.go`. diff --git a/docs/doc-site/maintainers/annotations/swagger-file.md b/docs/doc-site/maintainers/annotations/swagger-file.md new file mode 100644 index 0000000..cf65bf5 --- /dev/null +++ b/docs/doc-site/maintainers/annotations/swagger-file.md @@ -0,0 +1,47 @@ +--- +title: "swagger:file" +weight: 60 +description: "Marks a parameter or response body as a binary file (`{type: file}`)." +--- + + +## What it does + +Marks a parameter or response body as a binary file +(`{type: file}`). The scanner emits the file-type marker without +further introspection of the Go type. + +## Where it goes + +On a struct field doc inside a `swagger:parameters` (multipart file +upload) or `swagger:response` (file download) struct. + +## Syntax + +```ebnf +FileBlock = ANN_FILE , [ Title ] , [ Description ] ; +``` + +Takes no argument — an optional title/description may follow on the +doc comment. + +## Supported keywords + +Standard parameter / response keywords; the file marker stacks with +`in:` and other parameter shape keywords. See the +[keywords reference]({{% relref "keywords" %}}). + +## Example + +```go +// UploadParams declares a multipart file upload. +// +// swagger:parameters uploadFile +type UploadParams struct { + // File is the uploaded asset. + // + // in: formData + // swagger:file + File io.ReadCloser `json:"file"` +} +``` diff --git a/docs/doc-site/maintainers/annotations/swagger-ignore.md b/docs/doc-site/maintainers/annotations/swagger-ignore.md new file mode 100644 index 0000000..d48ead2 --- /dev/null +++ b/docs/doc-site/maintainers/annotations/swagger-ignore.md @@ -0,0 +1,60 @@ +--- +title: "swagger:ignore" +weight: 70 +description: "Excludes the surrounding declaration (or one field) from the generated spec." +--- + + +## What it does + +Excludes the surrounding declaration from the generated spec. The +scanner sees the decl and the doc, classifies it, then drops it. + +When `swagger:ignore` appears **after** another classifier on the same +comment block (e.g. `swagger:model` first, then `swagger:ignore`), the +first annotation wins and the ignore is silently overridden. Place +`swagger:ignore` first if you genuinely want the decl excluded. + +## Where it goes + +On a type declaration to exclude the whole type, or on a struct field +doc to exclude that one field. + +## Syntax + +```ebnf +IgnoreBlock = ANN_IGNORE , [ Title ] , [ Description ] ; +``` + +Takes no argument — an optional title/description may follow on the +doc comment. + +## Supported keywords + +None — the annotation is a stateless classifier marker. + +## Example + +```go +// Internal is not exposed. +// +// swagger:ignore +type Internal struct { + SecretField string +} +``` + +On a single field: + +```go +type User struct { + Name string `json:"name"` + + // PasswordHash is internal. + // + // swagger:ignore + PasswordHash string `json:"-"` +} +``` + +**Full example.** `fixtures/enhancements/top-level-kinds/types.go`. diff --git a/docs/doc-site/maintainers/annotations/swagger-meta.md b/docs/doc-site/maintainers/annotations/swagger-meta.md new file mode 100644 index 0000000..9b7ec69 --- /dev/null +++ b/docs/doc-site/maintainers/annotations/swagger-meta.md @@ -0,0 +1,65 @@ +--- +title: "swagger:meta" +weight: 80 +description: "Declares the package as the top-level OpenAPI spec container." +--- + + +## What it does + +Declares the package as the OpenAPI spec container. The scanner reads the +package doc comment for the top-level spec fields: title (via +[stripPackagePrefix]({{% relref "grammar#prose" %}}) of the doc's first line), +description, license, contact, host, basePath, version, schemes, consumes, +produces, securityDefinitions, extensions, and the rest of the meta keyword +surface. + +## Where it goes + +On the package doc comment. No arguments — a bare annotation. + +## Syntax + +```ebnf +MetaBlock = ANN_META , [ Title ] , [ Description ] , MetaBody ; +``` + +The body is a `MetaBody` of single-line `MetaKeyword`s +(`version`, `host`, `basePath`, `license`, `contact`, `schemes`) and +`MetaRawBlock`s (`consumes`, `produces`, `security`, +`securityDefinitions`, `tos`). See [grammar §meta-family]({{% relref "grammar#meta-family" %}}). + +## Supported keywords + +All [meta single-line keywords]({{% relref "keywords#meta-single-line-keywords" %}}) +(`schemes`, `version`, `host`, `basePath`, `license`, `contact`) plus the +meta-scope [body keywords]({{% relref "keywords#body-keywords" %}}) +(`consumes`, `produces`, `security`, `securityDefinitions`, `extensions`, +`infoExtensions`, `tos`, `externalDocs`, `tags`). A `Tags:` block declares the +spec's top-level `tags` (name, description, nested `externalDocs`, `x-*` +extensions per tag). + +## Example + +```go +// Package petstore Petstore API. +// +// The purpose of this application is to provide an application +// that is using plain Go code to define an API. +// +// Schemes: http, https +// Host: petstore.swagger.io +// BasePath: /v2 +// Version: 1.0.0 +// +// Consumes: +// - application/json +// +// Produces: +// - application/json +// +// swagger:meta +package petstore +``` + +**Full example.** `fixtures/goparsing/spec/api.go`. diff --git a/docs/doc-site/maintainers/annotations/swagger-model.md b/docs/doc-site/maintainers/annotations/swagger-model.md new file mode 100644 index 0000000..615e1f6 --- /dev/null +++ b/docs/doc-site/maintainers/annotations/swagger-model.md @@ -0,0 +1,77 @@ +--- +title: "swagger:model" +weight: 90 +description: "Publishes a Go type as a definitions entry." +--- + + +## What it does + +Declares a Go type as a published model. The scanner walks the type, +emits a schema into the spec's `definitions` map, and resolves +cross-references between models. + +The title/description split follows a heuristic: a single-line comment +**ending in a period** becomes the `title`; one **without** a trailing +period becomes the `description`; a **multi-line** comment uses the +first line as `title` and the rest as `description`. The descriptive +prose must come **before** the `swagger:model` line — an +annotation-first block still publishes the model but drops its title +and description. + +## Where it goes + +On a type declaration (`type T struct { … }`, `type T int`, +`type T = Other`, …). + +## Syntax + +```ebnf +ModelAnnotation = ANN_MODEL , [ IDENT_NAME ] ; +``` + +The optional `IDENT_NAME` is the name the model takes in `definitions` +(default: the Go type's name). It must be a plain identifier (a JSON +label), not a Go-qualified name — a dotted name such as `utils.Error` +is rejected with a warning and dropped. Cross-package types resolve +automatically, so reference a model by its bare name. + +The annotation opens a [`SchemaBlock`]({{% relref "grammar#schema-family" %}}) +body — its fields and their doc comments carry the schema validations. + +## Supported keywords + +All [schema decorators]({{% relref "keywords#schema-decorators" %}}) plus the +[length / array / numeric validations]({{% relref "keywords#numeric-validations" %}}) +on field doc comments. + +## Example + +```go +// Pet is the petstore's primary entity. +// +// swagger:model +type Pet struct { + // ID is the unique identifier. + ID int64 `json:"id"` + + // Name is the pet's display name. + Name string `json:"name"` + + // Tags categorise the pet. + Tags []string `json:"tags,omitempty"` +} +``` + +With a name override, the type is published as `#/definitions/PetWithExtras`: + +```go +// swagger:model PetWithExtras +type DetailedPet struct { … } +``` + +A field group declaring several names (`R, G, B, A uint8`) emits **one +property per name**; a `json:` tag on the group cannot rename the +individual fields, though tag options still apply. + +**Full example.** `fixtures/enhancements/named-struct-tags-ref/types.go`. diff --git a/docs/doc-site/maintainers/annotations/swagger-name.md b/docs/doc-site/maintainers/annotations/swagger-name.md new file mode 100644 index 0000000..ea608d0 --- /dev/null +++ b/docs/doc-site/maintainers/annotations/swagger-name.md @@ -0,0 +1,66 @@ +--- +title: "swagger:name" +weight: 100 +description: "Overrides the emitted property name of a struct field or interface method." +--- + + +## What it does + +Overrides the JSON property name that a struct field or interface +method renders as. By default the scanner derives names from +`json:"…"` struct tags (or the Go identifier for fields / methods with +no tag); `swagger:name` overrides that derivation when the tag-based +shape isn't appropriate — typically on **interface methods**, which +cannot carry struct tags. + +## Where it goes + +On a struct field doc OR an interface method doc. + +## Syntax + +```ebnf +NameAnnotation = ANN_NAME , IDENT_NAME ; +``` + +The required `IDENT_NAME` is the JSON property name to use. + +## Supported keywords + +None — the override name is the entire surface. + +## Example + +```go +// UserProfile is the user's profile interface. +// +// swagger:model +type UserProfile interface { + // ID is the user identifier. + ID() string + + // FullName is the user's display name. + // + // swagger:name fullName + FullName() string +} +``` + +Without `swagger:name`, the method `FullName()` would publish as +property `FullName` (PascalCase). The annotation renames it to +`fullName`. + +**Full example.** `fixtures/enhancements/interface-methods/types.go`. + +## Deprecated / legacy forms + +`swagger:name` is the **legacy** annotation form. The canonical, +universal field-naming mechanism is the +[`name:` keyword]({{% relref "/maintainers/keywords#name" %}}), which +works at *every* field site — model properties, interface methods, +parameters, and response headers — with the precedence `name:` > +`swagger:name` > `json:` tag > Go field name. `swagger:name` remains +honoured (and idiomatic on interface methods, shown above), but reach +for `name:` in new code; it is the only form that works on parameters +and headers. diff --git a/docs/doc-site/maintainers/annotations/swagger-operation.md b/docs/doc-site/maintainers/annotations/swagger-operation.md new file mode 100644 index 0000000..6c47438 --- /dev/null +++ b/docs/doc-site/maintainers/annotations/swagger-operation.md @@ -0,0 +1,91 @@ +--- +title: "swagger:operation" +weight: 110 +description: "Declares an HTTP route + operation with a YAML-document body." +--- + + +## What it does + +Declares an HTTP route + operation. Same header line as +{{% relref "swagger-route" %}} (method, path, optional tags, operation ID), +but with a different body shape: instead of the structured `Parameters:` / +`Responses:` keyword surface, `swagger:operation`'s body is a single YAML +document spelling out the OpenAPI operation object directly. + +Use `swagger:operation` when you want to author the operation in YAML (closer +to the OpenAPI spec text) or when the operation has shapes the keyword surface +doesn't cover. + +## Where it goes + +On a function or variable declaration whose doc comment carries the +annotation. The Go entity itself doesn't have to be a handler — the annotation +publishes a path/operation independent of the carrier. + +## Syntax + +```ebnf +InlineOperationBlock = ANN_OPERATION , OperationArgs + , [ Title ] , [ Description ] , InlineOperationBody ; + +OperationArgs = HTTP_METHOD , URL_PATH , { IDENT_NAME } , IDENT_NAME ; +``` + +`InlineOperationBody` is an `OPAQUE_YAML` document. The trailing `IDENT_NAME` +is the operation ID; the run before it is the tag list. The header line shape +authors rely on: + +``` +swagger:operation [tag1 tag2 …] +``` + +- `` — `GET`, `POST`, `PUT`, `DELETE`, `PATCH`, `HEAD`, `OPTIONS`. + Case insensitive. +- `` — starts with `/`; supports path-parameter braces (`/items/{id}`). + Only RFC 6570 Level-1 expansion (simple `{name}` substitution) is allowed; + an inline regex constraint (`/items/{id:[0-9]+}`) is stripped to the bare + form with a warning. +- `[tag1 tag2 …]` — optional whitespace-separated tag list (at least two + characters each). +- `` — the unique operation identifier. + +## Supported keywords + +None inside the YAML body — it is structurally YAML, not the keyword grammar. +The header line is the entire annotation surface. + +## Example + +```go +// swagger:operation GET /items/{id} items getItem +// +// --- +// summary: Get item by ID +// parameters: +// - name: id +// in: path +// required: true +// type: integer +// responses: +// '200': +// description: the requested item +// schema: +// $ref: '#/definitions/Item' +// default: +// $ref: '#/responses/genericError' +func GetItem() {} +``` + +The `---` delimits the YAML body; everything between the fences is parsed as an +OpenAPI 2.0 operation object. + +**Full example.** `fixtures/enhancements/parameters-map-postdecl/api.go`. + +## Deprecated / legacy forms + +`swagger:operation` also accepts a structured `Parameters:` body (shared with +{{% relref "swagger-route" %}}). In that body the per-parameter chunk sigil +`+ name:` is the historic chunk-start; `- name:` is accepted as a YAML-friendly +alias and is preferred for YAML-correctness. See the chunk grammar in +{{% relref "sub-languages#parameters" %}}. diff --git a/docs/doc-site/maintainers/annotations/swagger-parameters.md b/docs/doc-site/maintainers/annotations/swagger-parameters.md new file mode 100644 index 0000000..e1b37d0 --- /dev/null +++ b/docs/doc-site/maintainers/annotations/swagger-parameters.md @@ -0,0 +1,87 @@ +--- +title: "swagger:parameters" +weight: 120 +description: "Declares a Go struct as the parameter set for one or more operations." +--- + + +## What it does + +Declares a Go struct as the parameters set for one or more operations. +Each field becomes one parameter on the named operation(s), and the +field's doc comment carries its `in:`, `required:`, validations, and +description. + +- A parameter's **name** comes from the field's `json:` tag, falling + back to the Go field name when there is no tag (the `form:` tag is not + consulted). A `name:` keyword in the field doc takes precedence over + both and is the canonical, preferred way to set the name — the legacy + `swagger:name` *annotation* is inert here and emits a `context-invalid` + diagnostic pointing at `name:`. See the + [universal `name` keyword]({{% relref "/maintainers/keywords#name" %}}). +- Operation IDs **accumulate**: the same ID may appear in several + `swagger:parameters` lines to compose a set from multiple structs, and + one struct may carry several lines splitting a long ID list. +- `swagger:parameters` declarations are collected across **all scanned + packages** and matched to operations by ID, so a shared set can live in + its own package. + +## Where it goes + +On a struct declaration. A bare slice variable (`var Filters []string`) +carries no per-field `in:`/`type:`/`required:`, so parameters must be a +struct. + +## Syntax + +```ebnf +ParametersAnnotation = ANN_PARAMETERS , IDENT_NAME , { IDENT_NAME } ; +``` + +The `IDENT_NAME` arguments are the operation IDs this set applies to (at +least one). The first argument may instead be a `*` wildcard (spec-level +shared `#/parameters/{name}`) or a `/path` (inlined into that exact +path-item) — see +[Sharing parameters & responses]({{% relref "/tutorials/sharing-parameters-and-responses" %}}). + +The annotation opens a [`SchemaBlock`]({{% relref "grammar#schema-family" %}}) +body — field doc comments carry the parameter validations. + +## Supported keywords + +[param-context keywords]({{% relref "keywords#parameter-location" %}}) on +fields: `in`, `required`, the numeric / length / format validations, +`default`, `example`, `enum`, `allowEmptyValue`, `collectionFormat`. + +## Example + +```go +// ListItemsParams declares pagination + filter parameters for the +// listItems operation. +// +// swagger:parameters listItems +type ListItemsParams struct { + // Offset is the page offset. + // + // in: query + // minimum: 0 + // default: 0 + Offset int `json:"offset"` + + // Limit is the page size. + // + // in: query + // minimum: 1 + // maximum: 100 + // default: 20 + Limit int `json:"limit"` + + // Tag is the filter tag. + // + // in: query + // required: false + Tag string `json:"tag,omitempty"` +} +``` + +**Full example.** `fixtures/enhancements/simple-schema-violation/api.go`. diff --git a/docs/doc-site/maintainers/annotations/swagger-patternproperties.md b/docs/doc-site/maintainers/annotations/swagger-patternproperties.md new file mode 100644 index 0000000..5a1ca7d --- /dev/null +++ b/docs/doc-site/maintainers/annotations/swagger-patternproperties.md @@ -0,0 +1,62 @@ +--- +title: "swagger:patternProperties" +weight: 130 +description: "Adds typed patternProperties entries mapping a name regex to a value schema." +--- + + +## What it does + +Adds **typed** `patternProperties` entries — each maps a property-name +regex to a value schema. It is the typed counterpart of the regex-only +[`patternProperties:` keyword]({{% relref "/maintainers/keywords#patternproperties" %}}) +(which uses an empty, any-value schema). + +{{% notice style="note" %}} +`patternProperties` is a JSON-Schema (draft-4) keyword, **beyond the +Swagger 2.0 subset**. codescan emits it ungated — your downstream tooling +must understand it. +{{% /notice %}} + +## Where it goes + +On a type declaration (alongside `swagger:model`). + +## Syntax + +```ebnf +PatternPropertiesAnnotation = ANN_PATTERN_PROPERTIES , PatternPair , { "," , PatternPair } ; +PatternPair = STRING_VALUE , ":" , ValueType ; +ValueType = TYPE_REF | IDENT_NAME | "[]" , ValueType ; +``` + +A comma-separated list of `"": ` pairs. The regex +(`STRING_VALUE`) is **double-quoted** — it may contain spaces, colons, +commas; only `\"` is an escape inside it, other backslashes like `\d` +are preserved. Each `` reuses the {{% relref "swagger-type" %}} +value grammar (primitive / `[]T` / type-name → `$ref`). + +## Supported keywords + +None of its own. It composes with `maxProperties` / `minProperties` / +[`additionalProperties`]({{% relref "swagger-additionalproperties" %}}). + +## Example + +```go +// Headers carries x-prefixed string values and numeric-keyed counters. +// +// swagger:model +// swagger:patternProperties "^x-": string, "^\d+$": integer +type Headers struct { + Known string `json:"known"` +} +``` + +**Precedence.** Same lowest-priority, object-only rule as +[`swagger:additionalProperties`]({{% relref "swagger-additionalproperties" %}}). +Each regex is RE2-hygiene-checked: one that does not compile raises a +`CodeInvalidAnnotation` warning but is **preserved**; a structurally +malformed pair list is dropped with a diagnostic. + +**Full example.** `fixtures/enhancements/pattern-properties-typed/api.go`. diff --git a/docs/doc-site/maintainers/annotations/swagger-response.md b/docs/doc-site/maintainers/annotations/swagger-response.md new file mode 100644 index 0000000..d55f1a1 --- /dev/null +++ b/docs/doc-site/maintainers/annotations/swagger-response.md @@ -0,0 +1,83 @@ +--- +title: "swagger:response" +weight: 140 +description: "Declares a Go struct as a named response object." +--- + + +## What it does + +Declares a Go struct as a named response object, emitted into the spec's +top-level `responses` map. Routes / operations reference it by name via +the response sub-language (`Responses:` body in `swagger:route`, or the +YAML `$ref` form in `swagger:operation`). + +The struct's fields contribute the response shape: + +- A field named `Body` (or carrying `in: body`) becomes the response + body schema. The body may be a struct, a `$ref`'d model, **or a + primitive** — `Body string` emits `schema: {type: string}`. +- Other fields default to response **headers**: a field with neither + `Body`/`in: body` nor `in: header` is treated as a header, not a body + property. A header's key comes from the `json:` tag / Go field name, or + a `name:` keyword (e.g. `name: X-Rate-Limit`) — the canonical, preferred + form, see the [`name` keyword]({{% relref "/maintainers/keywords#name" %}}). +- An **anonymously embedded** struct marked `in: body` *is* the body (a + `$ref` to the model), not a promotion of its fields. +- An `interface{}` / `any`-typed field emits an empty schema (`{}`, or + `{type: array, items: {}}` for a slice) — "any type", valid OpenAPI 2.0. + +## Where it goes + +On a struct declaration. + +## Syntax + +```ebnf +ResponseAnnotation = ANN_RESPONSE , [ IDENT_NAME ] ; +``` + +The optional `IDENT_NAME` is the published response name (default: the +Go type's name). A `*` wildcard (`swagger:response *`) explicitly marks +the response as a **shared** one, registered at `#/responses/{name}` for +operations to `$ref` by name — see +[Sharing parameters & responses]({{% relref "/tutorials/sharing-parameters-and-responses" %}}). + +The annotation opens a [`SchemaBlock`]({{% relref "grammar#schema-family" %}}) +body. + +## Supported keywords + +- **Body field:** schema-context keywords. +- **Header field:** header-context keywords — numeric / length / format + validations, `pattern`, `enum`, `default`, `example`, + `collectionFormat`. `required:` is silently dropped (the OAS v2 Header + object has no `required` field). + +## Example + +```go +// GenericError is the catch-all error response. +// +// swagger:response genericError +type GenericError struct { + // in: body + Body struct { + // Message is the human-readable error message. + Message string `json:"message"` + + // Code is the machine-readable error category. + Code string `json:"code,omitempty"` + } + + // X-Request-ID echoes the request correlation header. + // + // in: header + XRequestID string `json:"X-Request-ID"` +} +``` + +Routes can then reference it via `response:genericError` in their +`Responses:` body. + +**Full example.** `fixtures/enhancements/routes-full-petstore-shape/handlers.go`. diff --git a/docs/doc-site/maintainers/annotations/swagger-route.md b/docs/doc-site/maintainers/annotations/swagger-route.md new file mode 100644 index 0000000..0a270fe --- /dev/null +++ b/docs/doc-site/maintainers/annotations/swagger-route.md @@ -0,0 +1,105 @@ +--- +title: "swagger:route" +weight: 150 +description: "Declares an HTTP route + operation in one annotation." +--- + + +## What it does + +Declares an HTTP route + operation in one annotation. The header line carries +the method, path, optional tags, and the operation ID; the comment body +carries the operation's metadata (consumes / produces / schemes / security / +parameters / responses / extensions). + +This is the **terser of the two operation-declaration annotations**. Most +go-swagger projects use `swagger:route` for hand-written operations; see +{{% relref "swagger-operation" %}} for the YAML-body alternative. + +## Where it goes + +On a function or variable declaration whose doc comment carries the +annotation. The Go entity itself doesn't have to be a handler — the annotation +publishes a path/operation independent of the carrier. + +A godoc-style identifier may precede the annotation on the same comment line +(`// ListPets swagger:route GET /pets pets users listPets`); that leading +identifier is recognised as a godoc convention and is not part of the +annotation surface. + +## Syntax + +```ebnf +RouteBlock = ANN_ROUTE , OperationArgs + , [ Title ] , [ Description ] , RouteBody ; + +OperationArgs = HTTP_METHOD , URL_PATH , { IDENT_NAME } , IDENT_NAME ; +``` + +The trailing `IDENT_NAME` is the operation ID; the run before it is the tag +list. The header line shape authors rely on: + +``` +swagger:route [tag1 tag2 …] +``` + +- `` — `GET`, `POST`, `PUT`, `DELETE`, `PATCH`, `HEAD`, `OPTIONS`. + Case insensitive. +- `` — starts with `/`; supports path-parameter braces (`/items/{id}`). + Only RFC 6570 Level-1 expansion (simple `{name}` substitution) is allowed; + an inline regex constraint (`/items/{id:[0-9]+}`) is stripped to the bare + form with a warning. +- `[tag1 tag2 …]` — optional whitespace-separated tag list (at least two + characters each). +- `` — the unique operation identifier. + +## Supported keywords + +All [body keywords]({{% relref "keywords#body-keywords" %}}) legal in route +context (`consumes`, `produces`, `schemes`, `security`, `parameters`, +`responses`, `extensions`, `externalDocs`) plus inline `deprecated:` and a body +`tags:` list (a string list, unioned and deduplicated with the header-line +tags). The `Parameters:` and `Responses:` sub-languages are documented in +{{% relref "sub-languages#parameters" %}} and +{{% relref "sub-languages#responses" %}}. + +## Example + +```go +// ListPets swagger:route GET /pets pets users listPets +// +// List pets filtered by some parameters. +// +// Consumes: +// - application/json +// +// Produces: +// - application/json +// +// Schemes: http, https +// +// Security: +// api_key: +// oauth: read, write +// +// Parameters: +// + name: limit +// in: query +// type: integer +// minimum: 1 +// maximum: 100 +// +// Responses: +// 200: body:[]Pet the pet list +// default: response:genericError +func ListPets() {} +``` + +**Full example.** `fixtures/enhancements/routes-full-petstore-shape/handlers.go`. + +## Deprecated / legacy forms + +In the `Parameters:` body the per-parameter chunk sigil `+ name:` (used in the +sample above) is the historic chunk-start; `- name:` is accepted as a +YAML-friendly alias and is preferred for YAML-correctness. See the chunk +grammar in {{% relref "sub-languages#parameters" %}}. diff --git a/docs/doc-site/maintainers/annotations/swagger-strfmt.md b/docs/doc-site/maintainers/annotations/swagger-strfmt.md new file mode 100644 index 0000000..2830818 --- /dev/null +++ b/docs/doc-site/maintainers/annotations/swagger-strfmt.md @@ -0,0 +1,67 @@ +--- +title: "swagger:strfmt" +weight: 160 +description: "Marks a named type as a custom string format." +--- + + +## What it does + +Marks a named type as a custom string format. Wherever the type appears +as a field, the emitted schema is `{type: string, format: }`. Useful +for `UUID`, `Email`, `URL`-style types that have a Go type but should +serialise as a JSON string with a known format. + +A field typed by the marked type emits with the format; the underlying +type does NOT appear as a top-level model definition (strfmt-tagged types +are replaced by their format at every reference). A slice carries the +format onto its items: `{type: array, items: {type: string, format: …}}`. + +## Where it goes + +On a type declaration whose underlying form is a string-marshalable type +(typically implementing `encoding.TextMarshaler` / `encoding.TextUnmarshaler`). +`swagger:strfmt` may also sit on a struct **field** doc to override just +that field's published format. + +## Syntax + +```ebnf +StrfmtBlock = ANN_STRFMT , IDENT_NAME , [ Title ] , [ Description ] ; +``` + +The required `IDENT_NAME` is the format name (`uuid`, `email`, `mac`, …) — +the entire surface of the annotation. + +## Supported keywords + +None at the type level beyond `swagger:strfmt` itself; the format name is +the entire surface. + +## Example + +```go +// MAC is a hardware address rendered as a colon-separated hex string. +// +// swagger:strfmt mac +type MAC string + +func (m MAC) MarshalText() ([]byte, error) { return []byte(m), nil } +func (m *MAC) UnmarshalText(b []byte) error { *m = MAC(b); return nil } +``` + +A field typed `MAC` emits as `{type: string, format: mac}`. + +Adding `swagger:model` opts the type into a **first-class definition** +carrying the full `{type: string, format: …}` schema, with referencing +fields pointing at it via `$ref` — the general +`swagger:model ⇒ definition + $ref` rule. Without `swagger:model`, the +format inlines at every reference. + +A field-level override targets one field's format — e.g. +`// swagger:strfmt int64` on a `uint64` field emits +`{type: string, format: int64}`, a precision-safe, JSON-conformant string +encoding (the conformant alternative to the Go-specific `{integer, format: +uint64}` codescan emits for unsized/large ints by default). + +**Full example.** `fixtures/enhancements/text-marshal/types.go`. diff --git a/docs/doc-site/maintainers/annotations/swagger-type.md b/docs/doc-site/maintainers/annotations/swagger-type.md new file mode 100644 index 0000000..20b656d --- /dev/null +++ b/docs/doc-site/maintainers/annotations/swagger-type.md @@ -0,0 +1,99 @@ +--- +title: "swagger:type" +weight: 170 +description: "Replaces a field's or named type's inferred Swagger type with an inlined type." +--- + + +## What it does + +Replaces a field's (or named type's) inferred Swagger type with an +**inlined** type. `swagger:type` is an inline directive — it never emits a +`$ref`; the chosen type is rendered directly in place (the default +`$ref`-for-named-types is the *no-annotation* behaviour). + +## Where it goes + +On a type declaration, a struct field doc, OR a `swagger:parameters` field +doc. + +{{% notice style="note" %}} +**On a parameter field** the override collapses the field to a simple +parameter — useful when a struct- or defined-typed field would otherwise +come out typeless (invalid Swagger 2.0). The argument is restricted to a +**scalar** or a **`[]`-wrapped scalar** there: the `inline` and type-name +forms are rejected with a diagnostic, since a non-body parameter has no +schema to inline a type into. A compatible `swagger:strfmt` on the same +field still rides as a supplementary format. +{{% /notice %}} + +## Syntax + +```ebnf +TypeBlock = ANN_TYPE , TYPE_REF , [ Title ] , [ Description ] ; +``` + +The required `TYPE_REF` is one of: + +- a **scalar type** — `string`, `integer`, `number`, `boolean`, `object` + (or a Go-builtin spelling such as `int64`, `uint32`); +- **`[]T`** — an array whose items are the inlined `T` (recursive: + `[][]int64`, `[]Custom`); +- **`inline`** — expand the field's own Go type in place, instead of the + `$ref` a named type would otherwise produce; +- a **known type name** — inline that type's schema (again, no `$ref`). + +An unknown name falls back to inlining the field's Go type, with a +`validate.unsupported-type` diagnostic. + +## Supported keywords + +None — the override type is the entire surface. + +## Example + +Type-level override: + +```go +// ULID is a Crockford-base32 unique identifier rendered as a string. +// +// swagger:type string +type ULID [16]byte +``` + +Fields typed `ULID` emit as `{type: string}` regardless of the underlying +`[16]byte` shape. + +Field-level override: + +```go +type Document struct { + // Body is an opaque payload published as a string blob. + // + // swagger:type string + Body json.RawMessage `json:"body"` +} +``` + +**Interaction with `swagger:strfmt`.** `swagger:type` wins on the type +axis; a `swagger:strfmt` format on the same field is kept only when +**compatible** with the resolved type (a `string` accepts any format, +numeric types accept the numeric width formats), otherwise it is dropped +with a shape-mismatch diagnostic. `swagger:strfmt` alone is unchanged. See +[`swagger:strfmt`]({{% relref "swagger-strfmt" %}}). + +**Interaction with `swagger:model`.** On a *type declaration* that also +carries `swagger:model`, the override shapes the type's **first-class +definition** (e.g. `swagger:type string` + `swagger:model` → a +`{type: string}` definition) and referencing fields `$ref` it. The +field-level inline form above is the behaviour *without* `swagger:model`. + +**Full example.** `fixtures/enhancements/named-struct-tags-ref/types.go`. + +## Deprecated / legacy forms + +- The `array` argument is **deprecated** — use `inline`, or `[]T` for an + explicit element type. It still works, with a `validate.deprecated` + warning. +- `file` as an argument is rejected with a diagnostic — use + [`swagger:file`]({{% relref "swagger-file" %}}). diff --git a/docs/doc-site/maintainers/keywords.md b/docs/doc-site/maintainers/keywords.md index 59cccf1..7f982d5 100644 --- a/docs/doc-site/maintainers/keywords.md +++ b/docs/doc-site/maintainers/keywords.md @@ -291,7 +291,7 @@ type MyObjectType map[string]interface{} For **typed** value schemas (a regex mapped to a primitive or a model `$ref` rather than the empty `{}`), use the decl-level -[`swagger:patternProperties`]({{% relref "/maintainers/annotations#swaggerpatternproperties" %}}) +[`swagger:patternProperties`]({{% relref "/maintainers/annotations/swagger-patternproperties" %}}) marker. `patternProperties` is a JSON-Schema keyword beyond the Swagger 2.0 subset — see [Maps & free-form objects]({{% relref "/tutorials/maps-and-free-form-objects" %}}). @@ -302,13 +302,13 @@ Sets the policy for keys beyond the named properties on an **object**-typed schema. The argument is `true` (allow any extra key), `false` (forbid extra keys — close the object), or a **value type** (a primitive / `[]T`, or a model name that becomes a `$ref` — the -[`swagger:type`]({{% relref "/maintainers/annotations#swaggertype" %}}) +[`swagger:type`]({{% relref "/maintainers/annotations/swagger-type" %}}) value grammar). On a map field it overrides the Go element schema; on a `$ref`'d field the value rides an `allOf` sibling so the reference is kept. Aliases: `additional properties`, `additional-properties`. This is the field-keyword form; the decl-level -[`swagger:additionalProperties`]({{% relref "/maintainers/annotations#swaggeradditionalproperties" %}}) +[`swagger:additionalProperties`]({{% relref "/maintainers/annotations/swagger-additionalproperties" %}}) marker does the same on a type. It is the lowest-priority, object-only annotation — dropped with a `CodeShapeMismatch` diagnostic if the field resolved to a non-object. @@ -541,7 +541,7 @@ Precedence, most-explicit-wins and **identical in every context**: name: keyword > swagger:name annotation > json: tag > Go field name ``` -[`swagger:name`]({{% relref "/maintainers/annotations#swaggername" %}}) +[`swagger:name`]({{% relref "/maintainers/annotations/swagger-name" %}}) is the older annotation form — still honoured, and idiomatic on interface methods — but `name:` is the universal keyword. Using `swagger:name` in a parameter or response-header context (where `name:` diff --git a/docs/doc-site/shaping-the-output/field-types-and-formats/forcing-a-format.md b/docs/doc-site/shaping-the-output/field-types-and-formats/forcing-a-format.md index 011c275..1c9b1e7 100644 --- a/docs/doc-site/shaping-the-output/field-types-and-formats/forcing-a-format.md +++ b/docs/doc-site/shaping-the-output/field-types-and-formats/forcing-a-format.md @@ -31,5 +31,5 @@ a `UUID` type → `{string, format: uuid}`); see The `swagger:type` annotation is the related tool when you want to override the whole **type**, not just its format — see [Type discovery]({{% relref "type-discovery" %}}) and the -[`swagger:type` reference]({{% relref "/maintainers/annotations#swaggertype" %}}). +[`swagger:type` reference]({{% relref "/maintainers/annotations/swagger-type" %}}). {{% /notice %}} diff --git a/docs/doc-site/shaping-the-output/names-and-refs/alias-rendering.md b/docs/doc-site/shaping-the-output/names-and-refs/alias-rendering.md index 88e29de..0e0ae3f 100644 --- a/docs/doc-site/shaping-the-output/names-and-refs/alias-rendering.md +++ b/docs/doc-site/shaping-the-output/names-and-refs/alias-rendering.md @@ -40,7 +40,7 @@ type over aliasing one. Reach for `RefAliases` / `TransparentAliases` only when you specifically need to control whether an alias name survives in the output. The `swagger:alias` *annotation* is -[deprecated]({{% relref "/maintainers/annotations#swaggeralias--deprecated" %}}) +[deprecated]({{% relref "/maintainers/annotations/swagger-alias" %}}) and has no effect — alias rendering is governed by the plain Go alias plus these options, or by `swagger:model` for a first-class definition. {{% /notice %}} diff --git a/docs/doc-site/tutorials/document-metadata.md b/docs/doc-site/tutorials/document-metadata.md index 8a80e83..d8b3c52 100644 --- a/docs/doc-site/tutorials/document-metadata.md +++ b/docs/doc-site/tutorials/document-metadata.md @@ -35,7 +35,7 @@ the tags your routes reference (above, `pets` and `store`). For the full meta keyword surface (security definitions, external docs, extensions, terms of service), see the -[`swagger:meta` reference]({{% relref "/maintainers/annotations#swaggermeta" %}}) +[`swagger:meta` reference]({{% relref "/maintainers/annotations/swagger-meta" %}}) and the [meta keywords]({{% relref "/maintainers/keywords#meta-single-line-keywords" %}}). ## Security diff --git a/docs/doc-site/tutorials/maps-and-free-form-objects.md b/docs/doc-site/tutorials/maps-and-free-form-objects.md index 9ce3e8c..f656d8f 100644 --- a/docs/doc-site/tutorials/maps-and-free-form-objects.md +++ b/docs/doc-site/tutorials/maps-and-free-form-objects.md @@ -58,7 +58,7 @@ the policy, where `` is `true`, `false`, or a value type: A **type spec** instead of a bool gives the extra values a schema — a primitive (or `[]T`), or a model name that becomes a `$ref` (the same value-type grammar as -[`swagger:type`]({{% relref "/maintainers/annotations#swaggertype" %}}), except a +[`swagger:type`]({{% relref "/maintainers/annotations/swagger-type" %}}), except a type name resolves to a `$ref`): {{< compare left="concepts/maps/testdata/typed.json" leftlabel="integer — typed values" diff --git a/docs/doc-site/tutorials/model-definitions.md b/docs/doc-site/tutorials/model-definitions.md index d66554a..7bbc798 100644 --- a/docs/doc-site/tutorials/model-definitions.md +++ b/docs/doc-site/tutorials/model-definitions.md @@ -90,7 +90,7 @@ of an inlined type, `inline` to expand the field's own Go type, or a known type name to inline that type. `array` is deprecated in favour of `inline` / `[]T`, and `file` is rejected (use `swagger:file`). When combined with `swagger:strfmt`, the type wins and the format is kept only if compatible — see the -[reference]({{% relref "/maintainers/annotations#swaggertype" %}}). +[reference]({{% relref "/maintainers/annotations/swagger-type" %}}). The override also works **on an individual field doc** — no wrapper-type annotation. Here `Code` is published as a string while its `RawID` type is left diff --git a/docs/doc-site/tutorials/polymorphic-models.md b/docs/doc-site/tutorials/polymorphic-models.md index 3f7a21c..d321e8c 100644 --- a/docs/doc-site/tutorials/polymorphic-models.md +++ b/docs/doc-site/tutorials/polymorphic-models.md @@ -59,4 +59,4 @@ on the base; it is inherited by every subtype through the `$ref`. - [Routes & operations]({{% relref "/tutorials/routes-and-operations" %}}) — return a base type and let the discriminator carry the subtype. - [`discriminator` keyword]({{% relref "/maintainers/keywords#discriminator" %}}) - and [`swagger:allOf` reference]({{% relref "/maintainers/annotations#swaggerallof" %}}). + and [`swagger:allOf` reference]({{% relref "/maintainers/annotations/swagger-allof" %}}). diff --git a/docs/doc-site/tutorials/routes-and-operations.md b/docs/doc-site/tutorials/routes-and-operations.md index dcee9bc..8c8d369 100644 --- a/docs/doc-site/tutorials/routes-and-operations.md +++ b/docs/doc-site/tutorials/routes-and-operations.md @@ -64,7 +64,7 @@ usual shape for a POST or PUT payload: When a parameter field's Go type is a struct (or any type that has no simple Swagger representation), it cannot be a query/path/header parameter on its own. A -[`swagger:type`]({{% relref "/maintainers/annotations#swaggertype" %}}) override +[`swagger:type`]({{% relref "/maintainers/annotations/swagger-type" %}}) override collapses it to a simple parameter — a scalar, or a `[]`-wrapped scalar for an array parameter: From 1bf066647a70795a26b2db37668f72cca98faf54 Mon Sep 17 00:00:00 2001 From: Frederic BIDON Date: Sat, 27 Jun 2026 19:18:01 +0200 Subject: [PATCH 2/5] =?UTF-8?q?docs(doc-site):=20rework=20annotation=20pag?= =?UTF-8?q?es=20=E2=80=94=20Usage=20quick-form=20+=20live=20examples?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Give every per-annotation page a consistent shape: a friendly Usage quick-form at the top, a one-sentence lead in 'What it does', a formal 'Grammar (EBNF)' production, the supported keywords, and a worked Example. Where a test-covered witness exists, the Example is now a live {{< example >}} pane rendering the real annotated source beside the spec the scanner actually produces (concepts/models, concepts/routes, concepts/maps, concepts/meta) — so the reference can never drift from real output. swagger:ignore and swagger:default, which produce no definition, show source-only {{< code >}}; swagger:alias stays inert. Add two witness regions to back the new panes and regenerate their goldens: - concepts/models: Color{ R,G,B,A uint8 } — one property per name - concepts/maps: Settings — additionalProperties: {integer} Tidy docs/examples/go.mod (testify 2.5.1->2.6.0, x/tools 0.46->0.47) to match the root module so the examples module builds; this latent drift was surfaced by the new witness tests. Co-Authored-By: Claude Opus 4.8 Signed-off-by: Frederic BIDON --- .../swagger-additionalproperties.md | 27 ++++----- .../maintainers/annotations/swagger-alias.md | 8 ++- .../maintainers/annotations/swagger-allof.md | 55 ++++++------------- .../annotations/swagger-default.md | 29 +++++----- .../maintainers/annotations/swagger-enum.md | 54 ++++-------------- .../maintainers/annotations/swagger-file.md | 29 +++++----- .../maintainers/annotations/swagger-ignore.md | 38 +++++-------- .../maintainers/annotations/swagger-meta.md | 43 ++++++--------- .../maintainers/annotations/swagger-model.md | 55 +++++++++++++------ .../maintainers/annotations/swagger-name.md | 43 +++++++-------- .../annotations/swagger-operation.md | 41 ++++++-------- .../annotations/swagger-parameters.md | 41 ++++---------- .../annotations/swagger-patternproperties.md | 23 ++++---- .../annotations/swagger-response.md | 39 +++++-------- .../maintainers/annotations/swagger-route.md | 48 +++++----------- .../maintainers/annotations/swagger-strfmt.md | 33 ++++++----- .../maintainers/annotations/swagger-type.md | 46 ++++++++-------- docs/examples/concepts/maps/maps.go | 14 +++++ docs/examples/concepts/maps/maps_test.go | 1 + .../maps/testdata/addlpropstyped.json | 14 +++++ docs/examples/concepts/models/models.go | 14 +++++ docs/examples/concepts/models/models_test.go | 1 + .../concepts/models/testdata/multiname.json | 23 ++++++++ docs/examples/go.mod | 4 +- docs/examples/go.sum | 8 +-- 25 files changed, 347 insertions(+), 384 deletions(-) create mode 100644 docs/examples/concepts/maps/testdata/addlpropstyped.json create mode 100644 docs/examples/concepts/models/testdata/multiname.json diff --git a/docs/doc-site/maintainers/annotations/swagger-additionalproperties.md b/docs/doc-site/maintainers/annotations/swagger-additionalproperties.md index d40ddf8..9708d7f 100644 --- a/docs/doc-site/maintainers/annotations/swagger-additionalproperties.md +++ b/docs/doc-site/maintainers/annotations/swagger-additionalproperties.md @@ -4,13 +4,20 @@ weight: 10 description: "Sets a schema's additionalProperties policy for keys beyond the named properties." --- +## Usage + +```goish +// swagger:additionalProperties ( true | false | ) +``` ## What it does Sets a schema's `additionalProperties` — the policy for keys beyond the -named properties. On a struct it **complements** the named properties; on -a map type it **overrides** the element-derived value schema; on a type -that resolved to a bare `$ref` it **defines** a clean object. See the +named properties. + +On a struct it **complements** the named properties; on a map type it +**overrides** the element-derived value schema; on a type that resolved to +a bare `$ref` it **defines** a clean object. See the [Maps & free-form objects]({{% relref "/tutorials/maps-and-free-form-objects" %}}) tutorial. @@ -20,7 +27,7 @@ On a type declaration (alongside `swagger:model`). A field-level equivalent exists as the [`additionalProperties:` keyword]({{% relref "/maintainers/keywords#additionalproperties" %}}). -## Syntax +## Grammar (EBNF) ```ebnf AdditionalPropertiesAnnotation = ANN_ADDITIONAL_PROPERTIES , ( BOOL_VALUE | ValueType ) ; @@ -44,15 +51,9 @@ None of its own. It composes with `maxProperties` / `minProperties` / ## Example -```go -// Settings is an open object: named properties plus typed extra values. -// -// swagger:model -// swagger:additionalProperties integer -type Settings struct { - Name string `json:"name"` -} -``` +{{< example + go="concepts/maps/maps.go" goregion="addlpropstyped" + json="concepts/maps/testdata/addlpropstyped.json" >}} **Precedence — lowest priority.** `additionalProperties` only rides on an `object`. If a prior rule fixed a non-object type (a `swagger:type` diff --git a/docs/doc-site/maintainers/annotations/swagger-alias.md b/docs/doc-site/maintainers/annotations/swagger-alias.md index 551a079..9488dea 100644 --- a/docs/doc-site/maintainers/annotations/swagger-alias.md +++ b/docs/doc-site/maintainers/annotations/swagger-alias.md @@ -10,6 +10,12 @@ description: "Deprecated no-op — alias rendering is controlled by Go aliases + empty sink that only raises a `validate.deprecated` diagnostic. {{% /notice %}} +## Usage + +```goish +// swagger:alias [ IDENT_NAME ] +``` + ## What it does Nothing, today. Earlier documentation claimed it published a `$ref` to @@ -22,7 +28,7 @@ otherwise gets — and that force-inline behaviour has been removed. On a type alias / named-type declaration. -## Syntax +## Grammar (EBNF) ```ebnf AliasBlock = ANN_ALIAS , [ IDENT_NAME ] , [ Title ] , [ Description ] ; diff --git a/docs/doc-site/maintainers/annotations/swagger-allof.md b/docs/doc-site/maintainers/annotations/swagger-allof.md index cd032e6..ded6ac2 100644 --- a/docs/doc-site/maintainers/annotations/swagger-allof.md +++ b/docs/doc-site/maintainers/annotations/swagger-allof.md @@ -4,13 +4,19 @@ weight: 30 description: "Marks a struct as participating in an allOf composition." --- +## Usage + +```goish +// swagger:allOf +``` ## What it does -Marks a struct as participating in an `allOf` composition. The struct's -fields plus any embedded `swagger:model`-tagged base produce an -`allOf: [$ref base, {inline fields}]` schema. The companion convention is -to embed the base type as an anonymous field with this annotation on the +Marks a struct as participating in an `allOf` composition. + +The struct's fields plus any embedded `swagger:model`-tagged base produce +an `allOf: [$ref base, {inline fields}]` schema. The companion convention +is to embed the base type as an anonymous field with this annotation on the embedding's doc comment (or on the embedded type itself). ## Where it goes @@ -18,7 +24,7 @@ embedding's doc comment (or on the embedded type itself). On a struct field that embeds another type, or on a struct type that has at least one embedded base. -## Syntax +## Grammar (EBNF) ```ebnf AllOfBlock = ANN_ALLOF , [ Title ] , [ Description ] ; @@ -34,40 +40,13 @@ the inline-object member (the second `allOf` element). ## Example -```go -// Animal is the abstract base. -// -// swagger:model -type Animal struct { - Kind string `json:"kind"` -} - -// Dog is an Animal with a breed. -// -// swagger:model -type Dog struct { - // swagger:allOf - Animal - - Breed string `json:"breed"` -} -``` +A struct embedding a `swagger:model` base with `swagger:allOf` on the embed +produces an `allOf` of the base `$ref` and an inline-object member carrying +the embedding struct's own fields: -Produces: - -```json -"Dog": { - "allOf": [ - {"$ref": "#/definitions/Animal"}, - { - "type": "object", - "properties": { - "breed": {"type": "string", "x-go-name": "Breed"} - } - } - ] -} -``` +{{< example + go="concepts/models/models.go" goregion="allof" + json="concepts/models/testdata/allof.json" >}} The same composition applies when the embedding struct is a `swagger:response` body: the embedded base emits an `allOf: [{$ref}, …]` diff --git a/docs/doc-site/maintainers/annotations/swagger-default.md b/docs/doc-site/maintainers/annotations/swagger-default.md index 9947872..bb39d33 100644 --- a/docs/doc-site/maintainers/annotations/swagger-default.md +++ b/docs/doc-site/maintainers/annotations/swagger-default.md @@ -4,22 +4,26 @@ weight: 40 description: "Classifier hint marking a value declaration as a spec default anchor." --- +## Usage + +```goish +// swagger:default +``` ## What it does Marks the surrounding declaration as the spec's default value for the -corresponding shape. Used in narrow contexts where the scanner expects -an explicit anchor for a default. +corresponding shape. -This annotation is **value-only** — there's no exported entity it -publishes; it's a classifier hint the scanner consumes during -discovery. +Used in narrow contexts where the scanner expects an explicit anchor for a +default. This annotation is **value-only** — there's no exported entity it +publishes; it's a classifier hint the scanner consumes during discovery. ## Where it goes On a value declaration (`var`, `const`) or a struct field. -## Syntax +## Grammar (EBNF) ```ebnf DefaultClassifierBlock = ANN_DEFAULT , [ Title ] , [ Description ] ; @@ -37,10 +41,9 @@ directly. ## Example -```go -// DefaultLimit is the default page size used wherever Limit is not -// supplied by the caller. -// -// swagger:default -var DefaultLimit = 20 -``` +`swagger:default` is value-only: it produces no definition, so there is no +emitted spec to render. The source below shows the narrow classifier-hint +form — in practice most defaults come from the +[`default:` keyword]({{% relref "keywords#default" %}}) on a field. + +{{< code file="concepts/examples/examples.go" region="swaggerdefault" lang="go" >}} diff --git a/docs/doc-site/maintainers/annotations/swagger-enum.md b/docs/doc-site/maintainers/annotations/swagger-enum.md index 29187bb..c6dc96f 100644 --- a/docs/doc-site/maintainers/annotations/swagger-enum.md +++ b/docs/doc-site/maintainers/annotations/swagger-enum.md @@ -4,6 +4,11 @@ weight: 50 description: "Marks a named type as an enum and collects its const values." --- +## Usage + +```goish +// swagger:enum [ IDENT_NAME ] +``` ## What it does @@ -30,7 +35,7 @@ On a named type declaration. The type's `const` values are discovered via Go's type-system traversal; they do not need to live in the same file. The values surface only when a model reaches the enum type through a field. -## Syntax +## Grammar (EBNF) ```ebnf EnumBlock = ANN_ENUM , [ IDENT_NAME ] , [ Title ] , [ Description ] ; @@ -51,48 +56,13 @@ set; when present, it overrides the const-derived values and the ## Example -```go -// Priority is the urgency level on a task. -// -// swagger:enum Priority -type Priority string - -const ( - // PriorityLow is for tasks that can wait. - PriorityLow Priority = "low" - - // PriorityMedium is the default. - PriorityMedium Priority = "medium" - - // PriorityHigh is for tasks that must run soon. - PriorityHigh Priority = "high" -) - -// Task references Priority, which is what makes the enum reachable. -// -// swagger:model -type Task struct { - Priority Priority `json:"priority"` -} -``` +A named type marked `swagger:enum` with `const` values, referenced by a +model field, lands the values on that property (not on a standalone +definition) together with the `x-go-enum-desc` extension: -Produces (extract) — the values land on `Task`'s `priority` property, not -on a `Priority` definition: - -```json -{ - "Task": { - "type": "object", - "properties": { - "priority": { - "type": "string", - "enum": ["low", "medium", "high"], - "x-go-enum-desc": "low PriorityLow is for tasks that can wait.\nmedium PriorityMedium is the default.\nhigh PriorityHigh is for tasks that must run soon." - } - } - } -} -``` +{{< example + go="concepts/models/models.go" goregion="enum" + json="concepts/models/testdata/enum.json" >}} By default the const→value mapping is folded into the property's `description` **and** duplicated in `x-go-enum-desc`. Set the scanner diff --git a/docs/doc-site/maintainers/annotations/swagger-file.md b/docs/doc-site/maintainers/annotations/swagger-file.md index cf65bf5..6b76c32 100644 --- a/docs/doc-site/maintainers/annotations/swagger-file.md +++ b/docs/doc-site/maintainers/annotations/swagger-file.md @@ -4,19 +4,25 @@ weight: 60 description: "Marks a parameter or response body as a binary file (`{type: file}`)." --- +## Usage + +```goish +// swagger:file +``` ## What it does -Marks a parameter or response body as a binary file -(`{type: file}`). The scanner emits the file-type marker without -further introspection of the Go type. +Marks a parameter or response body as a binary file (`{type: file}`). + +The scanner emits the file-type marker without further introspection of the +Go type. ## Where it goes On a struct field doc inside a `swagger:parameters` (multipart file upload) or `swagger:response` (file download) struct. -## Syntax +## Grammar (EBNF) ```ebnf FileBlock = ANN_FILE , [ Title ] , [ Description ] ; @@ -33,15 +39,6 @@ Standard parameter / response keywords; the file marker stacks with ## Example -```go -// UploadParams declares a multipart file upload. -// -// swagger:parameters uploadFile -type UploadParams struct { - // File is the uploaded asset. - // - // in: formData - // swagger:file - File io.ReadCloser `json:"file"` -} -``` +{{< example + go="concepts/routes/routes.go" goregion="file" + json="concepts/routes/testdata/file.json" >}} diff --git a/docs/doc-site/maintainers/annotations/swagger-ignore.md b/docs/doc-site/maintainers/annotations/swagger-ignore.md index d48ead2..cc6931d 100644 --- a/docs/doc-site/maintainers/annotations/swagger-ignore.md +++ b/docs/doc-site/maintainers/annotations/swagger-ignore.md @@ -4,11 +4,17 @@ weight: 70 description: "Excludes the surrounding declaration (or one field) from the generated spec." --- +## Usage + +```goish +// swagger:ignore +``` ## What it does -Excludes the surrounding declaration from the generated spec. The -scanner sees the decl and the doc, classifies it, then drops it. +Excludes the surrounding declaration from the generated spec. + +The scanner sees the decl and the doc, classifies it, then drops it. When `swagger:ignore` appears **after** another classifier on the same comment block (e.g. `swagger:model` first, then `swagger:ignore`), the @@ -20,7 +26,7 @@ first annotation wins and the ignore is silently overridden. Place On a type declaration to exclude the whole type, or on a struct field doc to exclude that one field. -## Syntax +## Grammar (EBNF) ```ebnf IgnoreBlock = ANN_IGNORE , [ Title ] , [ Description ] ; @@ -35,26 +41,12 @@ None — the annotation is a stateless classifier marker. ## Example -```go -// Internal is not exposed. -// -// swagger:ignore -type Internal struct { - SecretField string -} -``` - -On a single field: +`swagger:ignore` produces no schema, so there is no live spec pane here: the +type below is scanned, classified, then dropped — it never reaches +`definitions`. On a type it excludes the whole declaration; on a struct +field it excludes just that one property (e.g. a `PasswordHash` kept out of +the wire shape). -```go -type User struct { - Name string `json:"name"` - - // PasswordHash is internal. - // - // swagger:ignore - PasswordHash string `json:"-"` -} -``` +{{< code file="concepts/models/models.go" region="ignore" lang="go" >}} **Full example.** `fixtures/enhancements/top-level-kinds/types.go`. diff --git a/docs/doc-site/maintainers/annotations/swagger-meta.md b/docs/doc-site/maintainers/annotations/swagger-meta.md index 9b7ec69..c63dd4d 100644 --- a/docs/doc-site/maintainers/annotations/swagger-meta.md +++ b/docs/doc-site/maintainers/annotations/swagger-meta.md @@ -4,21 +4,27 @@ weight: 80 description: "Declares the package as the top-level OpenAPI spec container." --- +## Usage + +```goish +// swagger:meta +``` ## What it does -Declares the package as the OpenAPI spec container. The scanner reads the -package doc comment for the top-level spec fields: title (via -[stripPackagePrefix]({{% relref "grammar#prose" %}}) of the doc's first line), -description, license, contact, host, basePath, version, schemes, consumes, -produces, securityDefinitions, extensions, and the rest of the meta keyword -surface. +Declares the package as the OpenAPI spec container. + +The scanner reads the package doc comment for the top-level spec fields: +title (via [stripPackagePrefix]({{% relref "grammar#prose" %}}) of the +doc's first line), description, license, contact, host, basePath, version, +schemes, consumes, produces, securityDefinitions, extensions, and the rest +of the meta keyword surface. ## Where it goes On the package doc comment. No arguments — a bare annotation. -## Syntax +## Grammar (EBNF) ```ebnf MetaBlock = ANN_META , [ Title ] , [ Description ] , MetaBody ; @@ -41,25 +47,8 @@ extensions per tag). ## Example -```go -// Package petstore Petstore API. -// -// The purpose of this application is to provide an application -// that is using plain Go code to define an API. -// -// Schemes: http, https -// Host: petstore.swagger.io -// BasePath: /v2 -// Version: 1.0.0 -// -// Consumes: -// - application/json -// -// Produces: -// - application/json -// -// swagger:meta -package petstore -``` +{{< example + go="concepts/meta/doc.go" goregion="meta" + json="concepts/meta/testdata/meta.json" >}} **Full example.** `fixtures/goparsing/spec/api.go`. diff --git a/docs/doc-site/maintainers/annotations/swagger-model.md b/docs/doc-site/maintainers/annotations/swagger-model.md index 615e1f6..e623b66 100644 --- a/docs/doc-site/maintainers/annotations/swagger-model.md +++ b/docs/doc-site/maintainers/annotations/swagger-model.md @@ -4,27 +4,34 @@ weight: 90 description: "Publishes a Go type as a definitions entry." --- +## Usage + +```goish +// swagger:model [] (where overrides the definition name; defaults to the Go type name) +``` ## What it does -Declares a Go type as a published model. The scanner walks the type, -emits a schema into the spec's `definitions` map, and resolves -cross-references between models. +Declares a Go type as a published model. + +The scanner walks the type, emits a schema into the spec's `definitions` map, +and resolves cross-references between models. The title/description split follows a heuristic: a single-line comment -**ending in a period** becomes the `title`; one **without** a trailing -period becomes the `description`; a **multi-line** comment uses the -first line as `title` and the rest as `description`. The descriptive -prose must come **before** the `swagger:model` line — an -annotation-first block still publishes the model but drops its title -and description. +**ending in a period** becomes the `title`. One **without** a trailing period +becomes the `description`; a **multi-line** comment uses the first line as +`title` and the rest as `description`. + +The descriptive prose must come **before** the `swagger:model` line — an +annotation-first block still publishes the model but drops its title and +description. ## Where it goes On a type declaration (`type T struct { … }`, `type T int`, `type T = Other`, …). -## Syntax +## Grammar (EBNF) ```ebnf ModelAnnotation = ANN_MODEL , [ IDENT_NAME ] ; @@ -41,14 +48,21 @@ body — its fields and their doc comments carry the schema validations. ## Supported keywords -All [schema decorators]({{% relref "keywords#schema-decorators" %}}) plus the -[length / array / numeric validations]({{% relref "keywords#numeric-validations" %}}) -on field doc comments. +Every [schema decorator]({{% relref "keywords#schema-decorators" %}}) and +[validation keyword]({{% relref "keywords#numeric-validations" %}}) is accepted +on a field doc comment. A keyword that is **not compatible** with the field's +inferred schema type (e.g. `minLength` on an integer) is ignored and raises a +diagnostic. ## Example +The doc comment above the type drives the model's name, title and description: + ```go -// Pet is the petstore's primary entity. +// Pet is the petstore's primary entity. <- title (first line, ends with a period) +// +// A pet can be any little animal you care about. <- description +// In this example the model name is inferred from the type name, here "Pet". // // swagger:model type Pet struct { @@ -63,15 +77,20 @@ type Pet struct { } ``` -With a name override, the type is published as `#/definitions/PetWithExtras`: +Pass an argument to override the name; the type is then published as +`#/definitions/PetWithExtras`: ```go // swagger:model PetWithExtras type DetailedPet struct { … } ``` -A field group declaring several names (`R, G, B, A uint8`) emits **one -property per name**; a `json:` tag on the group cannot rename the -individual fields, though tag options still apply. +A single field group declaring several names emits **one property per name**. +A `json:` tag on the group cannot rename the individual fields — each keeps its +own name — though tag options still apply: + +{{< example + go="concepts/models/models.go" goregion="multiname" + json="concepts/models/testdata/multiname.json" >}} **Full example.** `fixtures/enhancements/named-struct-tags-ref/types.go`. diff --git a/docs/doc-site/maintainers/annotations/swagger-name.md b/docs/doc-site/maintainers/annotations/swagger-name.md index ea608d0..0ca75bf 100644 --- a/docs/doc-site/maintainers/annotations/swagger-name.md +++ b/docs/doc-site/maintainers/annotations/swagger-name.md @@ -4,21 +4,27 @@ weight: 100 description: "Overrides the emitted property name of a struct field or interface method." --- +## Usage + +```goish +// swagger:name IDENT_NAME +``` ## What it does -Overrides the JSON property name that a struct field or interface -method renders as. By default the scanner derives names from -`json:"…"` struct tags (or the Go identifier for fields / methods with -no tag); `swagger:name` overrides that derivation when the tag-based -shape isn't appropriate — typically on **interface methods**, which -cannot carry struct tags. +Overrides the JSON property name that a struct field or interface method +renders as. + +By default the scanner derives names from `json:"…"` struct tags (or the +Go identifier for fields / methods with no tag); `swagger:name` overrides +that derivation when the tag-based shape isn't appropriate — typically on +**interface methods**, which cannot carry struct tags. ## Where it goes On a struct field doc OR an interface method doc. -## Syntax +## Grammar (EBNF) ```ebnf NameAnnotation = ANN_NAME , IDENT_NAME ; @@ -32,24 +38,13 @@ None — the override name is the entire surface. ## Example -```go -// UserProfile is the user's profile interface. -// -// swagger:model -type UserProfile interface { - // ID is the user identifier. - ID() string - - // FullName is the user's display name. - // - // swagger:name fullName - FullName() string -} -``` +On an interface method, `swagger:name` overrides the property name the +method would otherwise publish under (PascalCase Go method name) with the +chosen JSON name: -Without `swagger:name`, the method `FullName()` would publish as -property `FullName` (PascalCase). The annotation renames it to -`fullName`. +{{< example + go="concepts/models/models.go" goregion="name" + json="concepts/models/testdata/name.json" >}} **Full example.** `fixtures/enhancements/interface-methods/types.go`. diff --git a/docs/doc-site/maintainers/annotations/swagger-operation.md b/docs/doc-site/maintainers/annotations/swagger-operation.md index 6c47438..e0af8d4 100644 --- a/docs/doc-site/maintainers/annotations/swagger-operation.md +++ b/docs/doc-site/maintainers/annotations/swagger-operation.md @@ -4,14 +4,21 @@ weight: 110 description: "Declares an HTTP route + operation with a YAML-document body." --- +## Usage + +```goish +// swagger:operation METHOD PATH [tag …] OPERATION_ID +``` ## What it does -Declares an HTTP route + operation. Same header line as -{{% relref "swagger-route" %}} (method, path, optional tags, operation ID), -but with a different body shape: instead of the structured `Parameters:` / -`Responses:` keyword surface, `swagger:operation`'s body is a single YAML -document spelling out the OpenAPI operation object directly. +Declares an HTTP route + operation with a YAML-document body. + +Same header line as {{% relref "swagger-route" %}} (method, path, optional +tags, operation ID), but with a different body shape: instead of the +structured `Parameters:` / `Responses:` keyword surface, `swagger:operation`'s +body is a single YAML document spelling out the OpenAPI operation object +directly. Use `swagger:operation` when you want to author the operation in YAML (closer to the OpenAPI spec text) or when the operation has shapes the keyword surface @@ -23,7 +30,7 @@ On a function or variable declaration whose doc comment carries the annotation. The Go entity itself doesn't have to be a handler — the annotation publishes a path/operation independent of the carrier. -## Syntax +## Grammar (EBNF) ```ebnf InlineOperationBlock = ANN_OPERATION , OperationArgs @@ -57,25 +64,9 @@ The header line is the entire annotation surface. ## Example -```go -// swagger:operation GET /items/{id} items getItem -// -// --- -// summary: Get item by ID -// parameters: -// - name: id -// in: path -// required: true -// type: integer -// responses: -// '200': -// description: the requested item -// schema: -// $ref: '#/definitions/Item' -// default: -// $ref: '#/responses/genericError' -func GetItem() {} -``` +{{< example + go="concepts/routes/routes.go" goregion="operation" + json="concepts/routes/testdata/operation.json" >}} The `---` delimits the YAML body; everything between the fences is parsed as an OpenAPI 2.0 operation object. diff --git a/docs/doc-site/maintainers/annotations/swagger-parameters.md b/docs/doc-site/maintainers/annotations/swagger-parameters.md index e1b37d0..2a8a96e 100644 --- a/docs/doc-site/maintainers/annotations/swagger-parameters.md +++ b/docs/doc-site/maintainers/annotations/swagger-parameters.md @@ -4,10 +4,16 @@ weight: 120 description: "Declares a Go struct as the parameter set for one or more operations." --- +## Usage + +```goish +// swagger:parameters OPERATION_ID [OPERATION_ID …] +``` ## What it does -Declares a Go struct as the parameters set for one or more operations. +Declares a Go struct as the parameter set for one or more operations. + Each field becomes one parameter on the named operation(s), and the field's doc comment carries its `in:`, `required:`, validations, and description. @@ -32,7 +38,7 @@ On a struct declaration. A bare slice variable (`var Filters []string`) carries no per-field `in:`/`type:`/`required:`, so parameters must be a struct. -## Syntax +## Grammar (EBNF) ```ebnf ParametersAnnotation = ANN_PARAMETERS , IDENT_NAME , { IDENT_NAME } ; @@ -55,33 +61,8 @@ fields: `in`, `required`, the numeric / length / format validations, ## Example -```go -// ListItemsParams declares pagination + filter parameters for the -// listItems operation. -// -// swagger:parameters listItems -type ListItemsParams struct { - // Offset is the page offset. - // - // in: query - // minimum: 0 - // default: 0 - Offset int `json:"offset"` - - // Limit is the page size. - // - // in: query - // minimum: 1 - // maximum: 100 - // default: 20 - Limit int `json:"limit"` - - // Tag is the filter tag. - // - // in: query - // required: false - Tag string `json:"tag,omitempty"` -} -``` +{{< example + go="concepts/routes/routes.go" goregion="parameters" + json="concepts/routes/testdata/parameters.json" >}} **Full example.** `fixtures/enhancements/simple-schema-violation/api.go`. diff --git a/docs/doc-site/maintainers/annotations/swagger-patternproperties.md b/docs/doc-site/maintainers/annotations/swagger-patternproperties.md index 5a1ca7d..7e7809c 100644 --- a/docs/doc-site/maintainers/annotations/swagger-patternproperties.md +++ b/docs/doc-site/maintainers/annotations/swagger-patternproperties.md @@ -4,11 +4,18 @@ weight: 130 description: "Adds typed patternProperties entries mapping a name regex to a value schema." --- +## Usage + +```goish +// swagger:patternProperties "": [ , "": … ] +``` ## What it does Adds **typed** `patternProperties` entries — each maps a property-name -regex to a value schema. It is the typed counterpart of the regex-only +regex to a value schema. + +It is the typed counterpart of the regex-only [`patternProperties:` keyword]({{% relref "/maintainers/keywords#patternproperties" %}}) (which uses an empty, any-value schema). @@ -22,7 +29,7 @@ must understand it. On a type declaration (alongside `swagger:model`). -## Syntax +## Grammar (EBNF) ```ebnf PatternPropertiesAnnotation = ANN_PATTERN_PROPERTIES , PatternPair , { "," , PatternPair } ; @@ -43,15 +50,9 @@ None of its own. It composes with `maxProperties` / `minProperties` / ## Example -```go -// Headers carries x-prefixed string values and numeric-keyed counters. -// -// swagger:model -// swagger:patternProperties "^x-": string, "^\d+$": integer -type Headers struct { - Known string `json:"known"` -} -``` +{{< example + go="concepts/maps/maps.go" goregion="patterntyped" + json="concepts/maps/testdata/patterntyped.json" >}} **Precedence.** Same lowest-priority, object-only rule as [`swagger:additionalProperties`]({{% relref "swagger-additionalproperties" %}}). diff --git a/docs/doc-site/maintainers/annotations/swagger-response.md b/docs/doc-site/maintainers/annotations/swagger-response.md index d55f1a1..ae7dee4 100644 --- a/docs/doc-site/maintainers/annotations/swagger-response.md +++ b/docs/doc-site/maintainers/annotations/swagger-response.md @@ -4,13 +4,19 @@ weight: 140 description: "Declares a Go struct as a named response object." --- +## Usage + +```goish +// swagger:response [ IDENT_NAME ] +``` ## What it does -Declares a Go struct as a named response object, emitted into the spec's -top-level `responses` map. Routes / operations reference it by name via -the response sub-language (`Responses:` body in `swagger:route`, or the -YAML `$ref` form in `swagger:operation`). +Declares a Go struct as a named response object. + +It is emitted into the spec's top-level `responses` map. Routes / +operations reference it by name via the response sub-language (`Responses:` +body in `swagger:route`, or the YAML `$ref` form in `swagger:operation`). The struct's fields contribute the response shape: @@ -31,7 +37,7 @@ The struct's fields contribute the response shape: On a struct declaration. -## Syntax +## Grammar (EBNF) ```ebnf ResponseAnnotation = ANN_RESPONSE , [ IDENT_NAME ] ; @@ -56,26 +62,9 @@ body. ## Example -```go -// GenericError is the catch-all error response. -// -// swagger:response genericError -type GenericError struct { - // in: body - Body struct { - // Message is the human-readable error message. - Message string `json:"message"` - - // Code is the machine-readable error category. - Code string `json:"code,omitempty"` - } - - // X-Request-ID echoes the request correlation header. - // - // in: header - XRequestID string `json:"X-Request-ID"` -} -``` +{{< example + go="concepts/routes/routes.go" goregion="response" + json="concepts/routes/testdata/response.json" >}} Routes can then reference it via `response:genericError` in their `Responses:` body. diff --git a/docs/doc-site/maintainers/annotations/swagger-route.md b/docs/doc-site/maintainers/annotations/swagger-route.md index 0a270fe..78d20da 100644 --- a/docs/doc-site/maintainers/annotations/swagger-route.md +++ b/docs/doc-site/maintainers/annotations/swagger-route.md @@ -4,13 +4,19 @@ weight: 150 description: "Declares an HTTP route + operation in one annotation." --- +## Usage + +```goish +// swagger:route METHOD PATH [tag …] OPERATION_ID +``` ## What it does -Declares an HTTP route + operation in one annotation. The header line carries -the method, path, optional tags, and the operation ID; the comment body -carries the operation's metadata (consumes / produces / schemes / security / -parameters / responses / extensions). +Declares an HTTP route + operation in one annotation. + +The header line carries the method, path, optional tags, and the operation ID; +the comment body carries the operation's metadata (consumes / produces / +schemes / security / parameters / responses / extensions). This is the **terser of the two operation-declaration annotations**. Most go-swagger projects use `swagger:route` for hand-written operations; see @@ -27,7 +33,7 @@ A godoc-style identifier may precede the annotation on the same comment line identifier is recognised as a godoc convention and is not part of the annotation surface. -## Syntax +## Grammar (EBNF) ```ebnf RouteBlock = ANN_ROUTE , OperationArgs @@ -65,35 +71,9 @@ tags). The `Parameters:` and `Responses:` sub-languages are documented in ## Example -```go -// ListPets swagger:route GET /pets pets users listPets -// -// List pets filtered by some parameters. -// -// Consumes: -// - application/json -// -// Produces: -// - application/json -// -// Schemes: http, https -// -// Security: -// api_key: -// oauth: read, write -// -// Parameters: -// + name: limit -// in: query -// type: integer -// minimum: 1 -// maximum: 100 -// -// Responses: -// 200: body:[]Pet the pet list -// default: response:genericError -func ListPets() {} -``` +{{< example + go="concepts/routes/routes.go" goregion="route" + json="concepts/routes/testdata/route.json" >}} **Full example.** `fixtures/enhancements/routes-full-petstore-shape/handlers.go`. diff --git a/docs/doc-site/maintainers/annotations/swagger-strfmt.md b/docs/doc-site/maintainers/annotations/swagger-strfmt.md index 2830818..7792da5 100644 --- a/docs/doc-site/maintainers/annotations/swagger-strfmt.md +++ b/docs/doc-site/maintainers/annotations/swagger-strfmt.md @@ -4,13 +4,20 @@ weight: 160 description: "Marks a named type as a custom string format." --- +## Usage + +```goish +// swagger:strfmt FORMAT_NAME +``` ## What it does -Marks a named type as a custom string format. Wherever the type appears -as a field, the emitted schema is `{type: string, format: }`. Useful -for `UUID`, `Email`, `URL`-style types that have a Go type but should -serialise as a JSON string with a known format. +Marks a named type as a custom string format. + +Wherever the type appears as a field, the emitted schema is +`{type: string, format: }`. Useful for `UUID`, `Email`, `URL`-style +types that have a Go type but should serialise as a JSON string with a +known format. A field typed by the marked type emits with the format; the underlying type does NOT appear as a top-level model definition (strfmt-tagged types @@ -24,7 +31,7 @@ On a type declaration whose underlying form is a string-marshalable type `swagger:strfmt` may also sit on a struct **field** doc to override just that field's published format. -## Syntax +## Grammar (EBNF) ```ebnf StrfmtBlock = ANN_STRFMT , IDENT_NAME , [ Title ] , [ Description ] ; @@ -40,17 +47,13 @@ the entire surface. ## Example -```go -// MAC is a hardware address rendered as a colon-separated hex string. -// -// swagger:strfmt mac -type MAC string - -func (m MAC) MarshalText() ([]byte, error) { return []byte(m), nil } -func (m *MAC) UnmarshalText(b []byte) error { *m = MAC(b); return nil } -``` +A named type marked `swagger:strfmt` (here a `MarshalText`/`UnmarshalText` +hardware address) emits as `{type: string, format: …}` wherever it is +referenced — a field typed `MAC` comes out as `{type: string, format: mac}`: -A field typed `MAC` emits as `{type: string, format: mac}`. +{{< example + go="concepts/models/models.go" goregion="strfmt" + json="concepts/models/testdata/strfmt.json" >}} Adding `swagger:model` opts the type into a **first-class definition** carrying the full `{type: string, format: …}` schema, with referencing diff --git a/docs/doc-site/maintainers/annotations/swagger-type.md b/docs/doc-site/maintainers/annotations/swagger-type.md index 20b656d..7969047 100644 --- a/docs/doc-site/maintainers/annotations/swagger-type.md +++ b/docs/doc-site/maintainers/annotations/swagger-type.md @@ -4,13 +4,20 @@ weight: 170 description: "Replaces a field's or named type's inferred Swagger type with an inlined type." --- +## Usage + +```goish +// swagger:type (where is a scalar, []T, inline, or a known type name) +``` ## What it does Replaces a field's (or named type's) inferred Swagger type with an -**inlined** type. `swagger:type` is an inline directive — it never emits a -`$ref`; the chosen type is rendered directly in place (the default -`$ref`-for-named-types is the *no-annotation* behaviour). +**inlined** type. + +`swagger:type` is an inline directive — it never emits a `$ref`; the chosen +type is rendered directly in place (the default `$ref`-for-named-types is +the *no-annotation* behaviour). ## Where it goes @@ -27,7 +34,7 @@ schema to inline a type into. A compatible `swagger:strfmt` on the same field still rides as a supplementary format. {{% /notice %}} -## Syntax +## Grammar (EBNF) ```ebnf TypeBlock = ANN_TYPE , TYPE_REF , [ Title ] , [ Description ] ; @@ -52,28 +59,21 @@ None — the override type is the entire surface. ## Example -Type-level override: +Type-level override — a named type whose underlying shape is irrelevant to +the wire form is inlined to the chosen scalar; fields typed by it emit as +`{type: string}` regardless of the underlying shape: -```go -// ULID is a Crockford-base32 unique identifier rendered as a string. -// -// swagger:type string -type ULID [16]byte -``` - -Fields typed `ULID` emit as `{type: string}` regardless of the underlying -`[16]byte` shape. +{{< example + go="concepts/models/models.go" goregion="type" + json="concepts/models/testdata/type.json" >}} -Field-level override: +Field-level override — the same directive on a single struct field replaces +just that field's inferred type in place (e.g. an opaque payload published +as a string blob): -```go -type Document struct { - // Body is an opaque payload published as a string blob. - // - // swagger:type string - Body json.RawMessage `json:"body"` -} -``` +{{< example + go="concepts/models/models.go" goregion="typefield" + json="concepts/models/testdata/typefield.json" >}} **Interaction with `swagger:strfmt`.** `swagger:type` wins on the type axis; a `swagger:strfmt` format on the same field is kept only when diff --git a/docs/examples/concepts/maps/maps.go b/docs/examples/concepts/maps/maps.go index c18c3ce..899fbc5 100644 --- a/docs/examples/concepts/maps/maps.go +++ b/docs/examples/concepts/maps/maps.go @@ -123,3 +123,17 @@ type TypedPatterns struct { } // endsnippet:patterntyped + +// snippet:addlpropstyped + +// Settings is an open object: it keeps its named property and complements it +// with typed (integer) extra values — the swagger:additionalProperties marker +// sets the policy for keys beyond the named ones. +// +// swagger:model +// swagger:additionalProperties integer +type Settings struct { + Name string `json:"name"` +} + +// endsnippet:addlpropstyped diff --git a/docs/examples/concepts/maps/maps_test.go b/docs/examples/concepts/maps/maps_test.go index 3a9da42..7b1f3b2 100644 --- a/docs/examples/concepts/maps/maps_test.go +++ b/docs/examples/concepts/maps/maps_test.go @@ -68,6 +68,7 @@ func TestMapFragments(t *testing.T) { goldenJSON(t, doc, "ref", "RefObject") goldenJSON(t, doc, "fieldkeyword", "Holder") goldenJSON(t, doc, "patterntyped", "TypedPatterns") + goldenJSON(t, doc, "addlpropstyped", "Settings") // A map renders as an object with a single value schema. counts := doc.Definitions["Inventory"].Properties["counts"] diff --git a/docs/examples/concepts/maps/testdata/addlpropstyped.json b/docs/examples/concepts/maps/testdata/addlpropstyped.json new file mode 100644 index 0000000..ddda060 --- /dev/null +++ b/docs/examples/concepts/maps/testdata/addlpropstyped.json @@ -0,0 +1,14 @@ +{ + "description": "Settings is an open object: it keeps its named property and complements it\nwith typed (integer) extra values — the swagger:additionalProperties marker\nsets the policy for keys beyond the named ones.", + "type": "object", + "properties": { + "name": { + "type": "string", + "x-go-name": "Name" + } + }, + "additionalProperties": { + "type": "integer" + }, + "x-go-package": "github.com/go-openapi/codescan/docs/examples/concepts/maps" +} diff --git a/docs/examples/concepts/models/models.go b/docs/examples/concepts/models/models.go index 1beef4d..c52d9e1 100644 --- a/docs/examples/concepts/models/models.go +++ b/docs/examples/concepts/models/models.go @@ -24,6 +24,20 @@ type Pet struct { // endsnippet:model +// snippet:multiname + +// Color is an RGBA colour. A single field group declaring several names emits +// one property per name — R, G, B and A each become their own integer property. +// A json tag on the group cannot rename the individual fields (each keeps its +// own name), though tag options such as omitempty still apply. +// +// swagger:model +type Color struct { + R, G, B, A uint8 `json:",omitempty"` +} + +// endsnippet:multiname + // snippet:strfmt // MAC is a hardware address rendered as a colon-separated hex string. diff --git a/docs/examples/concepts/models/models_test.go b/docs/examples/concepts/models/models_test.go index 267adff..a17a509 100644 --- a/docs/examples/concepts/models/models_test.go +++ b/docs/examples/concepts/models/models_test.go @@ -77,6 +77,7 @@ func TestModelFragments(t *testing.T) { cases := []struct{ feature, defName string }{ {"model", "Pet"}, // swagger:model — a plain struct definition + {"multiname", "Color"}, // swagger:model — one property per name in a field group {"strfmt", "Device"}, // swagger:strfmt — a {type:string, format} field {"enum", "Task"}, // swagger:enum — enum values inlined on the field {"allof", "Dog"}, // swagger:allOf — two base $refs + inline arm diff --git a/docs/examples/concepts/models/testdata/multiname.json b/docs/examples/concepts/models/testdata/multiname.json new file mode 100644 index 0000000..5911580 --- /dev/null +++ b/docs/examples/concepts/models/testdata/multiname.json @@ -0,0 +1,23 @@ +{ + "description": "Color is an RGBA colour. A single field group declaring several names emits\none property per name — R, G, B and A each become their own integer property.\nA json tag on the group cannot rename the individual fields (each keeps its\nown name), though tag options such as omitempty still apply.", + "type": "object", + "properties": { + "A": { + "type": "integer", + "format": "uint8" + }, + "B": { + "type": "integer", + "format": "uint8" + }, + "G": { + "type": "integer", + "format": "uint8" + }, + "R": { + "type": "integer", + "format": "uint8" + } + }, + "x-go-package": "github.com/go-openapi/codescan/docs/examples/concepts/models" +} diff --git a/docs/examples/go.mod b/docs/examples/go.mod index 86764a8..3d275a7 100644 --- a/docs/examples/go.mod +++ b/docs/examples/go.mod @@ -9,7 +9,7 @@ go 1.25.0 require ( github.com/go-openapi/codescan v0.0.0 github.com/go-openapi/spec v0.22.6 - github.com/go-openapi/testify/v2 v2.5.1 + github.com/go-openapi/testify/v2 v2.6.0 ) require ( @@ -26,7 +26,7 @@ require ( go.yaml.in/yaml/v3 v3.0.4 // indirect golang.org/x/mod v0.37.0 // indirect golang.org/x/sync v0.21.0 // indirect - golang.org/x/tools v0.46.0 // indirect + golang.org/x/tools v0.47.0 // indirect ) replace github.com/go-openapi/codescan => ../.. diff --git a/docs/examples/go.sum b/docs/examples/go.sum index 82f9195..6d182f2 100644 --- a/docs/examples/go.sum +++ b/docs/examples/go.sum @@ -24,8 +24,8 @@ github.com/go-openapi/swag/yamlutils v0.26.1 h1:0TSLK+lXs9vfIhAWzBeI/lOzEnIoot6W github.com/go-openapi/swag/yamlutils v0.26.1/go.mod h1:7W5b7PRX9MxwL7TjeG7H8HkyBGRsIDRObhyMWFgBI2M= github.com/go-openapi/testify/enable/yaml/v2 v2.5.1 h1:q9NtHwK4qHF7yZziBPvZyv7zWAIk8ok88Gh2mR6Jpc8= github.com/go-openapi/testify/enable/yaml/v2 v2.5.1/go.mod h1:JW0MXIotCYps/XsgJnG3a8Q7rE5xAiBwoOD5OfaIQBk= -github.com/go-openapi/testify/v2 v2.5.1 h1:TMdhCaw8fUNraVSf3Omoob1dO/AzBfhtFAPW0an6sBo= -github.com/go-openapi/testify/v2 v2.5.1/go.mod h1:SgsVHtfooshd0tublTtJ50FPKhujf47YRqauXXOUxfw= +github.com/go-openapi/testify/v2 v2.6.0 h1:5PKH2HE7YJ/LuRPQGvSxBRlFXNQhSetBLlGAgUEu3ug= +github.com/go-openapi/testify/v2 v2.6.0/go.mod h1:SgsVHtfooshd0tublTtJ50FPKhujf47YRqauXXOUxfw= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= @@ -34,7 +34,7 @@ golang.org/x/mod v0.37.0 h1:vF1DjpVEshcIqoEaauuHebaLk1O1forxjxBaVn884JQ= golang.org/x/mod v0.37.0/go.mod h1:m8S8VeM9r4dzDwjrKO0a1sZP3YjeMamRRlD+fmR2Q/0= golang.org/x/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM= golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= -golang.org/x/tools v0.46.0 h1:7jTurBkPZu4moS/Uy4OQT1M+QBlsj3wejyZwsT8Z7rk= -golang.org/x/tools v0.46.0/go.mod h1:FrD85F8l+NWL+9XWBSyVSHO6Ne4jutsfIFba7AWQ5Ys= +golang.org/x/tools v0.47.0 h1:7Kn5x/d1svx/PzryTsqeoZN4TZwqeH5pGWjefhLi/1Q= +golang.org/x/tools v0.47.0/go.mod h1:dFHnyTvFWY212G+h7ZY4Vsp/K3U4/7W9TyVaAul8uCA= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= From 032a639c8f47f24affa27a00ce05664f2fe2cb71 Mon Sep 17 00:00:00 2001 From: Frederic BIDON Date: Sat, 27 Jun 2026 20:45:59 +0200 Subject: [PATCH 3/5] docs(doc-site): restructure keyword reference into class pages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Split the monolithic maintainers/keywords.md (~3800 words, three stacked intro sections + one giant flat table) into a lean section landing plus one page per keyword class. The landing foregrounds the three things that matter about a keyword: its class (a navigation table linking the subsection pages), the annotation contexts that accept it (a transposed context matrix — the flip of the annotation × keyword compatibility table), and its value shape. The value-shapes and annotation-contexts reference tables move to a dedicated appendix page that reads last. Five class pages — parameters & responses, schema validations & decorators, routes & operations, security, spec metadata — each carry a scoped summary table and witness-backed {{< example >}} worked examples drawn from the existing concepts/* fixtures. Each keyword has a single canonical home; pages where it merely visits list it in their summary table and cross-link. Repoint the 20 inbound keywords#anchor references across the annotation pages and tutorials at the owning subsection page (absolute paths where a basename collides with a tutorial of the same name). Pure documentation move; no scanner or golden changes. Co-Authored-By: Claude Opus 4.8 Signed-off-by: Frederic BIDON --- .../swagger-additionalproperties.md | 2 +- .../maintainers/annotations/swagger-allof.md | 2 +- .../annotations/swagger-default.md | 4 +- .../maintainers/annotations/swagger-enum.md | 2 +- .../maintainers/annotations/swagger-meta.md | 4 +- .../maintainers/annotations/swagger-model.md | 4 +- .../maintainers/annotations/swagger-name.md | 2 +- .../annotations/swagger-parameters.md | 4 +- .../annotations/swagger-patternproperties.md | 2 +- .../annotations/swagger-response.md | 2 +- .../maintainers/annotations/swagger-route.md | 2 +- docs/doc-site/maintainers/keywords.md | 866 ------------------ docs/doc-site/maintainers/keywords/_index.md | 71 ++ .../doc-site/maintainers/keywords/appendix.md | 53 ++ .../keywords/parameters-and-responses.md | 152 +++ .../keywords/routes-and-operations.md | 170 ++++ .../schema-validations-and-decorators.md | 207 +++++ .../doc-site/maintainers/keywords/security.md | 72 ++ .../maintainers/keywords/spec-metadata.md | 167 ++++ docs/doc-site/tutorials/document-metadata.md | 2 +- .../tutorials/maps-and-free-form-objects.md | 2 +- docs/doc-site/tutorials/model-definitions.md | 2 +- docs/doc-site/tutorials/polymorphic-models.md | 2 +- docs/doc-site/tutorials/security.md | 2 +- 24 files changed, 912 insertions(+), 886 deletions(-) delete mode 100644 docs/doc-site/maintainers/keywords.md create mode 100644 docs/doc-site/maintainers/keywords/_index.md create mode 100644 docs/doc-site/maintainers/keywords/appendix.md create mode 100644 docs/doc-site/maintainers/keywords/parameters-and-responses.md create mode 100644 docs/doc-site/maintainers/keywords/routes-and-operations.md create mode 100644 docs/doc-site/maintainers/keywords/schema-validations-and-decorators.md create mode 100644 docs/doc-site/maintainers/keywords/security.md create mode 100644 docs/doc-site/maintainers/keywords/spec-metadata.md diff --git a/docs/doc-site/maintainers/annotations/swagger-additionalproperties.md b/docs/doc-site/maintainers/annotations/swagger-additionalproperties.md index 9708d7f..4b91683 100644 --- a/docs/doc-site/maintainers/annotations/swagger-additionalproperties.md +++ b/docs/doc-site/maintainers/annotations/swagger-additionalproperties.md @@ -25,7 +25,7 @@ tutorial. On a type declaration (alongside `swagger:model`). A field-level equivalent exists as the -[`additionalProperties:` keyword]({{% relref "/maintainers/keywords#additionalproperties" %}}). +[`additionalProperties:` keyword]({{% relref "/maintainers/keywords/schema-validations-and-decorators#additionalproperties" %}}). ## Grammar (EBNF) diff --git a/docs/doc-site/maintainers/annotations/swagger-allof.md b/docs/doc-site/maintainers/annotations/swagger-allof.md index ded6ac2..977f2c8 100644 --- a/docs/doc-site/maintainers/annotations/swagger-allof.md +++ b/docs/doc-site/maintainers/annotations/swagger-allof.md @@ -35,7 +35,7 @@ follow on the doc comment. ## Supported keywords -[Schema-context keywords]({{% relref "keywords#schema-decorators" %}}) on +[Schema-context keywords]({{% relref "/maintainers/keywords/schema-validations-and-decorators#schema-decorators" %}}) on the inline-object member (the second `allOf` element). ## Example diff --git a/docs/doc-site/maintainers/annotations/swagger-default.md b/docs/doc-site/maintainers/annotations/swagger-default.md index bb39d33..71ea960 100644 --- a/docs/doc-site/maintainers/annotations/swagger-default.md +++ b/docs/doc-site/maintainers/annotations/swagger-default.md @@ -35,7 +35,7 @@ doc comment. ## Supported keywords None of its own. Most spec defaults are instead carried by the -[`default:` keyword]({{% relref "keywords#default" %}}) on the relevant +[`default:` keyword]({{% relref "/maintainers/keywords/schema-validations-and-decorators#default" %}}) on the relevant field; this annotation has a narrow surface and is not commonly authored directly. @@ -44,6 +44,6 @@ directly. `swagger:default` is value-only: it produces no definition, so there is no emitted spec to render. The source below shows the narrow classifier-hint form — in practice most defaults come from the -[`default:` keyword]({{% relref "keywords#default" %}}) on a field. +[`default:` keyword]({{% relref "/maintainers/keywords/schema-validations-and-decorators#default" %}}) on a field. {{< code file="concepts/examples/examples.go" region="swaggerdefault" lang="go" >}} diff --git a/docs/doc-site/maintainers/annotations/swagger-enum.md b/docs/doc-site/maintainers/annotations/swagger-enum.md index c6dc96f..5a6b586 100644 --- a/docs/doc-site/maintainers/annotations/swagger-enum.md +++ b/docs/doc-site/maintainers/annotations/swagger-enum.md @@ -49,7 +49,7 @@ are equivalent. ## Supported keywords -[Schema-context keywords]({{% relref "keywords#schema-decorators" %}}). The +[Schema-context keywords]({{% relref "/maintainers/keywords/schema-validations-and-decorators#schema-decorators" %}}). The `enum:` keyword can ALSO be used inline on the type doc to force a value set; when present, it overrides the const-derived values and the `x-go-enum-desc` is recomputed (or dropped) accordingly. diff --git a/docs/doc-site/maintainers/annotations/swagger-meta.md b/docs/doc-site/maintainers/annotations/swagger-meta.md index c63dd4d..31903e9 100644 --- a/docs/doc-site/maintainers/annotations/swagger-meta.md +++ b/docs/doc-site/maintainers/annotations/swagger-meta.md @@ -37,9 +37,9 @@ The body is a `MetaBody` of single-line `MetaKeyword`s ## Supported keywords -All [meta single-line keywords]({{% relref "keywords#meta-single-line-keywords" %}}) +All [meta single-line keywords]({{% relref "/maintainers/keywords/spec-metadata#meta-single-line-keywords" %}}) (`schemes`, `version`, `host`, `basePath`, `license`, `contact`) plus the -meta-scope [body keywords]({{% relref "keywords#body-keywords" %}}) +meta-scope [body keywords]({{% relref "/maintainers/keywords/spec-metadata" %}}) (`consumes`, `produces`, `security`, `securityDefinitions`, `extensions`, `infoExtensions`, `tos`, `externalDocs`, `tags`). A `Tags:` block declares the spec's top-level `tags` (name, description, nested `externalDocs`, `x-*` diff --git a/docs/doc-site/maintainers/annotations/swagger-model.md b/docs/doc-site/maintainers/annotations/swagger-model.md index e623b66..043f481 100644 --- a/docs/doc-site/maintainers/annotations/swagger-model.md +++ b/docs/doc-site/maintainers/annotations/swagger-model.md @@ -48,8 +48,8 @@ body — its fields and their doc comments carry the schema validations. ## Supported keywords -Every [schema decorator]({{% relref "keywords#schema-decorators" %}}) and -[validation keyword]({{% relref "keywords#numeric-validations" %}}) is accepted +Every [schema decorator]({{% relref "/maintainers/keywords/schema-validations-and-decorators#schema-decorators" %}}) and +[validation keyword]({{% relref "/maintainers/keywords/schema-validations-and-decorators#numeric-validations" %}}) is accepted on a field doc comment. A keyword that is **not compatible** with the field's inferred schema type (e.g. `minLength` on an integer) is ignored and raises a diagnostic. diff --git a/docs/doc-site/maintainers/annotations/swagger-name.md b/docs/doc-site/maintainers/annotations/swagger-name.md index 0ca75bf..7288c3b 100644 --- a/docs/doc-site/maintainers/annotations/swagger-name.md +++ b/docs/doc-site/maintainers/annotations/swagger-name.md @@ -52,7 +52,7 @@ chosen JSON name: `swagger:name` is the **legacy** annotation form. The canonical, universal field-naming mechanism is the -[`name:` keyword]({{% relref "/maintainers/keywords#name" %}}), which +[`name:` keyword]({{% relref "/maintainers/keywords/parameters-and-responses#name" %}}), which works at *every* field site — model properties, interface methods, parameters, and response headers — with the precedence `name:` > `swagger:name` > `json:` tag > Go field name. `swagger:name` remains diff --git a/docs/doc-site/maintainers/annotations/swagger-parameters.md b/docs/doc-site/maintainers/annotations/swagger-parameters.md index 2a8a96e..113033d 100644 --- a/docs/doc-site/maintainers/annotations/swagger-parameters.md +++ b/docs/doc-site/maintainers/annotations/swagger-parameters.md @@ -24,7 +24,7 @@ description. both and is the canonical, preferred way to set the name — the legacy `swagger:name` *annotation* is inert here and emits a `context-invalid` diagnostic pointing at `name:`. See the - [universal `name` keyword]({{% relref "/maintainers/keywords#name" %}}). + [universal `name` keyword]({{% relref "/maintainers/keywords/parameters-and-responses#name" %}}). - Operation IDs **accumulate**: the same ID may appear in several `swagger:parameters` lines to compose a set from multiple structs, and one struct may carry several lines splitting a long ID list. @@ -55,7 +55,7 @@ body — field doc comments carry the parameter validations. ## Supported keywords -[param-context keywords]({{% relref "keywords#parameter-location" %}}) on +[param-context keywords]({{% relref "/maintainers/keywords/parameters-and-responses#parameter-location" %}}) on fields: `in`, `required`, the numeric / length / format validations, `default`, `example`, `enum`, `allowEmptyValue`, `collectionFormat`. diff --git a/docs/doc-site/maintainers/annotations/swagger-patternproperties.md b/docs/doc-site/maintainers/annotations/swagger-patternproperties.md index 7e7809c..95131db 100644 --- a/docs/doc-site/maintainers/annotations/swagger-patternproperties.md +++ b/docs/doc-site/maintainers/annotations/swagger-patternproperties.md @@ -16,7 +16,7 @@ Adds **typed** `patternProperties` entries — each maps a property-name regex to a value schema. It is the typed counterpart of the regex-only -[`patternProperties:` keyword]({{% relref "/maintainers/keywords#patternproperties" %}}) +[`patternProperties:` keyword]({{% relref "/maintainers/keywords/schema-validations-and-decorators#patternproperties" %}}) (which uses an empty, any-value schema). {{% notice style="note" %}} diff --git a/docs/doc-site/maintainers/annotations/swagger-response.md b/docs/doc-site/maintainers/annotations/swagger-response.md index ae7dee4..fa090cb 100644 --- a/docs/doc-site/maintainers/annotations/swagger-response.md +++ b/docs/doc-site/maintainers/annotations/swagger-response.md @@ -27,7 +27,7 @@ The struct's fields contribute the response shape: `Body`/`in: body` nor `in: header` is treated as a header, not a body property. A header's key comes from the `json:` tag / Go field name, or a `name:` keyword (e.g. `name: X-Rate-Limit`) — the canonical, preferred - form, see the [`name` keyword]({{% relref "/maintainers/keywords#name" %}}). + form, see the [`name` keyword]({{% relref "/maintainers/keywords/parameters-and-responses#name" %}}). - An **anonymously embedded** struct marked `in: body` *is* the body (a `$ref` to the model), not a promotion of its fields. - An `interface{}` / `any`-typed field emits an empty schema (`{}`, or diff --git a/docs/doc-site/maintainers/annotations/swagger-route.md b/docs/doc-site/maintainers/annotations/swagger-route.md index 78d20da..4fbdc1f 100644 --- a/docs/doc-site/maintainers/annotations/swagger-route.md +++ b/docs/doc-site/maintainers/annotations/swagger-route.md @@ -61,7 +61,7 @@ swagger:route [tag1 tag2 …] ## Supported keywords -All [body keywords]({{% relref "keywords#body-keywords" %}}) legal in route +All [body keywords]({{% relref "/maintainers/keywords/routes-and-operations" %}}) legal in route context (`consumes`, `produces`, `schemes`, `security`, `parameters`, `responses`, `extensions`, `externalDocs`) plus inline `deprecated:` and a body `tags:` list (a string list, unioned and deduplicated with the header-line diff --git a/docs/doc-site/maintainers/keywords.md b/docs/doc-site/maintainers/keywords.md deleted file mode 100644 index 7f982d5..0000000 --- a/docs/doc-site/maintainers/keywords.md +++ /dev/null @@ -1,866 +0,0 @@ ---- -title: "Keyword reference" -weight: 20 -description: "Per-keyword reference card: every keyword form, its value shape, and the contexts where it is legal." ---- - - -This document catalogs the `keyword: value` forms recognised inside -annotation blocks. The keywords come in two flavours: - -- **Inline keywords** — one line, `keyword: value` shape, with the - value classified by a [value shape](#value-shapes) (number, integer, - boolean, string, …). -- **Body keywords** — a header line followed by indented continuation - lines. The body's interpretation depends on the keyword (a flat - token list for `Consumes:`, a YAML map for `SecurityDefinitions:`, - a per-line sub-language for `Parameters:` / `Responses:` on - `swagger:route`). - -The reader-friendly orientation is in [annotations.md]({{% relref "annotations" %}}) -(which annotation accepts which keywords, with examples); this file -is the **per-keyword reference card**. Implementers wanting the -formal productions should read [grammar.md]({{% relref "grammar" %}}). - ---- - -## Table of contents - -- [Reading the tables](#reading-the-tables) -- [Value shapes](#value-shapes) -- [Annotation contexts](#annotation-contexts) -- [Summary table](#summary-table) -- [Numeric validations](#numeric-validations) — `maximum`, `minimum`, `multipleOf` -- [Length / array / object validations](#length--array--object-validations) — `maxLength`, `minLength`, `maxItems`, `minItems`, `maxProperties`, `minProperties`, `patternProperties`, `additionalProperties` -- [Format validations](#format-validations) — `pattern`, `unique`, `collectionFormat` -- [Schema decorators](#schema-decorators) — `default`, `example`, `enum`, `required`, `readOnly`, `discriminator`, `deprecated` -- [Parameter location](#parameter-location) — `in` -- [Meta single-line keywords](#meta-single-line-keywords) — `schemes`, `version`, `host`, `basePath`, `license`, `contact` -- [Body keywords](#body-keywords) — `consumes`, `produces`, `security`, `securityDefinitions`, `responses`, `parameters`, `examples`, `extensions`, `infoExtensions`, `tos`, `externalDocs`, `tags` - ---- - -## Reading the tables - -Each keyword entry carries: - -- **Name** — canonical spelling. This is what `Property.Keyword.Name` - compares equal to. Comparisons are case-insensitive on the - canonical spelling and on every alias. -- **Aliases** — alternate spellings the lexer accepts. They map to - the canonical name at lex time; consumers never see alias values. -- **Value shape** — the lexical category of the value. See - [value shapes](#value-shapes) for what each one means and how it - surfaces to consumers. -- **Contexts** — the family-level scopes where the keyword is legal. - Using a keyword outside its legal contexts emits a - `CodeContextInvalid` diagnostic and the keyword is dropped from - the affected block. - -## Value shapes - -The grammar's lexer classifies every value into one of these -shapes. The shape determines which Walker callback fires for the -property and which field of `Property.Typed` carries the parsed -value. - -| Shape | Typed payload | Example value forms | -|-------|---------------|---------------------| -| `number` | `float64` (with optional `<`/`<=`/`>`/`>=`/`=` prefix) | `5`, `1.5`, `<10`, `>=0`, `=42` | -| `integer` | `int64` | `5`, `100` | -| `boolean` | `bool` | `true`, `false`, `1`, `0` | -| `string` | raw `string` | `^[a-z]+$`, `date-time`, `multipart/form-data` | -| `comma-list` | raw `string`; split on `,` by `Property.AsList()` | `http, https`, `a,b,c` | -| `enum-option` | typed `string` (closed-vocab match) | `csv`, `pipes` for `collectionFormat:` | -| `raw-block` | accumulated body lines on `Property.Body` | multi-line YAML, indented token lists | -| `raw-value` | the verbatim post-colon text on `Property.Value` | `42`, `"orange"`, `[1, 2, 3]` | - -When typing fails (e.g. `maximum: notanumber`) the lexer emits a -`CodeInvalidNumber` / `CodeInvalidInteger` / `CodeInvalidBoolean` -diagnostic and the property reaches the Walker with a zero-value -payload. Consumers gate on `Property.IsTyped()` to skip -malformed-typed values; the corresponding builder field stays -unwritten. - -## Annotation contexts - -The closed set of contexts a keyword can legally appear in. A keyword -table entry's `Contexts` field combines these: - -| Context | Meaning | -|---------|---------| -| `param` | Parameter doc on a `swagger:parameters` struct field, or a `+ name:` chunk inside `swagger:route Parameters:` | -| `header` | Header field on a `swagger:response` struct | -| `schema` | Top-level model or struct field on a `swagger:model` | -| `items` | Items-level (array element) validation on either parameter or schema | -| `route` | Route-level metadata under `swagger:route` | -| `operation` | Inline operation metadata under `swagger:operation` | -| `meta` | Package-level metadata under `swagger:meta` | -| `response` | Response-level decorations | - ---- - -## Summary table - -The full keyword surface, in the order the keyword table declares -them. Detailed entries follow this table. - -| Keyword | Aliases | Shape | Contexts | -|---------|---------|-------|----------| -| `maximum` | `max` | number | param, header, schema, items | -| `minimum` | `min` | number | param, header, schema, items | -| `multipleOf` | `multiple of`, `multiple-of` | number | param, header, schema, items | -| `maxLength` | `max length`, `max-length`, `maxLen`, `max len`, `max-len`, `maximum length`, `maximum-length`, `maximumLength`, `maximum len`, `maximum-len` | integer | param, header, schema, items | -| `minLength` | `min length`, `min-length`, `minLen`, `min len`, `min-len`, `minimum length`, `minimum-length`, `minimumLength`, `minimum len`, `minimum-len` | integer | param, header, schema, items | -| `pattern` | — | string | param, header, schema, items | -| `maxItems` | `max items`, `max-items`, `max.items`, `maximum items`, `maximum-items`, `maximumItems` | integer | param, header, schema, items | -| `minItems` | `min items`, `min-items`, `min.items`, `minimum items`, `minimum-items`, `minimumItems` | integer | param, header, schema, items | -| `unique` | — | boolean | param, header, schema, items | -| `collectionFormat` | `collection format`, `collection-format` | enum-option (`csv`, `ssv`, `tsv`, `pipes`, `multi`) | param, header, items | -| `maxProperties` | `max properties`, `max-properties`, `maximum properties`, `maximum-properties`, `maximumProperties` | integer | schema | -| `minProperties` | `min properties`, `min-properties`, `minimum properties`, `minimum-properties`, `minimumProperties` | integer | schema | -| `patternProperties` | `pattern properties`, `pattern-properties` | string (regex) | schema | -| `additionalProperties` | `additional properties`, `additional-properties` | `true` / `false` / type spec | schema | -| `default` | — | raw-value | param, header, schema, items | -| `example` | — | raw-value | param, header, schema, items | -| `enum` | — | raw-value | param, header, schema, items | -| `required` | — | boolean | param, schema | -| `readOnly` | `read only`, `read-only` | boolean | schema | -| `discriminator` | — | boolean | schema | -| `deprecated` | — | boolean | operation, route, schema | -| `in` | — | enum-option (`query`, `path`, `header`, `body`, `formData`) | param | -| `schemes` | — | raw-block (token list) | meta, route, operation | -| `version` | — | string | meta | -| `host` | — | string | meta | -| `basePath` | `base path`, `base-path` | string | meta | -| `license` | — | string | meta | -| `contact` | `contact info`, `contact-info` | string | meta | -| `consumes` | — | raw-block (token list) | meta, route, operation | -| `produces` | — | raw-block (token list) | meta, route, operation | -| `security` | — | raw-block (security requirements) | meta, route, operation | -| `securityDefinitions` | `security definitions`, `security-definitions` | raw-block (YAML map) | meta | -| `responses` | — | raw-block (response sub-language) | route, operation | -| `parameters` | — | raw-block (parameter chunk sub-language) | route, operation | -| `examples` | — | raw-block (YAML map keyed by mime type) | response | -| `extensions` | — | raw-block (YAML map of `x-*` entries) | meta, route, operation, schema, param, header | -| `infoExtensions` | `info extensions`, `info-extensions` | raw-block (YAML map of `x-*` entries) | meta | -| `tos` | `terms of service`, `terms-of-service`, `termsOfService` | raw-block (prose paragraph) | meta | -| `externalDocs` | `external docs`, `external-docs` | raw-block (YAML map) | meta, route, operation, schema, field | -| `tags` | — | raw-block (YAML) | meta, route, operation | - ---- - -## Numeric validations - -Apply to numeric schema types (`integer`, `number`). On a typed -schema with a non-numeric type, these keywords emit -`CodeShapeMismatch` and drop. On a typeless schema (no `type:` -declared upstream), they apply best-effort. - -### `maximum` - -Upper bound on a numeric value. Alias: `max`. - -The value may carry a leading comparison operator that becomes the -exclusive/inclusive bound: - -- `maximum: 10` — inclusive (≤ 10). -- `maximum: <10` — exclusive (< 10). -- `maximum: <=10` — inclusive (same as no prefix). -- `maximum: =10` — inclusive. - -Maps to `schema.maximum` and `schema.exclusiveMaximum`. - -```go -// Limit is the cap on items per page. -// -// maximum: 100 -// minimum: 1 -type Limit int -``` - -— from `fixtures/enhancements/...` (any numeric-validation fixture). - -### `minimum` - -Lower bound on a numeric value. Alias: `min`. Same operator-prefix -shape as `maximum`. Maps to `schema.minimum` and -`schema.exclusiveMinimum`. - -### `multipleOf` - -Divisibility constraint. The value must be a positive number. -Aliases: `multiple of`, `multiple-of`. Maps to `schema.multipleOf`. - -```go -// AllowedStep enforces increments of 5. -// -// multipleOf: 5 -type AllowedStep int -``` - ---- - -## Length / array / object validations - -`maxLength` / `minLength` apply only to **string-typed** schemas; -`maxItems` / `minItems` apply only to **array-typed** schemas; -`maxProperties` / `minProperties` / `patternProperties` apply only to -**object-typed** schemas. Using the wrong pairing emits -`CodeShapeMismatch` and drops the keyword. - -The object keywords are also **full-Schema-only**: there is no -SimpleSchema (non-body parameter, response header, or items chain) form -for them in OAS v2. Placing one on a SimpleSchema site emits -`CodeUnsupportedInSimpleSchema` and drops it. (Whether the offending -schema is detected via shape or via SimpleSchema mode, the keyword is -always dropped — never silently kept on a type it can't validate.) - -### `maxLength` - -Maximum string length. Many aliases for ergonomic spelling: -`max length`, `max-length`, `maxLen`, `max len`, `max-len`, -`maximum length`, `maximum-length`, `maximumLength`, `maximum len`, -`maximum-len`. Maps to `schema.maxLength`. - -### `minLength` - -Minimum string length. Same alias set as `maxLength` with `min` in -place of `max`. Maps to `schema.minLength`. - -### `maxItems` - -Maximum array length. Aliases: `max items`, `max-items`, -`max.items`, `maximum items`, `maximum-items`, `maximumItems`. Maps -to `schema.maxItems`. - -### `minItems` - -Minimum array length. Same alias shape as `maxItems` with `min` in -place of `max`. Maps to `schema.minItems`. - -```go -// Tags is a non-empty, bounded list. -// -// minItems: 1 -// maxItems: 20 -// unique: true -type Tags []string -``` - -### `maxProperties` - -Maximum number of properties on an **object**-typed schema. Aliases: -`max properties`, `max-properties`, `maximum properties`, -`maximum-properties`, `maximumProperties`. Maps to -`schema.maxProperties`. Schema-only — there is no SimpleSchema -(param/header/items) equivalent in OAS v2. - -### `minProperties` - -Minimum number of properties on an **object**-typed schema. Same -alias shape as `maxProperties` with `min` in place of `max`. Maps to -`schema.minProperties`. - -### `patternProperties` - -Constrains the **names** of properties on an **object**-typed schema by -regex. The argument is a single regex string; each `patternProperties` -line adds one entry to `schema.patternProperties`, mapping the regex to -an empty value schema (`{}` — "any value allowed for matching property -names"). Repeated lines accumulate distinct entries. Aliases: -`pattern properties`, `pattern-properties`. - -Like the `pattern` keyword, the regex is RE2-hygiene-checked: a value -that does not compile under Go's RE2 engine raises a -`CodeInvalidAnnotation` warning but is **preserved** on the schema -(downstream tools may use JSON Schema's wider regex dialect) — it is -never dropped silently. - -```go -// MyObjectType is a free-form object with property-count bounds and a -// property-name pattern. -// -// minProperties: 1 -// maxProperties: 10 -// patternProperties: ^x- -// -// swagger:model MyObjectType -type MyObjectType map[string]interface{} -``` - -For **typed** value schemas (a regex mapped to a primitive or a model -`$ref` rather than the empty `{}`), use the decl-level -[`swagger:patternProperties`]({{% relref "/maintainers/annotations/swagger-patternproperties" %}}) -marker. `patternProperties` is a JSON-Schema keyword beyond the Swagger -2.0 subset — see -[Maps & free-form objects]({{% relref "/tutorials/maps-and-free-form-objects" %}}). - -### `additionalProperties` - -Sets the policy for keys beyond the named properties on an -**object**-typed schema. The argument is `true` (allow any extra key), -`false` (forbid extra keys — close the object), or a **value type** (a -primitive / `[]T`, or a model name that becomes a `$ref` — the -[`swagger:type`]({{% relref "/maintainers/annotations/swagger-type" %}}) -value grammar). On a map field it overrides the Go element schema; on a -`$ref`'d field the value rides an `allOf` sibling so the reference is -kept. Aliases: `additional properties`, `additional-properties`. - -This is the field-keyword form; the decl-level -[`swagger:additionalProperties`]({{% relref "/maintainers/annotations/swagger-additionalproperties" %}}) -marker does the same on a type. It is the lowest-priority, object-only -annotation — dropped with a `CodeShapeMismatch` diagnostic if the field -resolved to a non-object. - -```go -type Holder struct { - // Values maps any key to an integer, overriding the Go element type. - // - // additionalProperties: integer - Values map[string]string `json:"values"` -} -``` - ---- - -## Format validations - -### `pattern` - -A regex constraint on a string value. The pattern is preserved -verbatim on `schema.pattern` — including backslash escapes: `\d`, -`\.`, `\n` reach the spec as the literal two-character sequences your -consumer's regex engine expects, not interpreted by the scanner. The -grammar runs a best-effort RE2 compile (Go's regex engine) on the -value; if it fails, a `CodeInvalidAnnotation` diagnostic surfaces with -the compile error. The value still lands on the schema — downstream -tools may use JSON Schema's wider regex dialect. - -```go -// Slug is a URL-friendly identifier. -// -// pattern: ^[a-z0-9-]+$ -type Slug string -``` - -### `unique` - -Marks an array-typed schema as set-valued (no duplicates). Boolean. -Maps to `schema.uniqueItems`. - -### `collectionFormat` - -How an array value is serialised on the wire. Closed-vocab: - -- `csv` — comma-separated (default). -- `ssv` — space-separated. -- `tsv` — tab-separated. -- `pipes` — pipe-separated. -- `multi` — repeated `?key=val&key=val2` (query params only). - -Aliases: `collection format`, `collection-format`. Maps to -`parameter.collectionFormat` / `items.collectionFormat`. Schema-level -contexts ignore this keyword (it's a SimpleSchema concept; schemas -serialise via `application/json`). - -When the source value doesn't match the closed vocab, the raw value -is preserved verbatim on the parameter (matches the original -behaviour where `pipe` as a typo for `pipes` round-trips). - -```go -// Tags is the form-data array of label tokens. -// -// in: query -// type: array -// collectionFormat: csv -// items.type: string -type TagsParam []string -``` - ---- - -## Schema decorators - -### `default` - -Default value for a schema or simple-schema field. Raw-value shape — -the post-colon text is captured verbatim and coerced against the -resolved schema type at write time (`ParseDefault` / -`CoerceValue`). - -Multi-line bodies are accepted for complex literals: - -```go -// Limits is the throughput envelope. -// -// default: -// { -// "rps": 100, -// "burst": 200 -// } -type Limits struct { ... } -``` - -Single-line form for primitives: - -```go -// Page is the page number. -// -// in: query -// type: integer -// default: 1 -type PageParam int -``` - -### `example` - -An example value for the schema, surfaced in tooling. Same raw-value -shape as `default`. Maps to `schema.example` (or `parameter.example` -for SimpleSchema parameters). This is the **singular**, schema-scoped -keyword; for the **plural** response-scoped [`examples`](#examples) (a -map keyed by mime type) see the Body keywords section. - -### `enum` - -A closed set of allowed values. Accepted surface forms: - -- **Comma list**: `enum: red, green, blue` — split on `,` and - trimmed. -- **Bracketed comma list**: `enum: [red, green, blue]` — the same, with - a surrounding `[ ]` pair stripped as delimiters (so the unquoted - bracketed and unbracketed forms are equivalent). -- **JSON array**: `enum: ["red", "green", "blue"]` — parsed via - YAML/JSON. -- **Multi-line list with `-` markers**: - ``` - enum: - - red - - green - - blue - ``` - -Each element is coerced against the resolved schema type. Maps to -`schema.enum`. - -For string-typed enums driven by Go `const` declarations the -`swagger:enum` annotation is the more idiomatic surface — it picks -up the constant names AND their godoc descriptions and produces an -`x-go-enum-desc` extension alongside the enum values. The -`enum:` keyword is the manual override. - -By default the const→value mapping that `swagger:enum` derives is folded -into the field's `description` **and** duplicated in `x-go-enum-desc`. Set -the scanner option `SkipEnumDescriptions: true` to keep the authored prose -as the description; the mapping then rides `x-go-enum-desc` only. This is -independent of `SkipExtensions` (set both to suppress the mapping entirely). - -When a struct field references a named primitive (`Status State` → -`type State string`), an `enum:` line in the referenced type's own doc -comment is parsed into that definition's enum values; the surrounding -prose becomes its title/description and the `enum:` line never leaks into -the text. - -### `required` - -Marks a field as required. Boolean. - -- On a `swagger:model` struct field: adds the field's name to the - enclosing schema's `required` array. -- On a `swagger:parameters` struct field: sets `parameter.required`. -- On a `swagger:response` header: not applicable; the keyword is - silently dropped (response headers don't carry `required`). - -### `readOnly` - -Marks a schema property as read-only. Aliases: `read only`, -`read-only`. Maps to `schema.readOnly`. - -Schema-only — emitting `readOnly:` inside a SimpleSchema context -(non-body parameter, response header) emits -`CodeUnsupportedInSimpleSchema` and drops the keyword. - -### `discriminator` - -Marks the property as the discriminator for an `allOf` polymorphic -schema. Boolean. Writes the property's name onto the enclosing -schema's `discriminator` field. Schema-only. The property should also be -`required` (a subtype cannot be selected from an absent value). Subtypes -that `allOf`-embed the base inherit the discriminator; the discriminator -value for each is its definition name — a custom-value annotation -(`swagger:discriminatorValue`) is not implemented. See the -[Polymorphic models]({{% relref "/tutorials/polymorphic-models" %}}) -tutorial. - -### `deprecated` - -Marks the carrying entity as deprecated. Boolean. On operations -(`operation.deprecated`) and routes (`operation.deprecated` on the -synthesised op) it writes the native OpenAPI 2.0 `deprecated` field. -OpenAPI 2.0 has no native `deprecated` on the Schema object, so on a -**model or model field** it emits `x-deprecated: true` instead. A -godoc-style `Deprecated:` paragraph (the pkgsite convention) is an exact -synonym for `deprecated: true`, recognised in any context — and is the -idiomatic form on a Go doc comment, where a bare `deprecated: true` line -reads as a malformed deprecation notice to Go linters. Because it carries -semantic intent, `x-deprecated` survives even when `SkipExtensions` is set. - ---- - -## Parameter location - -### `in` - -Where the parameter value comes from. Closed-vocab: - -- `query` — query string parameter. -- `path` — path-parameter substitution. -- `header` — request header. -- `body` — request body (JSON, etc.). -- `formData` — form-data body field (note: `form` accepted as an - alias inside `swagger:route Parameters:` chunks; the lexer - normalises to `formData` at the canonical surface). - -A non-matching value emits a context-invalid diagnostic; the -parameter loses its `in` and may end up incorrectly classified -downstream. - -### `name` - -Sets the published name of **any** field it decorates, overriding the -`json:` tag / Go field name. It is the one canonical field-naming -keyword and works at every field site: a `swagger:model` property, an -interface method, a `swagger:parameters` field (the parameter name), and -a `swagger:response` header field (the `Headers` map key). Being -structural, it is stripped from the description rather than leaking into -it. - -Precedence, most-explicit-wins and **identical in every context**: - -```text -name: keyword > swagger:name annotation > json: tag > Go field name -``` - -[`swagger:name`]({{% relref "/maintainers/annotations/swagger-name" %}}) -is the older annotation form — still honoured, and idiomatic on -interface methods — but `name:` is the universal keyword. Using -`swagger:name` in a parameter or response-header context (where `name:` -is canonical) is inert and now emits a `context-invalid` diagnostic -pointing you at the keyword. - -```go -// PageParams declares pagination query parameters. -// -// swagger:parameters listItems -type PageParams struct { - // in: query - // minimum: 0 - Offset int `json:"offset"` - - // in: query - // minimum: 1 - // maximum: 100 - // default: 20 - Limit int `json:"limit"` -} -``` - ---- - -## Meta single-line keywords - -Single-line keywords under `swagger:meta`. Values are taken as-is -from the post-colon string. - -### `schemes` - -Accepted URL schemes. Flexible list — all forms below produce the -same `["http", "https"]` output: - -``` -Schemes: http, https -Schemes: - - http - - https -Schemes: http - - https -``` - -Maps to `spec.schemes`. See [sub-languages.md]({{% relref "sub-languages" %}}) -§flex-lists for the unified rule. - -### `version` - -API version string. Maps to `info.version`. - -### `host` - -Default host. Defaults to `localhost` when empty. Maps to -`spec.host`. - -### `basePath` - -URL base path. Maps to `spec.basePath`. Aliases: `base path`, -`base-path`. - -### `license` - -License declaration. Two forms accepted: - -``` -License: Apache 2.0 http://www.apache.org/licenses/LICENSE-2.0.html -``` - -…where the trailing token starting with a URL scheme becomes -`license.url` and the prefix becomes `license.name`. A bare name -with no URL is accepted too. - -### `contact` - -Contact declaration. Author writes a `Name URL` triple, in -any order. The grammar recognises: - -- `Name ` — Go's `net/mail.ParseAddress` form. -- `Name http://example.com` — same + trailing - URL. -- Just a URL, no name. - -Aliases: `contact info`, `contact-info`. Maps to `info.contact`. - ---- - -## Body keywords - -Body keywords have a header line ending in `:` and indented -continuation lines. The body's structure depends on the keyword. -See [sub-languages.md]({{% relref "sub-languages" %}}) for the full -sub-language specifications; this section covers the keyword -shape. - -### `consumes` / `produces` - -Media-type lists. Same flex-list rule as `schemes:` — comma -inline, multi-line bare, YAML `-` markers, or any combination. -Maps to `consumes` / `produces` on the surrounding scope (spec, -operation). - -``` -Consumes: - - application/json - - application/xml - -Produces: application/json -``` - -Security-requirements list, parsed as **YAML** — a sequence of -requirement objects. Within one item, multiple schemes are **ANDed** -(all required); separate items are **ORed** (any one grants access). -Scopes are a flow (`[read, write]`) or block list; an empty list -(`api_key: []`) means the scheme is required with no scopes. - -``` -Security: - - api_key: [] # OR this requirement… - - oauth2: [read, write] # …or this one -``` - -``` -Security: - - api_key: [] # AND: both required (one item, two keys) - oauth2: [write] -``` - -Maps to `security`. A bare top-level mapping (`api_key:` / -`oauth2: read, write`, comma-split scopes) is still read as one OR -requirement per key for back-compatibility. Legal in `swagger:meta` -(document default), `swagger:route`, and `swagger:operation`; an empty -`Security: []` on an operation is an explicit "no security" opt-out. - -### `securityDefinitions` - -YAML map declaring security schemes. The body is parsed as YAML -directly into the `spec.securityDefinitions` shape — see -[OAS v2 §5.2.16](https://swagger.io/specification/v2/#securityDefinitionsObject). - -``` -SecurityDefinitions: - api_key: - type: apiKey - in: header - name: X-API-Key - oauth2: - type: oauth2 - flow: implicit - authorizationUrl: https://example.com/auth - scopes: - read: read access - write: write access -``` - -Aliases: `security definitions`, `security-definitions`. Meta-only. - -### `responses` - -Per-route / per-operation response declarations. Each line is one -response in the form `: `. See -[sub-languages.md §responses]({{% relref "sub-languages#responses" %}}) for the -full per-line grammar. - -``` -Responses: - 200: body:User the requested user - 404: description: not found - default: response:genericError -``` - -### `examples` - -Response-level examples on a `swagger:response` struct — a YAML map -whose first-level keys are **mime types** and whose values are the -example payloads, populating the OAS2 `Response.examples` field. Note -the OAS2 keyword split: the **plural** `examples` is the *response* -keyword (this one); the **singular** [`example`](#example) is the -schema / param / header / items decorator. The `swagger:operation` YAML -body carries `examples` natively (it is unmarshalled straight into the -spec types); this keyword is the struct-`swagger:response` counterpart. - -```go -// swagger:response widgetResponse -// -// examples: -// -// application/json: -// name: alice -// count: 3 -// application/xml: "alice" -type WidgetResponse struct { - // in: body - Body Widget `json:"body"` -} -``` - -### `parameters` - -Per-route / per-operation parameter declarations. Body is a sequence -of `+ name:` chunks (the `+` is the chunk-start sigil; `-` is -accepted as an alias). See -[sub-languages.md §parameters]({{% relref "sub-languages#parameters" %}}) for -the full per-chunk grammar. - -``` -Parameters: - + name: id - in: path - type: integer - required: true - + name: limit - in: query - type: integer - default: 20 - minimum: 1 - maximum: 100 -``` - -### `extensions` / `infoExtensions` - -Vendor extension declarations as a YAML map. Keys must start with -`x-` or `X-`; non-`x-*` keys emit `CodeInvalidAnnotation` and drop. - -- `extensions:` lands the entries on the surrounding scope - (`spec.extensions`, `operation.extensions`, `schema.extensions`, - `parameter.extensions`, `header.extensions`, …) — including on - parameters and response headers. -- `infoExtensions:` is meta-only; entries land on `info.extensions`. - -``` -Extensions: - x-internal-id: 42 - x-feature-flags: - - alpha - - beta - x-nested: - enabled: true - rate: 0.5 -``` - -Aliases: `info extensions`, `info-extensions` (for `infoExtensions`). - -### `tos` - -Terms-of-service prose paragraph. Multi-line body is joined with -`\n` after dropping whitespace-only lines. Aliases: -`terms of service`, `terms-of-service`, `termsOfService`. Maps to -`info.termsOfService`. Meta-only. - -### `externalDocs` - -External documentation pointer as a YAML map with `description` and -`url` keys. Aliases: `external docs`, `external-docs`. - -Emitted on: - -- **`swagger:meta`** → the top-level `externalDocs` object (and, nested under a - `Tags:` entry, that tag's `externalDocs`); -- **`swagger:route` / `swagger:operation`** → the operation's `externalDocs`; -- **`swagger:model`** (and any full Schema, e.g. a body parameter's schema) - → the schema's `externalDocs`; -- a **struct field** → the property's `externalDocs`. On a `$ref`'d field - (whose property is a bare `$ref`) it is lifted onto the wrapping `allOf` - compound, alongside the field's `description` and `x-*` siblings. - -An empty block (no `description`/`url`) is skipped rather than emitting a -bare `externalDocs: {}`. It is a **full-Schema-only** keyword: on a -SimpleSchema site (a non-body parameter, response header, or items -chain) it is dropped with a `CodeUnsupportedInSimpleSchema` diagnostic. - -``` -ExternalDocs: - description: Reference documentation - url: https://example.com/docs -``` - ---- - -### `tags` - -Top-level tag declarations. Behaviour depends on context: - -- In **`swagger:meta`** the body is a YAML sequence of tag objects emitted into - the spec's top-level `tags` — each with a `name`, an optional `description`, - a nested `externalDocs`, and any `x-*` vendor extensions: - - ``` - Tags: - - name: pets - description: Everything about your Pets - externalDocs: - description: Find out more - url: https://example.com/docs/pets - - name: store - x-display-name: Store - ``` - -- In **`swagger:route` / `swagger:operation`** it is a plain string list, - unioned and deduplicated with the annotation's header-line tags onto the - operation's `tags`. - ---- - -## Cross-keyword interactions - -A handful of keyword interactions are worth flagging: - -- **`default` + `example` + `enum`** on the same field: all three - may co-occur. The values are coerced against the resolved schema - type independently. If `enum` is declared and `default` is not a - member of it, no diagnostic fires today — downstream JSON Schema - validation catches it. -- **`type` + numeric validations + `format`** on a body parameter: - the schema dispatcher's `checkShape` gates numeric / length - validations against the resolved type. `format` is type-blind - (any format string lands). -- **`required` on a `$ref'd` field**: writes to the enclosing - schema's `required` array (the standard JSON-Schema-draft-4 - shape). If the field has sibling overrides, the `$ref` rewrites - into an `allOf` compound — see - [grammar.md]({{% relref "grammar" %}}) §refoverride. diff --git a/docs/doc-site/maintainers/keywords/_index.md b/docs/doc-site/maintainers/keywords/_index.md new file mode 100644 index 0000000..ac46613 --- /dev/null +++ b/docs/doc-site/maintainers/keywords/_index.md @@ -0,0 +1,71 @@ +--- +title: "Keyword reference" +weight: 20 +description: "The keyword: value forms recognised inside annotation blocks — grouped by class, with the annotation contexts that accept each one and its value shape." +--- + +Keywords are the `keyword: value` lines that decorate an +[annotation]({{% relref "annotations" %}}) block. They come in two flavours: +**inline** (one line, `keyword: value`, the value classified by a +[value shape]({{% relref "appendix#value-shapes" %}})) and **body** (a header line +plus indented continuation lines — a flat token list, a YAML map, or a per-line +sub-language). Three things matter about any keyword: the **class** it belongs to, +the **annotation contexts** that accept it, and its **value shape**. + +This section groups the surface by class — pick the page that matches what you're +decorating. For the formal productions see [grammar.md]({{% relref "grammar" %}}); +for the value-shape and context-token reference tables see the +[Appendix]({{% relref "appendix" %}}). + +## Keyword classes + +| Class | Covers | Keywords | +|-------|--------|----------| +| [Parameters & responses]({{% relref "parameters-and-responses" %}}) | request parameters and response headers (the reduced SimpleSchema surface) | `in`, `name`, `collectionFormat`, `examples`, + the shared validations | +| [Schema validations & decorators]({{% relref "schema-validations-and-decorators" %}}) | model schemas and struct fields | `maximum`/`minimum`/`multipleOf`, `maxLength`/`minLength`, `maxItems`/`minItems`, `maxProperties`/`minProperties`, `pattern`, `patternProperties`, `additionalProperties`, `unique`, `default`, `example`, `enum`, `required`, `readOnly`, `discriminator`, `deprecated` | +| [Routes & operations]({{% relref "/maintainers/keywords/routes-and-operations" %}}) | `swagger:route` / `swagger:operation` metadata | `schemes`, `consumes`, `produces`, `responses`, `parameters`, `tags` | +| [Security]({{% relref "/maintainers/keywords/security" %}}) | authentication requirements & scheme definitions | `security`, `securityDefinitions` | +| [Spec metadata]({{% relref "spec-metadata" %}}) | `swagger:meta` top-of-document fields | `version`, `host`, `basePath`, `license`, `contact`, `tos`, `infoExtensions`, `externalDocs`, `extensions`, `tags` | + +{{< children type="card" description="true" >}} + +## Context matrix + +Which annotation family accepts a given keyword — the transpose of the +[annotation × keyword matrix]({{% relref "annotations#annotation--keyword-compatibility-matrix" %}}). +A ✅ means the keyword is legal on that annotation (on the annotation's own block or +on one of its fields); a blank means it is rejected there with a +`CodeContextInvalid` diagnostic. The detailed entry for each keyword lives on its +class page (linked above). + +| Keyword | `meta` | `model` | `parameters` | `response` | `route` | `operation` | +|---------|:------:|:-------:|:------------:|:----------:|:-------:|:-----------:| +| `maximum` `minimum` `multipleOf` | | ✅ | ✅ | ✅ | | | +| `maxLength` `minLength` | | ✅ | ✅ | ✅ | | | +| `maxItems` `minItems` `unique` | | ✅ | ✅ | ✅ | | | +| `pattern` | | ✅ | ✅ | ✅ | | | +| `collectionFormat` | | | ✅ | ✅ | | | +| `maxProperties` `minProperties` | | ✅ | | | | | +| `patternProperties` `additionalProperties` | | ✅ | | | | | +| `default` `example` `enum` | | ✅ | ✅ | ✅ | | | +| `required` | | ✅ | ✅ | | | | +| `readOnly` `discriminator` | | ✅ | | | | | +| `deprecated` | | ✅ | | | ✅ | ✅ | +| `in` | | | ✅ | | | | +| `name` | | ✅ | ✅ | ✅ | | | +| `examples` | | | | ✅ | | | +| `schemes` `consumes` `produces` | ✅ | | | | ✅ | ✅ | +| `security` | ✅ | | | | ✅ | ✅ | +| `securityDefinitions` | ✅ | | | | | | +| `responses` `parameters` | | | | | ✅ | ✅ | +| `tags` | ✅ | | | | ✅ | ✅ | +| `version` `host` `basePath` `license` `contact` `tos` | ✅ | | | | | | +| `infoExtensions` | ✅ | | | | | | +| `externalDocs` | ✅ | ✅ | | | ✅ | ✅ | +| `extensions` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | + +The `parameters` / `response` columns also cover the **items** sub-context (array +elements): the array-element validations ride there too. `model` covers +`swagger:allOf` member fields. See the +[Appendix]({{% relref "appendix#annotation-contexts" %}}) for the precise meaning of +each context token (`param`, `header`, `schema`, `items`, …). diff --git a/docs/doc-site/maintainers/keywords/appendix.md b/docs/doc-site/maintainers/keywords/appendix.md new file mode 100644 index 0000000..7155d0d --- /dev/null +++ b/docs/doc-site/maintainers/keywords/appendix.md @@ -0,0 +1,53 @@ +--- +title: "Appendix: shapes & contexts" +weight: 60 +description: "Reference tables — the value shapes the lexer classifies, and the meaning of each annotation-context token." +--- + +The two reference tables behind the keyword class pages: the **value shapes** the +lexer assigns to a keyword's value, and the **context tokens** used in each page's +scoped summary table. + +## Value shapes + +The grammar's lexer classifies every value into one of these shapes. The shape +determines which Walker callback fires for the property and which field of +`Property.Typed` carries the parsed value. + +| Shape | Typed payload | Example value forms | +|-------|---------------|---------------------| +| `number` | `float64` (with optional `<`/`<=`/`>`/`>=`/`=` prefix) | `5`, `1.5`, `<10`, `>=0`, `=42` | +| `integer` | `int64` | `5`, `100` | +| `boolean` | `bool` | `true`, `false`, `1`, `0` | +| `string` | raw `string` | `^[a-z]+$`, `date-time`, `multipart/form-data` | +| `comma-list` | raw `string`; split on `,` by `Property.AsList()` | `http, https`, `a,b,c` | +| `enum-option` | typed `string` (closed-vocab match) | `csv`, `pipes` for `collectionFormat:` | +| `raw-block` | accumulated body lines on `Property.Body` | multi-line YAML, indented token lists | +| `raw-value` | the verbatim post-colon text on `Property.Value` | `42`, `"orange"`, `[1, 2, 3]` | + +When typing fails (e.g. `maximum: notanumber`) the lexer emits a +`CodeInvalidNumber` / `CodeInvalidInteger` / `CodeInvalidBoolean` diagnostic and the +property reaches the Walker with a zero-value payload. Consumers gate on +`Property.IsTyped()` to skip malformed-typed values; the corresponding builder field +stays unwritten. + +## Annotation contexts + +The closed set of contexts a keyword can legally appear in. Each class page's scoped +summary table combines these in its **Contexts** column. + +| Context | Meaning | +|---------|---------| +| `param` | Parameter doc on a `swagger:parameters` struct field, or a `+ name:` chunk inside `swagger:route Parameters:` | +| `header` | Header field on a `swagger:response` struct | +| `schema` | Top-level model or struct field on a `swagger:model` | +| `items` | Items-level (array element) validation on either parameter or schema | +| `route` | Route-level metadata under `swagger:route` | +| `operation` | Inline operation metadata under `swagger:operation` | +| `meta` | Package-level metadata under `swagger:meta` | +| `response` | Response-level decorations | + +Using a keyword outside its legal contexts emits a `CodeContextInvalid` diagnostic +and the keyword is dropped from the affected block. The +[Context matrix]({{% relref "_index#context-matrix" %}}) maps these tokens onto the +annotation families. diff --git a/docs/doc-site/maintainers/keywords/parameters-and-responses.md b/docs/doc-site/maintainers/keywords/parameters-and-responses.md new file mode 100644 index 0000000..f6db0ea --- /dev/null +++ b/docs/doc-site/maintainers/keywords/parameters-and-responses.md @@ -0,0 +1,152 @@ +--- +title: "Parameters & responses" +weight: 10 +description: "Keywords that decorate swagger:parameters fields and swagger:response headers — the reduced OAS 2.0 SimpleSchema surface, plus the parameter location and response-level examples." +--- + +These keywords decorate `swagger:parameters` fields and `swagger:response` +headers. Both sites ride the reduced OAS 2.0 **SimpleSchema** surface: the +validations constrain a primitive (or array-of-primitive) value, but the +full-Schema-only keywords (`maxProperties` / `minProperties` / +`patternProperties` / `additionalProperties` / `readOnly` / `discriminator` / +`externalDocs`) do **not** apply here. The location keyword `in` and the +universal `name` keyword are at home on this page; the validations are shared +with [Schema validations & decorators]({{% relref "schema-validations-and-decorators" %}}). + +## Summary + +| Keyword | Aliases | Shape | Contexts | +|---------|---------|-------|----------| +| `in` | — | string (closed-vocab) | param | +| `name` | — | string | param, header, schema, items | +| `collectionFormat` | `collection format`, `collection-format` | string (closed-vocab) | param, header, items | +| `examples` | — | YAML map (mime → payload) | response | +| `maximum` | `max` | number | param, header | +| `minimum` | `min` | number | param, header | +| `multipleOf` | `multiple of`, `multiple-of` | number | param, header | +| `maxLength` | `max length`, `maxLen`, … | integer | param, header | +| `minLength` | `min length`, `minLen`, … | integer | param, header | +| `maxItems` | `max items`, `maximumItems`, … | integer | param, header | +| `minItems` | `min items`, `minimumItems`, … | integer | param, header | +| `pattern` | — | string | param, header | +| `unique` | — | boolean | param, header | +| `default` | — | raw-value | param, header | +| `example` | — | raw-value | param, header | +| `enum` | — | raw-value | param, header | +| `required` | — | boolean | param | + +The validation rows (`maximum` … `required`) are **visiting** here: they behave +exactly as on schemas — see +[Schema validations & decorators]({{% relref "schema-validations-and-decorators" %}}). +Two SimpleSchema restrictions apply on this page: `required` is **dropped on +response headers** (it sets `parameter.required` only on a body/non-body param), +and the object / structural keywords (`maxProperties`, `minProperties`, +`patternProperties`, `additionalProperties`, `readOnly`, `discriminator`, +`externalDocs`) are **not legal** here — placing one on a SimpleSchema site +drops it with `CodeUnsupportedInSimpleSchema`. + +## Worked example(s) + +A parameter set, every field carrying the SimpleSchema validation surface: + +{{< example + go="concepts/validations/validations.go" goregion="param" + json="concepts/validations/testdata/param.json" >}} + +A response with a validated header (note `in` is absent on header fields): + +{{< example + go="concepts/validations/validations.go" goregion="header" + json="concepts/validations/testdata/header.json" >}} + +## Parameter location + +### `in` + +Where the parameter value comes from. Closed-vocab: + +- `query` — query string parameter. +- `path` — path-parameter substitution. +- `header` — request header. +- `body` — request body (JSON, etc.). +- `formData` — form-data body field (note: `form` is accepted as an alias + inside `swagger:route Parameters:` chunks; the lexer normalises it to + `formData` at the canonical surface). + +A non-matching value emits a context-invalid diagnostic; the parameter loses its +`in` and may end up incorrectly classified downstream. The keyword is +parameter-only — it has no meaning on a response header (the header name is the +location). + +## Field naming + +### `name` + +Sets the published name of **any** field it decorates, overriding the `json:` +tag / Go field name. It is the one canonical field-naming keyword and works at +every field site: a `swagger:model` property, an interface method, a +`swagger:parameters` field (the parameter name), and a `swagger:response` header +field (the `Headers` map key). Being structural, it is stripped from the +description rather than leaking into it. + +Precedence, most-explicit-wins and **identical in every context**: + +```text +name: keyword > swagger:name annotation > json: tag > Go field name +``` + +[`swagger:name`]({{% relref "/maintainers/annotations/swagger-name" %}}) is the +older annotation form — still honoured, and idiomatic on interface methods — but +`name:` is the universal keyword. Using `swagger:name` in a parameter or +response-header context (where `name:` is canonical) is inert and now emits a +`context-invalid` diagnostic pointing you at the keyword. + +## Wire serialisation + +### `collectionFormat` + +How an array value is serialised on the wire. Closed-vocab: + +- `csv` — comma-separated (default). +- `ssv` — space-separated. +- `tsv` — tab-separated. +- `pipes` — pipe-separated. +- `multi` — repeated `?key=val&key=val2` (query params only). + +Aliases: `collection format`, `collection-format`. Maps to +`parameter.collectionFormat` / `items.collectionFormat`. This is a +SimpleSchema-only concept — schema-level contexts ignore it (schemas serialise +via `application/json`). When the source value doesn't match the closed vocab, +the raw value is **preserved verbatim** on the parameter (so `pipe` as a typo for +`pipes` round-trips). + +## Response examples + +### `examples` + +Response-level examples on a `swagger:response` struct — a YAML map whose +first-level keys are **mime types** and whose values are the example payloads, +populating the OAS2 `Response.examples` field. This is the **plural**, +response-scoped keyword; contrast the **singular**, schema/param/header-scoped +[`example`]({{% relref "schema-validations-and-decorators#example" %}}) decorator. +The `swagger:operation` YAML body carries `examples` natively (it is unmarshalled +straight into the spec types); this keyword is the struct-`swagger:response` +counterpart. + +```go +// swagger:response widgetResponse +// +// examples: +// +// application/json: +// name: alice +// count: 3 +// application/xml: "alice" +type WidgetResponse struct { + // in: body + Body Widget `json:"body"` +} +``` + +See also [Spec metadata]({{% relref "spec-metadata" %}}) for the document-level +keywords that frame these operations. diff --git a/docs/doc-site/maintainers/keywords/routes-and-operations.md b/docs/doc-site/maintainers/keywords/routes-and-operations.md new file mode 100644 index 0000000..d7ab183 --- /dev/null +++ b/docs/doc-site/maintainers/keywords/routes-and-operations.md @@ -0,0 +1,170 @@ +--- +title: "Routes & operations" +weight: 30 +description: "Keywords carried in a swagger:route or swagger:operation block — the operation's transport metadata and its parameter and response bodies." +--- + +These keywords live inside a `swagger:route` or `swagger:operation` block. They +carry the operation's transport metadata — the URL schemes, the media types it +consumes and produces — and the two sub-language bodies that declare its +parameters and responses. Several of them double as document-wide defaults under +`swagger:meta` ([Spec metadata]({{% relref "spec-metadata" %}})), where an +operation-level value overrides the default. + +## Summary + +| Keyword | Aliases | Shape | Contexts | +|---------|---------|-------|----------| +| `schemes` | — | flex-list | meta, route, operation | +| `consumes` | — | flex-list | meta, route, operation | +| `produces` | — | flex-list | meta, route, operation | +| `responses` | — | sub-language (`: `) | route, operation | +| `parameters` | — | sub-language (`+ name:` chunks) | route, operation | +| `tags` | — | string list / tag objects | meta, route, operation | +| `deprecated` | — | boolean | operation, route, schema | +| `security` | — | YAML sequence (raw-block) | meta, route, operation | +| `externalDocs` | `external docs`, `external-docs` | `{description, url}` | meta, route, operation, schema | +| `extensions` | — | `x-*` YAML map | route, operation (cross-cutting) | + +The visiting rows are documented where they primarily apply: `deprecated` is +detailed under +[Schema validations & decorators]({{% relref "schema-validations-and-decorators#deprecated" %}}); +`security` (and its `securityDefinitions` catalogue) under +[Security]({{% relref "/maintainers/keywords/security" %}}); `externalDocs` and `extensions` under +[Spec metadata]({{% relref "spec-metadata" %}}). + +## Worked examples + +A `swagger:route` block — the path-line annotation plus its transport metadata +and a `Responses:` body — side by side with the path item it produces: + +{{< example + go="concepts/routes/routes.go" goregion="route" + json="concepts/routes/testdata/route.json" >}} + +The `swagger:operation` long form carries the same metadata in a YAML body, +including an inline `parameters` sequence: + +{{< example + go="concepts/routes/routes.go" goregion="operation" + json="concepts/routes/testdata/operation.json" >}} + +## Transport metadata + +### `schemes` + +Accepted URL schemes for the operation. Flexible list — comma inline, multi-line +bare, YAML `-` markers, or any combination all produce the same output +(`Schemes: http, https` ≡ a `- http` / `- https` block). See +[sub-languages §flex-lists]({{% relref "sub-languages" %}}) for the unified rule. + +Maps to `schemes` on the enclosing operation. It is also a **document default** +under `swagger:meta` (`spec.schemes`), where an operation-level value overrides +the meta-level one — see [Spec metadata]({{% relref "spec-metadata" %}}). + +### `consumes` / `produces` + +Media-type lists — the request body MIME types the operation `consumes` and the +response MIME types it `produces`. Same flex-list rule as `schemes`: comma +inline, multi-line bare, YAML `-` markers, or any combination. + +``` +Consumes: + - application/json + - application/xml + +Produces: application/json +``` + +Map to `consumes` / `produces` on the surrounding scope. Like `schemes`, both are +also `swagger:meta` document defaults overridden per operation. + +## Body sub-languages + +### `responses` + +Per-route / per-operation response declarations. The body is one response per +line in the form `: `, where `` is an HTTP status (or +`default`) and `` names the body schema and/or description: + +``` +Responses: + 200: body:User the requested user + 404: description: not found + default: response:genericError +``` + +The full per-line grammar lives at +[sub-languages §responses]({{% relref "sub-languages#responses" %}}). + +### `parameters` + +Per-route / per-operation parameter declarations. The body is a sequence of +`+ name:` chunks — the `+` is the chunk-start sigil (`-` is accepted as an +alias) — each chunk a small key/value block describing one parameter: + +``` +Parameters: + + name: id + in: path + type: integer + required: true + + name: limit + in: query + type: integer + default: 20 + minimum: 1 + maximum: 100 +``` + +The full per-chunk grammar lives at +[sub-languages §parameters]({{% relref "sub-languages#parameters" %}}). + +### `tags` + +Tag declarations whose shape depends on context: + +- In **`swagger:route` / `swagger:operation`** the body is a plain **string + list**. It is unioned and deduplicated with the tags written on the + annotation's header line, and the result lands on the operation's `tags`: + + ``` + Tags: + - pets + - store + ``` + +- In **`swagger:meta`** the body is instead a YAML **sequence of tag objects** + emitted into the spec's top-level `tags` — each with a `name`, an optional + `description`, a nested `externalDocs`, and any `x-*` vendor extensions: + + ``` + Tags: + - name: pets + description: Everything about your Pets + externalDocs: + description: Find out more + url: https://example.com/docs/pets + - name: store + x-display-name: Store + ``` + + The meta tag-objects form is also referenced from + [Spec metadata]({{% relref "spec-metadata" %}}). + +## Visiting keywords + +These keywords also appear in a route/operation block but are detailed on their +home page: + +- `deprecated` — marks the operation deprecated (native OAS 2.0 `deprecated`). + See [Schema validations & decorators]({{% relref "schema-validations-and-decorators#deprecated" %}}). +- `security` — the per-route / per-operation requirement list (an empty + `Security: []` on an operation is an explicit public opt-out). + See [Security]({{% relref "/maintainers/keywords/security" %}}). +- `externalDocs` — the operation's external-documentation pointer. + See [Spec metadata]({{% relref "spec-metadata" %}}). +- `extensions` — vendor `x-*` entries on the operation. + See [Spec metadata]({{% relref "spec-metadata" %}}). + + diff --git a/docs/doc-site/maintainers/keywords/schema-validations-and-decorators.md b/docs/doc-site/maintainers/keywords/schema-validations-and-decorators.md new file mode 100644 index 0000000..9240ad1 --- /dev/null +++ b/docs/doc-site/maintainers/keywords/schema-validations-and-decorators.md @@ -0,0 +1,207 @@ +--- +title: "Schema validations & decorators" +weight: 20 +description: "Keywords that constrain and decorate a model schema or struct field — bounds, lengths, patterns, enums, defaults, and structural markers." +--- + +These keywords decorate a `swagger:model` schema or any struct field doc comment. +The validations constrain a value; the decorators carry defaults, examples, and +structural markers. Several also apply to parameters and response headers — there +they ride the reduced SimpleSchema surface +([Parameters & responses]({{% relref "parameters-and-responses" %}})). + +## Summary + +| Keyword | Aliases | Shape | Contexts | +|---------|---------|-------|----------| +| `maximum` | `max` | number | param, header, schema, items | +| `minimum` | `min` | number | param, header, schema, items | +| `multipleOf` | `multiple of`, `multiple-of` | number | param, header, schema, items | +| `maxLength` | `max length`, `maxLen`, … | integer | param, header, schema, items | +| `minLength` | `min length`, `minLen`, … | integer | param, header, schema, items | +| `maxItems` | `max items`, `maximumItems`, … | integer | param, header, schema, items | +| `minItems` | `min items`, `minimumItems`, … | integer | param, header, schema, items | +| `maxProperties` | `max properties`, … | integer | schema | +| `minProperties` | `min properties`, … | integer | schema | +| `pattern` | — | string | param, header, schema, items | +| `patternProperties` | `pattern properties`, `pattern-properties` | string (regex) | schema | +| `additionalProperties` | `additional properties`, `additional-properties` | `true`/`false`/type | schema | +| `unique` | — | boolean | param, header, schema, items | +| `default` | — | raw-value | param, header, schema, items | +| `example` | — | raw-value | param, header, schema, items | +| `enum` | — | raw-value | param, header, schema, items | +| `required` | — | boolean | param, schema | +| `readOnly` | `read only`, `read-only` | boolean | schema | +| `discriminator` | — | boolean | schema | +| `deprecated` | — | boolean | operation, route, schema | + +The shared rows above (param/header/schema/items) are detailed here; on parameters +and headers they behave the same, with the OAS 2.0 SimpleSchema restrictions noted on +the [Parameters & responses]({{% relref "parameters-and-responses" %}}) page. +`collectionFormat` and `in`/`name` live there too. + +## Worked examples + +Every validation on a model's fields, side by side with the schema it produces: + +{{< example + go="concepts/validations/validations.go" goregion="field" + json="concepts/validations/testdata/field.json" >}} + +The object-validation keywords constrain the *map* of properties rather than named +fields: + +{{< example + go="concepts/validations/validations.go" goregion="object" + json="concepts/validations/testdata/object.json" >}} + +## Numeric validations + +Apply to numeric schema types (`integer`, `number`). On a typed schema with a +non-numeric type they emit `CodeShapeMismatch` and drop; on a typeless schema they +apply best-effort. + +### `maximum` / `minimum` + +Upper / lower bound on a numeric value (aliases `max` / `min`). The value may carry a +leading comparison operator that sets the exclusive/inclusive bound: + +- `maximum: 10` — inclusive (≤ 10); +- `maximum: <10` — exclusive (< 10); +- `maximum: <=10` / `maximum: =10` — inclusive. + +Map to `schema.maximum`/`exclusiveMaximum` and `schema.minimum`/`exclusiveMinimum`. + +### `multipleOf` + +Divisibility constraint; the value must be a positive number. Aliases `multiple of`, +`multiple-of`. Maps to `schema.multipleOf`. + +## Length, array & object validations + +`maxLength` / `minLength` apply only to **string**-typed schemas; `maxItems` / +`minItems` only to **array**-typed; `maxProperties` / `minProperties` / +`patternProperties` only to **object**-typed. The wrong pairing emits +`CodeShapeMismatch` and drops. The object keywords are additionally +**full-Schema-only** — no SimpleSchema (non-body param, header, items) form exists in +OAS 2.0, so on such a site they drop with `CodeUnsupportedInSimpleSchema`. + +### `maxLength` / `minLength` + +String length bounds. Many ergonomic aliases (`max length`, `max-length`, `maxLen`, +`maximumLength`, …; `min` likewise). Map to `schema.maxLength` / `schema.minLength`. + +### `maxItems` / `minItems` + +Array length bounds (aliases `max items`, `maximumItems`, …). Map to +`schema.maxItems` / `schema.minItems`. + +### `maxProperties` / `minProperties` + +Property-count bounds on an **object** schema (aliases `max properties`, …). Map to +`schema.maxProperties` / `schema.minProperties`. Schema-only. + +### `patternProperties` + +Constrains the **names** of properties on an object schema by regex. The argument is +one regex string; each line adds an entry to `schema.patternProperties` mapping the +regex to an empty value schema (`{}` — any value allowed). Repeated lines accumulate. +Aliases `pattern properties`, `pattern-properties`. The regex is RE2-hygiene-checked: +one that doesn't compile raises `CodeInvalidAnnotation` but is **preserved**. + +For **typed** value schemas (a regex → primitive or model `$ref`), use the +decl-level [`swagger:patternProperties`]({{% relref "/maintainers/annotations/swagger-patternproperties" %}}) +marker. `patternProperties` is JSON-Schema, beyond the Swagger 2.0 subset — see +[Maps & free-form objects]({{% relref "/tutorials/maps-and-free-form-objects" %}}). + +### `additionalProperties` + +Policy for keys beyond the named properties on an object schema: `true` (allow any), +`false` (close the object), or a **value type** (primitive / `[]T`, or a model name +→ `$ref`). On a map field it overrides the Go element schema; on a `$ref`'d field the +value rides an `allOf` sibling so the reference is kept. Aliases `additional +properties`, `additional-properties`. Lowest-priority and object-only — dropped with +`CodeShapeMismatch` on a non-object. The decl-level +[`swagger:additionalProperties`]({{% relref "/maintainers/annotations/swagger-additionalproperties" %}}) +marker does the same on a type. + +## Format + +### `pattern` + +A regex constraint on a string value, preserved verbatim on `schema.pattern` — +including backslash escapes (`\d`, `\.`, `\n` reach the spec as literal two-character +sequences). The grammar runs a best-effort RE2 compile; a failure surfaces +`CodeInvalidAnnotation` but the value still lands (downstream tools may use a wider +regex dialect). + +### `unique` + +Marks an array-typed schema as set-valued (no duplicates). Boolean. Maps to +`schema.uniqueItems`. + +## Schema decorators + +### `default` + +Default value for a schema or simple-schema field. Raw-value shape — the post-colon +text is captured verbatim and coerced against the resolved schema type at write time +(`ParseDefault` / `CoerceValue`). Single-line for primitives (`default: 1`), +multi-line bodies for complex literals: + +```go +// default: +// { "rps": 100, "burst": 200 } +``` + +### `example` + +An example value for the schema, surfaced in tooling. Same raw-value shape as +`default`. Maps to `schema.example` (or `parameter.example` for SimpleSchema). This +is the **singular**, schema-scoped keyword; for the **plural** response-scoped +[`examples`]({{% relref "parameters-and-responses#examples" %}}) (a map keyed by mime +type) see Parameters & responses. + +### `enum` + +A closed set of allowed values. Accepted forms: comma list (`enum: red, green`), +bracketed comma list (`enum: [red, green]`), JSON array (`enum: ["red","green"]`), or +a multi-line `-` list. Each element is coerced against the resolved type; maps to +`schema.enum`. + +For string enums driven by Go `const`s the +[`swagger:enum`]({{% relref "/maintainers/annotations/swagger-enum" %}}) annotation is +more idiomatic — it picks up the constant names + godoc and produces +`x-go-enum-desc`. The `enum:` keyword is the manual override. (Set +`SkipEnumDescriptions: true` to keep the const→value mapping on `x-go-enum-desc` only, +out of the description.) + +### `required` + +Marks a field as required. Boolean. + +- On a `swagger:model` field: adds the field name to the schema's `required` array. +- On a `swagger:parameters` field: sets `parameter.required`. +- On a `swagger:response` header: not applicable — silently dropped. + +### `readOnly` + +Marks a schema property read-only. Aliases `read only`, `read-only`. Maps to +`schema.readOnly`. Schema-only — inside a SimpleSchema context it drops with +`CodeUnsupportedInSimpleSchema`. + +### `discriminator` + +Marks the property as the discriminator for an `allOf` polymorphic schema. Boolean; +writes the property name onto the schema's `discriminator`. Schema-only. The property +should also be `required`. Subtypes that `allOf`-embed the base inherit it; each +subtype's discriminator value is its definition name. See +[Polymorphic models]({{% relref "/tutorials/polymorphic-models" %}}). + +### `deprecated` + +Marks the carrying entity deprecated. Boolean. On operations/routes it writes the +native OAS 2.0 `deprecated`; OAS 2.0 has no Schema-object `deprecated`, so on a model +or field it emits `x-deprecated: true`. A godoc `Deprecated:` paragraph is an exact +synonym recognised in any context — and is idiomatic on Go doc comments. Because it +carries intent, `x-deprecated` survives even under `SkipExtensions`. diff --git a/docs/doc-site/maintainers/keywords/security.md b/docs/doc-site/maintainers/keywords/security.md new file mode 100644 index 0000000..6242b24 --- /dev/null +++ b/docs/doc-site/maintainers/keywords/security.md @@ -0,0 +1,72 @@ +--- +title: "Security" +weight: 40 +description: "Keywords that wire authentication — the requirements that gate a spec, route, or operation, and the scheme catalogue declared once in meta." +--- + +Two keywords carry authentication into the spec. `security` lists the +**requirements** that gate the document, a route, or a single operation; +`securityDefinitions` is the **scheme catalogue** — declared once in +`swagger:meta` and referenced by name from every requirement. A requirement is +only meaningful when the scheme it names is defined, so the two are almost always +authored together (see [Spec metadata]({{% relref "spec-metadata" %}}) for the +rest of the `swagger:meta` surface and [Routes & operations]({{% relref "/maintainers/keywords/routes-and-operations" %}}) +for where per-route requirements live). + +## Summary + +| Keyword | Aliases | Shape | Contexts | +|---------|---------|-------|----------| +| `security` | — | YAML sequence (raw-block) | meta, route, operation | +| `securityDefinitions` | `security definitions`, `security-definitions` | YAML map (raw-block) | meta | + +## Worked example + +The scheme catalogue and the document-wide default requirement, declared once in +the package `swagger:meta` block — the `schemes` golden captures both +`securityDefinitions` and the top-level `security`: + +{{< example + go="concepts/security/doc.go" goregion="meta" + json="concepts/security/testdata/schemes.json" >}} + +A route then overrides that default with its own `Security:` requirement — here +`oauth2` with the `read` and `write` scopes: + +{{< example + go="concepts/security/routes.go" goregion="routes" + json="concepts/security/testdata/route.json" >}} + +## Keyword details + +### `security` + +A YAML sequence of **requirement objects** parsed from the `Security:` body. The +semantics are OAS 2.0: + +- multiple keys **within one sequence item** are **ANDed** — all of those schemes + are required together (`{api_key, oauth2}` in one item); +- **separate items** are **ORed** — satisfying any one item grants access; +- a scheme's value is its **scope list**, a flow (`[read, write]`) or block list. + For non-scoped schemes (`apiKey`, `basic`) the list is empty (`api_key: []`), + meaning the scheme is required with no scopes; +- an empty top-level `Security: []` on an **operation** emits an explicit empty + requirement — an intentional public opt-out that overrides the document-wide + default rather than inheriting it. + +A bare top-level mapping (`api_key:` / `oauth2: read, write`, comma-split scopes) +is still read as one OR requirement per key for back-compatibility. Maps to +`security` on the enclosing object. Legal in `swagger:meta` (the document +default), `swagger:route`, and `swagger:operation`. The full per-line body +grammar lives at +[sub-languages §security requirements]({{% relref "sub-languages#security-requirements" %}}). + +### `securityDefinitions` + +A YAML map, parsed directly into the `spec.securityDefinitions` shape — each entry +is a named scheme (`apiKey`, `oauth2`, `basic`) with its OAS 2.0 fields (`type`, +`in`, `name`, `flow`, `authorizationUrl`, `tokenUrl`, `scopes`, …); see +[OAS v2 §securityDefinitionsObject](https://swagger.io/specification/v2/#securityDefinitionsObject). +Aliases `security definitions`, `security-definitions`. **Meta-only** — the +scheme catalogue is declared once at the top of the document and referenced by +name from every `security` requirement. Its detail anchor is `#securitydefinitions`. diff --git a/docs/doc-site/maintainers/keywords/spec-metadata.md b/docs/doc-site/maintainers/keywords/spec-metadata.md new file mode 100644 index 0000000..f3b3fa0 --- /dev/null +++ b/docs/doc-site/maintainers/keywords/spec-metadata.md @@ -0,0 +1,167 @@ +--- +title: "Spec metadata" +weight: 50 +description: "Top-of-document keywords authored under swagger:meta — version, host, base path, license, contact, terms of service — plus the cross-cutting vendor-extension and external-docs keywords." +--- + +These keywords author the spec's top-level fields. Most live in the package doc +comment carrying the `swagger:meta` block; a couple are cross-cutting and merely +have their *home* here — `extensions` lands on whatever scope it decorates, and +`externalDocs` rides meta, operations, schemas, and struct fields alike. The +remaining document-level concerns (`schemes`/`consumes`/`produces`, `security`/ +`securityDefinitions`, the meta `tags` form) are owned by sibling pages and only +visit this one. + +## Summary + +| Keyword | Aliases | Shape | Home | +|---------|---------|-------|------| +| `version` | — | string | here | +| `host` | — | string | here | +| `basePath` | `base path`, `base-path` | string | here | +| `license` | — | `Name [URL]` | here | +| `contact` | `contact info`, `contact-info` | `Name [URL]` | here | +| `tos` | `terms of service`, `terms-of-service`, `termsOfService` | prose | here | +| `infoExtensions` | `info extensions`, `info-extensions` | `x-*` YAML map | here | +| `extensions` | — | `x-*` YAML map | here (cross-cutting) | +| `externalDocs` | `external docs`, `external-docs` | `{description, url}` | here (cross-cutting) | +| `schemes` | — | flex-list | {{% relref "/maintainers/keywords/routes-and-operations" %}} | +| `consumes` / `produces` | — | flex-list | {{% relref "/maintainers/keywords/routes-and-operations" %}} | +| `security` | — | YAML | {{% relref "/maintainers/keywords/security" %}} | +| `securityDefinitions` | `security definitions`, `security-definitions` | YAML map | {{% relref "/maintainers/keywords/security" %}} | +| `tags` | — | YAML sequence | {{% relref "/maintainers/keywords/routes-and-operations#tags" %}} | + +The visiting rows are documented where they primarily apply: `schemes`, +`consumes`, and `produces` are document-wide defaults overridden per operation +([Routes & operations]({{% relref "/maintainers/keywords/routes-and-operations" %}})); `security` and +`securityDefinitions` are detailed under [Security]({{% relref "/maintainers/keywords/security" %}}); +the `swagger:meta` tag-objects form of `tags` is described under +[Routes & operations]({{% relref "/maintainers/keywords/routes-and-operations#tags" %}}). + +## Worked example + +A complete `swagger:meta` block, side by side with the document-level spec it +produces: + +{{< example + go="concepts/meta/doc.go" goregion="meta" + json="concepts/meta/testdata/meta.json" >}} + +## Meta single-line keywords + +Single-line keywords under `swagger:meta`. The value is taken as-is from the +post-colon string. + +### `version` + +API version string. Maps to `info.version`. + +### `host` + +Default host for the API. Defaults to `localhost` when empty. Maps to +`spec.host`. + +### `basePath` + +URL base path applied to every route. Maps to `spec.basePath`. Aliases: +`base path`, `base-path`. + +### `license` + +License declaration, in two accepted forms: + +``` +License: Apache 2.0 https://www.apache.org/licenses/LICENSE-2.0.html +``` + +The trailing token starting with a URL scheme becomes `license.url`; the prefix +becomes `license.name`. A bare name with no URL is accepted too. Maps to +`info.license`. + +### `contact` + +Contact declaration. The author writes a `Name URL` triple, in any +order; the grammar recognises: + +- `Name ` — Go's `net/mail.ParseAddress` form; +- `Name https://example.com` — same, plus a trailing URL; +- just a URL, with no name. + +Aliases: `contact info`, `contact-info`. Maps to `info.contact`. + +## Document-level keywords + +### `tos` + +Terms-of-service prose paragraph. The multi-line body is joined with `\n` after +dropping whitespace-only lines. Aliases: `terms of service`, `terms-of-service`, +`termsOfService`. Maps to `info.termsOfService`. Meta-only. + +### `infoExtensions` + +Vendor-extension declarations as a YAML map, landed on `info.extensions`. Keys +must start with `x-` or `X-`; a non-`x-*` key emits `CodeInvalidAnnotation` and +drops. Meta-only. Aliases: `info extensions`, `info-extensions`. + +``` +InfoExtensions: + x-logo: + url: https://example.com/logo.png + altText: Example +``` + +For the same map on the surrounding scope rather than `info`, use +[`extensions`](#extensions). + +### `extensions` + +Vendor-extension declarations as a YAML map, landed on the **surrounding scope** +rather than on `info`: `spec.extensions`, `operation.extensions`, +`schema.extensions`, `parameter.extensions`, `header.extensions`, and so on — +including on parameters and response headers. Keys must start with `x-` or `X-`; +a non-`x-*` key emits `CodeInvalidAnnotation` and drops. + +``` +Extensions: + x-internal-id: 42 + x-feature-flags: + - alpha + - beta + x-nested: + enabled: true + rate: 0.5 +``` + +This keyword is cross-cutting — it is documented here as its home, but applies +wherever a YAML body is parsed. For the meta-only `info.extensions` variant see +[`infoExtensions`](#infoextensions). + +### `externalDocs` + +External-documentation pointer as a YAML map with `description` and `url` keys. +Aliases: `external docs`, `external-docs`. + +Emitted on: + +- **`swagger:meta`** → the top-level `externalDocs` object (and, nested under a + `Tags:` entry, that tag's `externalDocs`); +- **`swagger:route` / `swagger:operation`** → the operation's `externalDocs`; +- **`swagger:model`** (and any full Schema, e.g. a body parameter's schema) → + the schema's `externalDocs`; +- a **struct field** → the property's `externalDocs`. On a `$ref`'d field (whose + property is a bare `$ref`) it is lifted onto the wrapping `allOf` compound, + alongside the field's `description` and `x-*` siblings. + +An empty block (no `description`/`url`) is skipped rather than emitting a bare +`externalDocs: {}`. It is a **full-Schema-only** keyword: on a SimpleSchema site +(a non-body parameter, response header, or items chain) it drops with a +`CodeUnsupportedInSimpleSchema` diagnostic. + +``` +ExternalDocs: + description: Reference documentation + url: https://example.com/docs +``` + +Like [`extensions`](#extensions), this keyword is cross-cutting; it is +documented here as its home. diff --git a/docs/doc-site/tutorials/document-metadata.md b/docs/doc-site/tutorials/document-metadata.md index d8b3c52..0d11871 100644 --- a/docs/doc-site/tutorials/document-metadata.md +++ b/docs/doc-site/tutorials/document-metadata.md @@ -36,7 +36,7 @@ the tags your routes reference (above, `pets` and `store`). For the full meta keyword surface (security definitions, external docs, extensions, terms of service), see the [`swagger:meta` reference]({{% relref "/maintainers/annotations/swagger-meta" %}}) -and the [meta keywords]({{% relref "/maintainers/keywords#meta-single-line-keywords" %}}). +and the [meta keywords]({{% relref "/maintainers/keywords/spec-metadata#meta-single-line-keywords" %}}). ## Security diff --git a/docs/doc-site/tutorials/maps-and-free-form-objects.md b/docs/doc-site/tutorials/maps-and-free-form-objects.md index f656d8f..265dc27 100644 --- a/docs/doc-site/tutorials/maps-and-free-form-objects.md +++ b/docs/doc-site/tutorials/maps-and-free-form-objects.md @@ -91,7 +91,7 @@ sibling so the reference is preserved: `patternProperties` constrains *extra* keys by a name regex rather than allowing all of them. The regex-only -[`patternProperties:` keyword]({{% relref "/maintainers/keywords#patternproperties" %}}) +[`patternProperties:` keyword]({{% relref "/maintainers/keywords/schema-validations-and-decorators#patternproperties" %}}) maps a pattern to an empty (any-value) schema. The decl-level `swagger:patternProperties "": , …` marker is the **typed** counterpart — each quoted regex pairs with a value spec (a primitive, or a model name that diff --git a/docs/doc-site/tutorials/model-definitions.md b/docs/doc-site/tutorials/model-definitions.md index 7bbc798..4e67700 100644 --- a/docs/doc-site/tutorials/model-definitions.md +++ b/docs/doc-site/tutorials/model-definitions.md @@ -110,7 +110,7 @@ annotation publishes it as `jsonClass` instead. {{% notice style="note" %}} `swagger:name` is the legacy annotation form. The -[`name:` keyword]({{% relref "/maintainers/keywords#name" %}}) is the +[`name:` keyword]({{% relref "/maintainers/keywords/parameters-and-responses#name" %}}) is the canonical, universal equivalent — it renames a property here exactly the same way, and is the *only* form that also works on parameters and response headers. Precedence: `name:` > `swagger:name` > `json:` tag > Go diff --git a/docs/doc-site/tutorials/polymorphic-models.md b/docs/doc-site/tutorials/polymorphic-models.md index d321e8c..7db8a5c 100644 --- a/docs/doc-site/tutorials/polymorphic-models.md +++ b/docs/doc-site/tutorials/polymorphic-models.md @@ -58,5 +58,5 @@ on the base; it is inherited by every subtype through the `$ref`. `swagger:allOf` composition this builds on, and the rest of the model surface. - [Routes & operations]({{% relref "/tutorials/routes-and-operations" %}}) — return a base type and let the discriminator carry the subtype. -- [`discriminator` keyword]({{% relref "/maintainers/keywords#discriminator" %}}) +- [`discriminator` keyword]({{% relref "/maintainers/keywords/schema-validations-and-decorators#discriminator" %}}) and [`swagger:allOf` reference]({{% relref "/maintainers/annotations/swagger-allof" %}}). diff --git a/docs/doc-site/tutorials/security.md b/docs/doc-site/tutorials/security.md index 27e9443..00dc22a 100644 --- a/docs/doc-site/tutorials/security.md +++ b/docs/doc-site/tutorials/security.md @@ -25,7 +25,7 @@ operations that do not state their own. The scheme `type` drives the rest: `apiKey` needs `in` + `name`, `oauth2` needs a `flow` (and the URLs/`scopes` it implies), `basic` needs nothing more. The full scheme surface is in the -[`securityDefinitions` reference]({{% relref "/maintainers/keywords#securitydefinitions" %}}). +[`securityDefinitions` reference]({{% relref "/maintainers/keywords/security#securitydefinitions" %}}). ## Require a scheme on a route From 32c9ee1bcdd7887b6de3ea3e7977fe14be75f5c2 Mon Sep 17 00:00:00 2001 From: Frederic BIDON Date: Sat, 27 Jun 2026 23:47:29 +0200 Subject: [PATCH 4/5] docs(doc-site): railroad (syntax) diagrams for the EBNF grammar MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add an opt-in 'railroad' shortcode that renders an ISO-14977 EBNF production as a Mermaid railroad diagram beside the plain EBNF text it complements (the text stays the authoritative grammar). Mermaid 11.16's railroad support is beta and its renderer collides when multiple diagrams are batched, so each is rendered in isolation via mermaid.render() with a unique id, sequentially. The script self-gates (loads Mermaid only on pages that contain a diagram), auto-quotes UPPERCASE lexer terminals so they get literal styling, and tracks the site's light/dark variant (re-rendering on Relearn's themeVariantLoaded). Mermaid is served as a local static asset so the rendered site makes no runtime CDN calls. Mermaid 11.16.0 is NOT committed — like the Relearn theme it is fetched per-environment (the CI 'Initialize theme and assets' step in update-doc.yml; locally per the doc-site README) and gitignored. Render diagrams for each parser family — top-level dispatch, schema body (validation + decorators expanded to the keyword level), operation (route / inline-operation / args), meta, and the classifier choice — using multi-rule blocks so block titles and their keyword detail both show. Co-Authored-By: Claude Opus 4.8 Signed-off-by: Frederic BIDON --- .github/workflows/update-doc.yml | 12 ++- docs/doc-site/maintainers/grammar.md | 53 ++++++++++++ hack/doc-site/hugo/README.md | 11 ++- .../hugo/layouts/partials/custom-header.html | 83 +++++++++++++++++++ .../hugo/layouts/shortcodes/railroad.html | 26 ++++++ hack/doc-site/hugo/themes/.gitignore | 1 + .../codescan-static/js/mermaid11/README.txt | 13 +++ 7 files changed, 194 insertions(+), 5 deletions(-) create mode 100644 hack/doc-site/hugo/layouts/shortcodes/railroad.html create mode 100644 hack/doc-site/hugo/themes/codescan-static/js/mermaid11/README.txt diff --git a/.github/workflows/update-doc.yml b/.github/workflows/update-doc.yml index fd951ba..7f97aee 100644 --- a/.github/workflows/update-doc.yml +++ b/.github/workflows/update-doc.yml @@ -53,18 +53,26 @@ jobs: git fetch upstream --prune --update-shallow --tags 'refs/tags/*:refs/tags/*' git fetch origin --prune --update-shallow --tags 'refs/tags/*:refs/tags/*' - - name: Initialize theme + name: Initialize theme and assets env: RELEARN_VERSION: 9.0.3 + MERMAID_VERSION: 11.16.0 run: | cd hack/doc-site/hugo - # Clone theme + # Clone theme (not committed) curl -sL -o relearn.tgz https://github.com/McShelby/hugo-theme-relearn/archive/refs/tags/"${RELEARN_VERSION}".tar.gz tar xf relearn.tgz rm -rf themes/hugo-relearn mv "hugo-theme-relearn-${RELEARN_VERSION}" hugo-relearn mv hugo-relearn themes/ + + # Fetch vendored Mermaid (not committed) — the railroad shortcode needs + # >= 11.16, newer than the theme's bundled Mermaid; served as a local + # static asset so the rendered site makes no runtime CDN calls. + mkdir -p themes/codescan-static/js/mermaid11 + curl -sL -o themes/codescan-static/js/mermaid11/mermaid.min.js \ + https://cdn.jsdelivr.net/npm/mermaid@"${MERMAID_VERSION}"/dist/mermaid.min.js - name: Prepare config run: | diff --git a/docs/doc-site/maintainers/grammar.md b/docs/doc-site/maintainers/grammar.md index 5c2d87f..a9bede3 100644 --- a/docs/doc-site/maintainers/grammar.md +++ b/docs/doc-site/maintainers/grammar.md @@ -263,6 +263,14 @@ AnnotatedBlock = SchemaBlock UnboundBlock = [ Description ] , UnboundBlockBody ; ``` +{{< railroad >}} +AnnotatedBlock = SchemaBlock | OperationFamilyBlock | MetaBlock | ClassifierBlock ; +SchemaBlock = SchemaAnnotation , [ Title ] , [ Description ] , SchemaAnnotationBody ; +OperationFamilyBlock = RouteBlock | InlineOperationBlock ; +MetaBlock = ANN_META , [ Title ] , [ Description ] , MetaBody ; +ClassifierBlock = StrfmtBlock | AliasBlock | AllOfBlock | EnumBlock | IgnoreBlock | DefaultClassifierBlock | TypeBlock | FileBlock ; +{{< /railroad >}} + The dispatcher reads the first `ANN_*` terminal; its identity selects the family. If no annotation appears, the input is an `UnboundBlock` — typically a Go struct field with description-only @@ -335,6 +343,18 @@ DiscriminatorLine = KW_DISCRIMINATOR , BOOL_VALUE ; DeprecatedLine = KW_DEPRECATED , BOOL_VALUE ; ``` +{{< railroad >}} +SchemaBodyItem = Validation | SchemaDecorator | ExtensionsBlock | ExternalDocsBlock | BLANK ; +Validation = NumericValidation | StringValidation | ArrayValidation | EnumValidation | RequiredLine | ReadOnlyLine ; +NumericValidation = ( KW_MAXIMUM | KW_MINIMUM | KW_MULTIPLE_OF ) , NUMBER_VALUE ; +StringValidation = ( KW_PATTERN , STRING_VALUE ) | ( ( KW_MAX_LENGTH | KW_MIN_LENGTH ) , INT_VALUE ) ; +ArrayValidation = ( ( KW_MAX_ITEMS | KW_MIN_ITEMS ) , INT_VALUE ) | ( KW_UNIQUE , BOOL_VALUE ) | ( KW_COLLECTION_FORMAT , ENUM_OPTION_VALUE ) ; +EnumValidation = RAW_VALUE_ENUM ; +RequiredLine = KW_REQUIRED , BOOL_VALUE ; +ReadOnlyLine = KW_READ_ONLY , BOOL_VALUE ; +SchemaDecorator = RAW_VALUE_DEFAULT | RAW_VALUE_EXAMPLE | ( KW_DISCRIMINATOR , BOOL_VALUE ) | ( KW_DEPRECATED , BOOL_VALUE ) ; +{{< /railroad >}} + ### Operation family `swagger:route` and `swagger:operation` are distinct block productions @@ -383,6 +403,20 @@ OperationRawBlock = RAW_BLOCK_CONSUMES | RAW_BLOCK_PARAMETERS ; ``` +{{< railroad >}} +RouteBlock = ANN_ROUTE , OperationArgs , [ Title ] , [ Description ] , RouteBody ; +{{< /railroad >}} + +{{< railroad >}} +InlineOperationBlock = ANN_OPERATION , OperationArgs , [ Title ] , [ Description ] , InlineOperationBody ; +{{< /railroad >}} + +_…where both share the header arguments:_ + +{{< railroad >}} +OperationArgs = HTTP_METHOD , URL_PATH , { IDENT_NAME } , IDENT_NAME ; +{{< /railroad >}} + The ` swagger:route ...` godoc-prefix exception (which allows a leading Go identifier on the route annotation line) is absorbed by the lexer; the EBNF sees a plain `ANN_ROUTE`. @@ -420,6 +454,13 @@ MetaRawBlock = RAW_BLOCK_CONSUMES | RAW_BLOCK_TOS ; ``` +{{< railroad >}} +MetaBlock = ANN_META , [ Title ] , [ Description ] , MetaBody ; +MetaBodyItem = MetaKeyword | MetaRawBlock | ExtensionsBlock | InfoExtensionsBlock | ExternalDocsBlock ; +MetaKeyword = ( ( KW_VERSION | KW_HOST | KW_BASE_PATH | KW_LICENSE | KW_CONTACT ) , STRING_VALUE ) | ( KW_SCHEMES , COMMA_LIST_VALUE ) ; +MetaRawBlock = RAW_BLOCK_CONSUMES | RAW_BLOCK_PRODUCES | RAW_BLOCK_SCHEMES | RAW_BLOCK_SECURITY | RAW_BLOCK_SECURITY_DEFINITIONS | RAW_BLOCK_TOS ; +{{< /railroad >}} + ### Classifier family Single-purpose annotations that classify the surrounding declaration @@ -445,6 +486,18 @@ TypeBlock = ANN_TYPE , TYPE_REF , [ Title ] , [ Description ] ; FileBlock = ANN_FILE , [ Title ] , [ Description ] ; ``` +{{< railroad >}} +ClassifierBlock = StrfmtBlock | AliasBlock | AllOfBlock | EnumBlock | IgnoreBlock | DefaultClassifierBlock | TypeBlock | FileBlock ; +StrfmtBlock = ANN_STRFMT , IDENT_NAME , [ Title ] , [ Description ] ; +AliasBlock = ANN_ALIAS , [ IDENT_NAME ] , [ Title ] , [ Description ] ; +AllOfBlock = ANN_ALLOF , [ Title ] , [ Description ] ; +EnumBlock = ANN_ENUM , [ IDENT_NAME ] , [ Title ] , [ Description ] ; +IgnoreBlock = ANN_IGNORE , [ Title ] , [ Description ] ; +DefaultClassifierBlock = ANN_DEFAULT , [ Title ] , [ Description ] ; +TypeBlock = ANN_TYPE , TYPE_REF , [ Title ] , [ Description ] ; +FileBlock = ANN_FILE , [ Title ] , [ Description ] ; +{{< /railroad >}} + Classifiers are stateless markers — they carry no validation body of their own. The surrounding declaration's other annotations (or the absence thereof) determine where the classification lands. diff --git a/hack/doc-site/hugo/README.md b/hack/doc-site/hugo/README.md index 4af6f7a..7b17ec4 100644 --- a/hack/doc-site/hugo/README.md +++ b/hack/doc-site/hugo/README.md @@ -63,9 +63,14 @@ The script: 2. Renders `codescan.yaml` from `codescan.yaml.template` 3. Starts `hugo server` on with live reload -Requires `hugo` (extended, ≥ v0.150) and `git` on `PATH`. The Relearn theme is -not committed — download it once into `themes/hugo-relearn` (see the CI -workflow for the exact release used). +Requires `hugo` (extended, ≥ v0.150) and `git` on `PATH`. Two third-party assets +are **not committed** — download them once (see the CI `update-doc.yml` workflow +for the exact pinned versions): + +- the Relearn theme into `themes/hugo-relearn`; +- Mermaid ≥ 11.16 into `themes/codescan-static/js/mermaid11/mermaid.min.js` (the + `railroad` shortcode needs it; served locally so the rendered site makes no + runtime CDN calls). ## Configuration diff --git a/hack/doc-site/hugo/layouts/partials/custom-header.html b/hack/doc-site/hugo/layouts/partials/custom-header.html index 7aef11f..a2e1d2e 100644 --- a/hack/doc-site/hugo/layouts/partials/custom-header.html +++ b/hack/doc-site/hugo/layouts/partials/custom-header.html @@ -183,3 +183,86 @@ }); })(); + + + diff --git a/hack/doc-site/hugo/layouts/shortcodes/railroad.html b/hack/doc-site/hugo/layouts/shortcodes/railroad.html new file mode 100644 index 0000000..8307bde --- /dev/null +++ b/hack/doc-site/hugo/layouts/shortcodes/railroad.html @@ -0,0 +1,26 @@ +{{- /* + railroad: render an EBNF production as a Mermaid railroad (syntax) diagram, + beside the plain ```ebnf``` text it complements (decorative — the EBNF text + is always the authoritative grammar). + + Usage — wrap one or more ISO-14977 productions: + + {{< railroad >}} + ModelAnnotation = ANN_MODEL , [ IDENT_NAME ] ; + {{< /railroad >}} + + The raw EBNF is carried verbatim in a + + diff --git a/hack/doc-site/hugo/themes/.gitignore b/hack/doc-site/hugo/themes/.gitignore index a8a17ed..37d8974 100644 --- a/hack/doc-site/hugo/themes/.gitignore +++ b/hack/doc-site/hugo/themes/.gitignore @@ -1 +1,2 @@ hugo-relearn +codescan-static/js/mermaid11/mermaid.min.js diff --git a/hack/doc-site/hugo/themes/codescan-static/js/mermaid11/README.txt b/hack/doc-site/hugo/themes/codescan-static/js/mermaid11/README.txt new file mode 100644 index 0000000..8cb34cb --- /dev/null +++ b/hack/doc-site/hugo/themes/codescan-static/js/mermaid11/README.txt @@ -0,0 +1,13 @@ +mermaid.min.js (Mermaid 11.16.0 UMD, dist/mermaid.min.js) is NOT committed. + +Like the Relearn theme, it is fetched per-environment: + - CI: the "Initialize theme and assets" step in .github/workflows/update-doc.yml + - local: see hack/doc-site/hugo/README.md + +Why vendored at all: the `railroad` shortcode (layouts/partials/custom-header.html) +needs Mermaid >= 11.16 — railroad diagrams are beta and newer than the theme's +bundled Mermaid. Serving it as a local static asset keeps the rendered site free of +runtime CDN calls. MIT License — https://github.com/mermaid-js/mermaid + +Fetch it with: + curl -sL -o mermaid.min.js https://cdn.jsdelivr.net/npm/mermaid@11.16.0/dist/mermaid.min.js From 05d042bb9dcfeefbcd35ef15eecabed87e32f815 Mon Sep 17 00:00:00 2001 From: Frederic BIDON Date: Sun, 28 Jun 2026 00:41:56 +0200 Subject: [PATCH 5/5] docs(doc-site): document swagger:title / swagger:description annotations swagger:title and swagger:description are simple but first-class schema- family override annotations (AnnTitle / AnnDescription), yet the reference omitted them. Add them so the annotation vocabulary is complete: - grammar.md: ANN_TITLE / ANN_DESCRIPTION terminals, the two SchemaAnnotation productions, and a note on the override semantics (schema-parser dispatch, the | literal body, CodeEmptyOverride, context rejection on params/headers). - new per-annotation leaf pages swagger-title.md (weight 165) and swagger-description.md (weight 45), each with a live witness-backed example (shaping/overrides for the plain override, shaping/markdowndesc for the | literal block). - the section landing (now nineteen annotations; both added to the compatibility matrix) and the at-a-glance annotation index. Co-Authored-By: Claude Opus 4.8 Signed-off-by: Frederic BIDON --- docs/doc-site/annotation-index/_index.md | 2 + .../maintainers/annotations/_index.md | 9 ++- .../annotations/swagger-description.md | 64 +++++++++++++++++++ .../maintainers/annotations/swagger-title.md | 52 +++++++++++++++ docs/doc-site/maintainers/grammar.md | 31 ++++++--- 5 files changed, 147 insertions(+), 11 deletions(-) create mode 100644 docs/doc-site/maintainers/annotations/swagger-description.md create mode 100644 docs/doc-site/maintainers/annotations/swagger-title.md diff --git a/docs/doc-site/annotation-index/_index.md b/docs/doc-site/annotation-index/_index.md index adca711..1f02644 100644 --- a/docs/doc-site/annotation-index/_index.md +++ b/docs/doc-site/annotation-index/_index.md @@ -16,6 +16,7 @@ tutorial that shows the annotation as runnable Go next to the spec it produces; | `swagger:alias` *(deprecated)* | type alias | **no effect** — alias rendering is controlled by Go aliases + options | [how-to]({{% relref "alias-rendering" %}}) | [reference]({{% relref "/maintainers/annotations/swagger-alias" %}}) | | `swagger:allOf` | embedded field / struct | an `allOf` composition | [example]({{% relref "/tutorials/model-definitions#swaggerallof" %}}) | [reference]({{% relref "/maintainers/annotations/swagger-allof" %}}) | | `swagger:default` | value / field doc | a default-value anchor | [example]({{% relref "/tutorials/examples-and-defaults#swaggerdefault" %}}) | [reference]({{% relref "/maintainers/annotations/swagger-default" %}}) | +| `swagger:description` | type / field / response doc | overrides the `description` (verbatim body with `\|`) | [how-to]({{% relref "overriding-titles-and-descriptions" %}}) | [reference]({{% relref "/maintainers/annotations/swagger-description" %}}) | | `swagger:enum` | named type | an `enum` array (+ `x-go-enum-desc`) | [example]({{% relref "/tutorials/model-definitions#swaggerenum" %}}) | [reference]({{% relref "/maintainers/annotations/swagger-enum" %}}) | | `swagger:file` | param / response field | `{type: file}` | [example]({{% relref "/tutorials/routes-and-operations#swaggerfile" %}}) | [reference]({{% relref "/maintainers/annotations/swagger-file" %}}) | | `swagger:ignore` | type / field doc | excludes the declaration | [example]({{% relref "/tutorials/model-definitions#swaggerignore" %}}) | [reference]({{% relref "/maintainers/annotations/swagger-ignore" %}}) | @@ -28,6 +29,7 @@ tutorial that shows the annotation as runnable Go next to the spec it produces; | `swagger:response` | struct declaration | a `responses` entry | [example]({{% relref "/tutorials/routes-and-operations#swaggerresponse" %}}) | [reference]({{% relref "/maintainers/annotations/swagger-response" %}}) | | `swagger:route` | func / var doc | a `paths` entry + operation | [example]({{% relref "/tutorials/routes-and-operations#swaggerroute" %}}) | [reference]({{% relref "/maintainers/annotations/swagger-route" %}}) | | `swagger:strfmt` | type declaration | `{type: string, format: …}` at every use | [example]({{% relref "/tutorials/model-definitions#swaggerstrfmt" %}}) | [reference]({{% relref "/maintainers/annotations/swagger-strfmt" %}}) | +| `swagger:title` | type / field doc | overrides the `title` | [how-to]({{% relref "overriding-titles-and-descriptions" %}}) | [reference]({{% relref "/maintainers/annotations/swagger-title" %}}) | | `swagger:type` | type / field doc | overrides the inferred Swagger type | [example]({{% relref "/tutorials/model-definitions#swaggertype" %}}) | [reference]({{% relref "/maintainers/annotations/swagger-type" %}}) | ## Keywords, not annotations diff --git a/docs/doc-site/maintainers/annotations/_index.md b/docs/doc-site/maintainers/annotations/_index.md index 78a1c8c..8095e61 100644 --- a/docs/doc-site/maintainers/annotations/_index.md +++ b/docs/doc-site/maintainers/annotations/_index.md @@ -12,7 +12,7 @@ is a route handler", "this is meta-information about the API" — and opens the door for [keywords]({{% relref "keywords" %}}) inside the same comment block. -There are seventeen annotations. They divide cleanly by what they +There are nineteen annotations. They divide cleanly by what they attach to: - **Spec-level**: `swagger:meta`. @@ -21,8 +21,9 @@ attach to: `swagger:additionalProperties`, `swagger:patternProperties`. - **Operation declarations**: `swagger:route`, `swagger:operation`. - **Companion declarations**: `swagger:parameters`, `swagger:response`. -- **Local hints**: `swagger:ignore`, `swagger:name`, `swagger:type`, - `swagger:file`, `swagger:default`. +- **Local hints & overrides**: `swagger:ignore`, `swagger:name`, + `swagger:title`, `swagger:description`, `swagger:type`, `swagger:file`, + `swagger:default`. This section is the **author-first reference**. Each annotation has its own page covering what it produces, where it goes, its EBNF-like @@ -135,6 +136,8 @@ contracts, and each annotation's own page for the detail. | `swagger:response` | ✅ (on header fields) | ✅ (on body field) | ✅ (body/header) | — | — | — | — | | `swagger:ignore` | — | — | — | — | — | — | — | | `swagger:name` | — | — | — | — | — | — | — | +| `swagger:title` | — | ✅ (override) | — | — | — | — | — | +| `swagger:description` | — | ✅ (override) | — | ✅ (body/header) | — | — | — | | `swagger:type` | — | — | — | — | — | — | — | | `swagger:additionalProperties` | — | ✅ (object schema) | — | — | — | — | — | | `swagger:patternProperties` | — | ✅ (object schema) | — | — | — | — | — | diff --git a/docs/doc-site/maintainers/annotations/swagger-description.md b/docs/doc-site/maintainers/annotations/swagger-description.md new file mode 100644 index 0000000..5a73947 --- /dev/null +++ b/docs/doc-site/maintainers/annotations/swagger-description.md @@ -0,0 +1,64 @@ +--- +title: "swagger:description" +weight: 45 +description: "Overrides the godoc-derived description on a model, field, response, or header." +--- + +## Usage + +```goish +// swagger:description (single line, or a blank-terminated body) +// swagger:description | (opens a verbatim literal markdown block) +``` + +## What it does + +Replaces the godoc-derived `description` on a schema with explicit text. + +By default a description comes from a declaration's doc comment; +`swagger:description` overrides it when the godoc prose isn't what you want to +publish. It is a schema-family **override** — a sibling of +[`swagger:title`]({{% relref "swagger-title" %}}). + +A trailing `|` opens a **verbatim literal markdown block**: the body is captured +exactly — blank lines, indentation, and table pipes preserved — until the next +line-leading annotation or end of comment. See +[Markdown descriptions]({{% relref "markdown-descriptions" %}}). + +## Where it goes + +On a type (model) doc comment, a struct-field doc comment, a `swagger:response` +struct, or a response header field. + +## Grammar (EBNF) + +```ebnf +DescriptionAnnotation = ANN_DESCRIPTION , RAW_VALUE ; +``` + +`RAW_VALUE` is the rest of the head line; under Option B a blank-terminated body +extends it, and a trailing `|` switches the body to verbatim literal capture. +The annotation dispatches through the +[schema parser]({{% relref "grammar#schema-family" %}}) (not the classifier +parser), so validation keywords co-located on the same comment group still +surface. + +## Supported keywords + +None of its own — the text (plus any folded body) is the entire argument. A bare +`swagger:description` with no text **suppresses** the godoc-derived description +and emits a `CodeEmptyOverride` diagnostic. + +## Example + +A plain override on a model and its fields: + +{{< example + go="shaping/overrides/overrides.go" goregion="model" + json="shaping/overrides/testdata/widget.json" >}} + +The `|` literal block captures a verbatim markdown body (table and list preserved): + +{{< example + go="shaping/markdowndesc/markdowndesc.go" goregion="markdown" + json="shaping/markdowndesc/testdata/markdown.json" >}} diff --git a/docs/doc-site/maintainers/annotations/swagger-title.md b/docs/doc-site/maintainers/annotations/swagger-title.md new file mode 100644 index 0000000..43bb13f --- /dev/null +++ b/docs/doc-site/maintainers/annotations/swagger-title.md @@ -0,0 +1,52 @@ +--- +title: "swagger:title" +weight: 165 +description: "Overrides the godoc-derived title on a model or field." +--- + +## Usage + +```goish +// swagger:title (where is the rest of the line) +``` + +## What it does + +Replaces the godoc-derived `title` on a schema with explicit text. + +By default a model's title comes from the first line of its doc comment; +`swagger:title` overrides that when the prose isn't the title you want to +publish. It is a schema-family **override** — a sibling of +[`swagger:description`]({{% relref "swagger-description" %}}). + +## Where it goes + +On a type (model) doc comment or a struct-field doc comment. It is +schema-only: a response has no `title` (the annotation is ignored there), and on +a non-body parameter or response header it is rejected with a +`CodeContextInvalid` diagnostic. + +## Grammar (EBNF) + +```ebnf +TitleAnnotation = ANN_TITLE , RAW_VALUE ; +``` + +`RAW_VALUE` is the rest of the head line, captured verbatim. The annotation +dispatches through the [schema parser]({{% relref "grammar#schema-family" %}}) +(not the classifier parser), so validation keywords co-located on the same +comment group still surface as schema validations. + +## Supported keywords + +None of its own — the text is the entire argument. A blank `swagger:title` +emits a `CodeEmptyOverride` diagnostic. + +## Example + +{{< example + go="shaping/overrides/overrides.go" goregion="model" + json="shaping/overrides/testdata/widget.json" >}} + +See [Overriding titles & descriptions]({{% relref "overriding-titles-and-descriptions" %}}) +for the full walkthrough. diff --git a/docs/doc-site/maintainers/grammar.md b/docs/doc-site/maintainers/grammar.md index a9bede3..c848e6d 100644 --- a/docs/doc-site/maintainers/grammar.md +++ b/docs/doc-site/maintainers/grammar.md @@ -133,6 +133,8 @@ are emitted as separate terminals. | `ANN_ADDITIONAL_PROPERTIES` | `swagger:additionalProperties` | | `ANN_PATTERN_PROPERTIES` | `swagger:patternProperties` | | `ANN_FILE` | `swagger:file` | +| `ANN_TITLE` | `swagger:title` | +| `ANN_DESCRIPTION` | `swagger:description` | #### Argument terminals @@ -292,15 +294,19 @@ SchemaBlock = SchemaAnnotation , [ Description ] , SchemaAnnotationBody ; -SchemaAnnotation = ModelAnnotation - | ResponseAnnotation - | ParametersAnnotation - | NameAnnotation ; +SchemaAnnotation = ModelAnnotation + | ResponseAnnotation + | ParametersAnnotation + | NameAnnotation + | TitleAnnotation + | DescriptionAnnotation ; -ModelAnnotation = ANN_MODEL , [ IDENT_NAME ] ; -ResponseAnnotation = ANN_RESPONSE , [ IDENT_NAME ] ; -ParametersAnnotation = ANN_PARAMETERS , IDENT_NAME , { IDENT_NAME } ; -NameAnnotation = ANN_NAME , IDENT_NAME ; +ModelAnnotation = ANN_MODEL , [ IDENT_NAME ] ; +ResponseAnnotation = ANN_RESPONSE , [ IDENT_NAME ] ; +ParametersAnnotation = ANN_PARAMETERS , IDENT_NAME , { IDENT_NAME } ; +NameAnnotation = ANN_NAME , IDENT_NAME ; +TitleAnnotation = ANN_TITLE , RAW_VALUE ; +DescriptionAnnotation = ANN_DESCRIPTION , RAW_VALUE ; SchemaAnnotationBody = { SchemaBodyItem } ; UnboundBlockBody = { SchemaBodyItem } ; @@ -343,6 +349,15 @@ DiscriminatorLine = KW_DISCRIMINATOR , BOOL_VALUE ; DeprecatedLine = KW_DEPRECATED , BOOL_VALUE ; ``` +`swagger:title` / `swagger:description` are schema-family **overrides** — they +replace the godoc-derived title / description on a model, field, response, or +header. They dispatch through the schema parser (not the classifier parser), so +validation keywords co-located on the same comment group still surface. The +`RAW_VALUE` is the rest of the head line; `swagger:description` additionally folds +a blank-terminated body (Option B) or, with a trailing `|`, a verbatim literal +markdown block. A blank override emits `CodeEmptyOverride`; `swagger:title` is +rejected with `CodeContextInvalid` on a non-body parameter or response header. + {{< railroad >}} SchemaBodyItem = Validation | SchemaDecorator | ExtensionsBlock | ExternalDocsBlock | BLANK ; Validation = NumericValidation | StringValidation | ArrayValidation | EnumValidation | RequiredLine | ReadOnlyLine ;