Enumerations
An enum in Go is a named type plus a block of constants. swagger:enum turns
that pair into an enum array on every schema, parameter and header the type
reaches. This page covers what the scanner accepts on the value side, what
decides the emitted type / format, and the two shapes that do not work.
Every Go snippet below comes from the test-covered
docs/examples/concepts/enums
package, and every JSON pane is a golden file a test regenerates.
swagger:enum
swagger:enum <name> collects the const values declared with that type. A
bare swagger:enum on the type declaration works too — the name is inferred
from the declaration it sits on.
The enum type is emitted because something points at it: a model field, a
parameter, a header. On its own it is unreachable, and unreachable types are not
published. Add swagger:model to the enum type to make it a first-class
definition (carrying the enum array) that fields $ref instead — the general
swagger:model ⇒ definition + $ref rule.
Each member’s doc comment becomes a line of the x-go-enum-desc extension, and
is appended to the property description. Set
SkipEnumDescriptions to keep the
mapping on the extension only.
Any constant expression, not just literals
The values come from the Go type-checker, which has already evaluated the const block. So the members do not have to be written as literals — anything the compiler can fold is collected.
iota is the case that matters most, because after the first line there is
nothing left in the source to read: the following specs carry neither a type nor
a value, and inherit both implicitly.
// Weekday is an iota enum: only the first spec carries a type and a value, and
// every following one inherits both implicitly.
//
// swagger:enum Weekday
type Weekday int
const (
// Sunday is the first day.
Sunday Weekday = iota
// Monday is the second day.
Monday
// Tuesday is the third day.
Tuesday
)
// Schedule carries the enum, which is what makes it reachable and so emitted.
//
// swagger:model
type Schedule struct {
// Day the job runs on.
Day Weekday `json:"day"`
}Full source: docs/examples/concepts/enums/enums.go
{
"type": "object",
"title": "Schedule carries the enum, which is what makes it reachable and so emitted.",
"properties": {
"day": {
"description": "Day the job runs on.\n0 Sunday is the first day.\n1 Monday is the second day.\n2 Tuesday is the third day.",
"type": "integer",
"format": "int64",
"enum": [
0,
1,
2
],
"x-go-enum-desc": "0 Sunday is the first day.\n1 Monday is the second day.\n2 Tuesday is the third day.",
"x-go-name": "Day"
}
},
"x-go-package": "github.com/go-openapi/codescan/docs/examples/concepts/enums"
}
Full source: docs/examples/concepts/enums/testdata/iota.json
Constant expressions and references to earlier members are collected the same way.
// Level is built from a constant expression and from a reference to an earlier
// member — neither of which is a literal.
//
// swagger:enum Level
type Level int
const (
// LevelLow is the floor.
LevelLow Level = 1 << 3
// LevelHigh doubles it.
LevelHigh Level = LevelLow * 2
)
// Threshold carries the computed enum.
//
// swagger:model
type Threshold struct {
// Level to alert at.
Level Level `json:"level"`
}Full source: docs/examples/concepts/enums/enums.go
{
"type": "object",
"title": "Threshold carries the computed enum.",
"properties": {
"level": {
"description": "Level to alert at.\n8 LevelLow is the floor.\n16 LevelHigh doubles it.",
"type": "integer",
"format": "int64",
"enum": [
8,
16
],
"x-go-enum-desc": "8 LevelLow is the floor.\n16 LevelHigh doubles it.",
"x-go-name": "Level"
}
},
"x-go-package": "github.com/go-openapi/codescan/docs/examples/concepts/enums"
}
Full source: docs/examples/concepts/enums/testdata/expressions.json
The same goes for every other constant form Go offers: negative values, the
non-decimal bases (0x2a, 0b101010, 0o52) and digit separators, values
above MaxInt64 in an unsigned enum, true / false, rune literals, and both
the raw and the escaped string forms.
Negative members are worth a pane of their own, since a signed constant is not a literal in the Go grammar — it is an expression wrapping one:
// PanDirection straddles zero. A signed constant is not a literal in the Go
// grammar — it is an expression wrapping one — so these are the members that
// used to go missing.
//
// swagger:enum PanDirection
type PanDirection int8
const (
// PanLeft pans to the left.
PanLeft PanDirection = -1
// NoPan holds the current position.
NoPan PanDirection = 0
// PanRight pans to the right.
PanRight PanDirection = 1
)
// Camera carries the signed enum, declared int8.
//
// swagger:model
type Camera struct {
// Pan direction of the camera.
Pan PanDirection `json:"pan"`
}Full source: docs/examples/concepts/enums/enums.go
{
"type": "object",
"title": "Camera carries the signed enum, declared int8.",
"properties": {
"pan": {
"description": "Pan direction of the camera.\n-1 PanLeft pans to the left.\n0 NoPan holds the current position.\n1 PanRight pans to the right.",
"type": "integer",
"format": "int8",
"enum": [
-1,
0,
1
],
"x-go-enum-desc": "-1 PanLeft pans to the left.\n0 NoPan holds the current position.\n1 PanRight pans to the right.",
"x-go-name": "Pan"
}
},
"x-go-package": "github.com/go-openapi/codescan/docs/examples/concepts/enums"
}
Full source: docs/examples/concepts/enums/testdata/signed.json
The type comes from the declaration
type and format are read from the Go type you declared, never from the
members. PanDirection above is an int8, so the property is
{integer, int8} — even though every member would fit in a smaller or larger
box.
This is also what makes the const block safe to reorder. Zoom is a
float32 whose first member is written 0, an integer literal; the schema is a
number enum regardless of which member comes first:
// Zoom is a float32 enum whose FIRST member is written as an integer literal.
// The schema type follows the declared type, so the block can be reordered
// freely.
//
// swagger:enum Zoom
type Zoom float32
const (
// ZoomNone is the neutral step.
ZoomNone Zoom = 0
// ZoomOut steps back.
ZoomOut Zoom = -1.5
// ZoomIn steps in.
ZoomIn Zoom = 1.5
)
// Lens carries the float enum.
//
// swagger:model
type Lens struct {
// Zoom step of the lens.
Zoom Zoom `json:"zoom"`
}Full source: docs/examples/concepts/enums/enums.go
{
"type": "object",
"title": "Lens carries the float enum.",
"properties": {
"zoom": {
"description": "Zoom step of the lens.\n0 ZoomNone is the neutral step.\n-1.5 ZoomOut steps back.\n1.5 ZoomIn steps in.",
"type": "number",
"format": "float",
"enum": [
0,
-1.5,
1.5
],
"x-go-enum-desc": "0 ZoomNone is the neutral step.\n-1.5 ZoomOut steps back.\n1.5 ZoomIn steps in.",
"x-go-name": "Zoom"
}
},
"x-go-package": "github.com/go-openapi/codescan/docs/examples/concepts/enums"
}
Full source: docs/examples/concepts/enums/testdata/width.json
A type declared over another named type keeps what that type contributed. An enum written over a string format is still that format:
// Kind is an enum written over a string format rather than over a plain string:
// the format of the type it is declared over comes with it.
//
// swagger:enum Kind
type Kind strfmt.UUID
const (
// KindPrimary is the primary kind.
KindPrimary Kind = "0a8bcf1e-0000-0000-0000-000000000000"
// KindSecondary is the secondary kind.
KindSecondary Kind = "0a8bcf1e-1111-1111-1111-111111111111"
)
// Label carries the formatted enum.
//
// swagger:model
type Label struct {
// Kind of the label.
Kind Kind `json:"kind"`
}Full source: docs/examples/concepts/enums/enums.go
{
"type": "object",
"title": "Label carries the formatted enum.",
"properties": {
"kind": {
"description": "Kind of the label.\n0a8bcf1e-0000-0000-0000-000000000000 KindPrimary is the primary kind.\n0a8bcf1e-1111-1111-1111-111111111111 KindSecondary is the secondary kind.",
"type": "string",
"format": "uuid",
"enum": [
"0a8bcf1e-0000-0000-0000-000000000000",
"0a8bcf1e-1111-1111-1111-111111111111"
],
"x-go-enum-desc": "0a8bcf1e-0000-0000-0000-000000000000 KindPrimary is the primary kind.\n0a8bcf1e-1111-1111-1111-111111111111 KindSecondary is the secondary kind.",
"x-go-name": "Kind"
}
},
"x-go-package": "github.com/go-openapi/codescan/docs/examples/concepts/enums"
}
Full source: docs/examples/concepts/enums/testdata/strfmt.json
Parameters and headers
OpenAPI 2.0 forbids a $ref on a non-body parameter or a response header, so
there the members and the format are written inline. Nothing changes on the
annotation side — the same enum type reaches in: query, path, header and
formData, and the items of an array-typed one:
// SearchParams consumes an enum from a non-body parameter, where OpenAPI 2.0
// forbids a $ref: the members and the format ship inline.
//
// swagger:parameters search
type SearchParams struct {
// Direction to pan while searching.
//
// in: query
Pan PanDirection `json:"pan"`
// Directions the client accepts.
//
// in: query
Accepted []PanDirection `json:"accepted"`
}Full source: docs/examples/concepts/enums/enums.go
[
{
"enum": [
-1,
0,
1
],
"type": "integer",
"format": "int8",
"x-go-enum-desc": "-1 PanLeft pans to the left.\n0 NoPan holds the current position.\n1 PanRight pans to the right.",
"x-go-name": "Pan",
"description": "Direction to pan while searching.\n-1 PanLeft pans to the left.\n0 NoPan holds the current position.\n1 PanRight pans to the right.",
"name": "pan",
"in": "query"
},
{
"type": "array",
"items": {
"enum": [
-1,
0,
1
],
"type": "integer",
"format": "int8"
},
"x-go-name": "Accepted",
"description": "Directions the client accepts.",
"name": "accepted",
"in": "query"
}
]
Full source: docs/examples/concepts/enums/testdata/params.json
Two shapes that do not work
A rune or byte enum emits integers. It is collected like any other, and
'a' reaches the spec as 97:
// Letter is a rune enum. A rune is an int32, on the wire as much as in Go, so
// the members are code points — 'a' is 97.
//
// swagger:enum Letter
type Letter rune
const (
// LetterA is the first letter.
LetterA Letter = 'a'
// LetterB is the second letter.
LetterB Letter = 'b'
)
// Glyph carries the rune enum.
//
// swagger:model
type Glyph struct {
// Letter of the glyph.
Letter Letter `json:"letter"`
}Full source: docs/examples/concepts/enums/enums.go
{
"type": "object",
"title": "Glyph carries the rune enum.",
"properties": {
"letter": {
"description": "Letter of the glyph.\n97 LetterA is the first letter.\n98 LetterB is the second letter.",
"type": "integer",
"format": "int32",
"enum": [
97,
98
],
"x-go-enum-desc": "97 LetterA is the first letter.\n98 LetterB is the second letter.",
"x-go-name": "Letter"
}
},
"x-go-package": "github.com/go-openapi/codescan/docs/examples/concepts/enums"
}
Full source: docs/examples/concepts/enums/testdata/runes.json
That is unlikely to be what you pictured, and it is the only faithful answer: a
scalar rune is an int32 on the wire as much as in Go, so json.Marshal
writes 97, and encoding/json refuses to unmarshal "a" back into the
field. A string-typed schema would describe a payload your own server rejects.
If you meant characters, declare the type over string
(type Letter string, LetterA Letter = "a") — that changes the wire, and the
schema follows.
An alias to a basic type cannot host an enum.
The Go type-checker erases the alias, so Zero is indistinguishable from any
other uint64 constant and there is no set of members to collect. Declare a
real type instead (type Unsigned uint64). An alias to a named enum type is
fine — the named type survives.
Where to go next
- Validations — the other constraints a property can carry, and the reduced surface parameters accept.
- Model definitions — the rest of the per-type annotations.
swagger:enumreference — the exhaustive rule.