diff --git a/.github/workflows/update-doc.yml b/.github/workflows/update-doc.yml index fd951ba7..7f97aee5 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/annotation-index/_index.md b/docs/doc-site/annotation-index/_index.md index b7616b70..1f026441 100644 --- a/docs/doc-site/annotation-index/_index.md +++ b/docs/doc-site/annotation-index/_index.md @@ -12,23 +12,25 @@ 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: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" %}}) | +| `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: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.md b/docs/doc-site/maintainers/annotations.md deleted file mode 100644 index e6f5d70c..00000000 --- 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 00000000..8095e615 --- /dev/null +++ b/docs/doc-site/maintainers/annotations/_index.md @@ -0,0 +1,149 @@ +--- +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 nineteen 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 & 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 +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:title` | — | ✅ (override) | — | — | — | — | — | +| `swagger:description` | — | ✅ (override) | — | ✅ (body/header) | — | — | — | +| `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 00000000..4b916838 --- /dev/null +++ b/docs/doc-site/maintainers/annotations/swagger-additionalproperties.md @@ -0,0 +1,64 @@ +--- +title: "swagger:additionalProperties" +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 +[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/schema-validations-and-decorators#additionalproperties" %}}). + +## Grammar (EBNF) + +```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 + +{{< 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` +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 00000000..9488dea2 --- /dev/null +++ b/docs/doc-site/maintainers/annotations/swagger-alias.md @@ -0,0 +1,52 @@ +--- +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 %}} + +## Usage + +```goish +// swagger:alias [ IDENT_NAME ] +``` + +## 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. + +## Grammar (EBNF) + +```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 00000000..977f2c8e --- /dev/null +++ b/docs/doc-site/maintainers/annotations/swagger-allof.md @@ -0,0 +1,56 @@ +--- +title: "swagger:allOf" +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 +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. + +## Grammar (EBNF) + +```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 "/maintainers/keywords/schema-validations-and-decorators#schema-decorators" %}}) on +the inline-object member (the second `allOf` element). + +## Example + +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: + +{{< 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}, …]` +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 00000000..71ea960f --- /dev/null +++ b/docs/doc-site/maintainers/annotations/swagger-default.md @@ -0,0 +1,49 @@ +--- +title: "swagger:default" +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. 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. + +## Grammar (EBNF) + +```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 "/maintainers/keywords/schema-validations-and-decorators#default" %}}) on the relevant +field; this annotation has a narrow surface and is not commonly authored +directly. + +## Example + +`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 "/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-description.md b/docs/doc-site/maintainers/annotations/swagger-description.md new file mode 100644 index 00000000..5a73947d --- /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-enum.md b/docs/doc-site/maintainers/annotations/swagger-enum.md new file mode 100644 index 00000000..5a6b5861 --- /dev/null +++ b/docs/doc-site/maintainers/annotations/swagger-enum.md @@ -0,0 +1,73 @@ +--- +title: "swagger:enum" +weight: 50 +description: "Marks a named type as an enum and collects its const values." +--- + +## Usage + +```goish +// swagger:enum [ IDENT_NAME ] +``` + +## 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. + +## Grammar (EBNF) + +```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 "/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. + +## Example + +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: + +{{< 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 +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 00000000..6b76c329 --- /dev/null +++ b/docs/doc-site/maintainers/annotations/swagger-file.md @@ -0,0 +1,44 @@ +--- +title: "swagger:file" +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. + +## Where it goes + +On a struct field doc inside a `swagger:parameters` (multipart file +upload) or `swagger:response` (file download) struct. + +## Grammar (EBNF) + +```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 + +{{< 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 new file mode 100644 index 00000000..cc6931d6 --- /dev/null +++ b/docs/doc-site/maintainers/annotations/swagger-ignore.md @@ -0,0 +1,52 @@ +--- +title: "swagger:ignore" +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. + +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. + +## Grammar (EBNF) + +```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 + +`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). + +{{< 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 new file mode 100644 index 00000000..31903e90 --- /dev/null +++ b/docs/doc-site/maintainers/annotations/swagger-meta.md @@ -0,0 +1,54 @@ +--- +title: "swagger:meta" +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. + +## Where it goes + +On the package doc comment. No arguments — a bare annotation. + +## Grammar (EBNF) + +```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 "/maintainers/keywords/spec-metadata#meta-single-line-keywords" %}}) +(`schemes`, `version`, `host`, `basePath`, `license`, `contact`) plus the +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-*` +extensions per tag). + +## Example + +{{< 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 new file mode 100644 index 00000000..043f4811 --- /dev/null +++ b/docs/doc-site/maintainers/annotations/swagger-model.md @@ -0,0 +1,96 @@ +--- +title: "swagger:model" +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. + +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`, …). + +## Grammar (EBNF) + +```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 + +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. + +## Example + +The doc comment above the type drives the model's name, title and description: + +```go +// 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 { + // 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"` +} +``` + +Pass an argument to override the name; the type is then published as +`#/definitions/PetWithExtras`: + +```go +// swagger:model PetWithExtras +type DetailedPet struct { … } +``` + +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 new file mode 100644 index 00000000..7288c3bd --- /dev/null +++ b/docs/doc-site/maintainers/annotations/swagger-name.md @@ -0,0 +1,61 @@ +--- +title: "swagger:name" +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. + +## Where it goes + +On a struct field doc OR an interface method doc. + +## Grammar (EBNF) + +```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 + +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: + +{{< example + go="concepts/models/models.go" goregion="name" + json="concepts/models/testdata/name.json" >}} + +**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/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 +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 00000000..e0af8d45 --- /dev/null +++ b/docs/doc-site/maintainers/annotations/swagger-operation.md @@ -0,0 +1,82 @@ +--- +title: "swagger:operation" +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 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 +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. + +## Grammar (EBNF) + +```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 + +{{< 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. + +**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 00000000..113033d9 --- /dev/null +++ b/docs/doc-site/maintainers/annotations/swagger-parameters.md @@ -0,0 +1,68 @@ +--- +title: "swagger:parameters" +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 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. + +- 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/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. +- `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. + +## Grammar (EBNF) + +```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 "/maintainers/keywords/parameters-and-responses#parameter-location" %}}) on +fields: `in`, `required`, the numeric / length / format validations, +`default`, `example`, `enum`, `allowEmptyValue`, `collectionFormat`. + +## Example + +{{< 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 new file mode 100644 index 00000000..95131dba --- /dev/null +++ b/docs/doc-site/maintainers/annotations/swagger-patternproperties.md @@ -0,0 +1,63 @@ +--- +title: "swagger:patternProperties" +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 +[`patternProperties:` keyword]({{% relref "/maintainers/keywords/schema-validations-and-decorators#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`). + +## Grammar (EBNF) + +```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 + +{{< 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" %}}). +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 00000000..fa090cba --- /dev/null +++ b/docs/doc-site/maintainers/annotations/swagger-response.md @@ -0,0 +1,72 @@ +--- +title: "swagger:response" +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. + +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: + +- 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/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 + `{type: array, items: {}}` for a slice) — "any type", valid OpenAPI 2.0. + +## Where it goes + +On a struct declaration. + +## Grammar (EBNF) + +```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 + +{{< 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. + +**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 00000000..4fbdc1f9 --- /dev/null +++ b/docs/doc-site/maintainers/annotations/swagger-route.md @@ -0,0 +1,85 @@ +--- +title: "swagger:route" +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). + +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. + +## Grammar (EBNF) + +```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 "/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 +tags). The `Parameters:` and `Responses:` sub-languages are documented in +{{% relref "sub-languages#parameters" %}} and +{{% relref "sub-languages#responses" %}}. + +## Example + +{{< example + go="concepts/routes/routes.go" goregion="route" + json="concepts/routes/testdata/route.json" >}} + +**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 00000000..7792da5e --- /dev/null +++ b/docs/doc-site/maintainers/annotations/swagger-strfmt.md @@ -0,0 +1,70 @@ +--- +title: "swagger:strfmt" +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. + +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. + +## Grammar (EBNF) + +```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 + +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}`: + +{{< 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 +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-title.md b/docs/doc-site/maintainers/annotations/swagger-title.md new file mode 100644 index 00000000..43bb13fa --- /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/annotations/swagger-type.md b/docs/doc-site/maintainers/annotations/swagger-type.md new file mode 100644 index 00000000..79690476 --- /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." +--- + +## 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). + +## 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 %}} + +## Grammar (EBNF) + +```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 — 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: + +{{< example + go="concepts/models/models.go" goregion="type" + json="concepts/models/testdata/type.json" >}} + +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): + +{{< 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 +**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/grammar.md b/docs/doc-site/maintainers/grammar.md index 5c2d87f6..c848e6d1 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 @@ -263,6 +265,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 @@ -284,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 } ; @@ -335,6 +349,27 @@ 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 ; +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 +418,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 +469,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 +501,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/docs/doc-site/maintainers/keywords.md b/docs/doc-site/maintainers/keywords.md deleted file mode 100644 index 59cccf18..00000000 --- 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#swaggerpatternproperties" %}}) -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#swaggertype" %}}) -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" %}}) -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#swaggername" %}}) -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 00000000..ac466134 --- /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 00000000..7155d0d7 --- /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 00000000..f6db0ea6 --- /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 00000000..d7ab183b --- /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 00000000..9240ad17 --- /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 00000000..6242b248 --- /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 00000000..f3b3fa09 --- /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/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 011c275a..1c9b1e72 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 88e29de6..0e0ae3f4 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 8a80e83f..0d118714 100644 --- a/docs/doc-site/tutorials/document-metadata.md +++ b/docs/doc-site/tutorials/document-metadata.md @@ -35,8 +35,8 @@ 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" %}}) -and the [meta keywords]({{% relref "/maintainers/keywords#meta-single-line-keywords" %}}). +[`swagger:meta` reference]({{% relref "/maintainers/annotations/swagger-meta" %}}) +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 9ce3e8c7..265dc27c 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" @@ -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 d66554a3..4e67700b 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 @@ -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 3f7a21c2..7db8a5c5 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" %}}) - and [`swagger:allOf` reference]({{% relref "/maintainers/annotations#swaggerallof" %}}). +- [`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/routes-and-operations.md b/docs/doc-site/tutorials/routes-and-operations.md index dcee9bc2..8c8d3692 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: diff --git a/docs/doc-site/tutorials/security.md b/docs/doc-site/tutorials/security.md index 27e9443f..00dc22ab 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 diff --git a/docs/examples/concepts/maps/maps.go b/docs/examples/concepts/maps/maps.go index c18c3ced..899fbc52 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 3a9da42c..7b1f3b27 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 00000000..ddda060d --- /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 1beef4db..c52d9e1c 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 267adff8..a17a5094 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 00000000..59115800 --- /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 86764a8e..3d275a7c 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 82f91959..6d182f22 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= diff --git a/hack/doc-site/hugo/README.md b/hack/doc-site/hugo/README.md index 4af6f7ab..7b17ec46 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 7aef11f9..a2e1d2e1 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 00000000..8307bdeb --- /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 a8a17ed2..37d89742 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 00000000..8cb34cb0 --- /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