diff --git a/cmd/oapi-gen/gen.go b/cmd/oapi-gen/gen.go index 1761d23..e4e1447 100644 --- a/cmd/oapi-gen/gen.go +++ b/cmd/oapi-gen/gen.go @@ -22,6 +22,7 @@ const ( directiveReadonly = "readonly" directiveRequired = "required" directiveFormat = "format" + directiveEnum = "enum" ) // Generator is a struct documentation generator. It gathers struct @@ -78,10 +79,11 @@ type structInfo struct { Docs map[string]string Attrs map[string]string Formats map[string]string + Enums map[string][]string } func (i structInfo) Empty() bool { - return len(i.Docs) == 0 && len(i.Attrs) == 0 && len(i.Formats) == 0 + return len(i.Docs) == 0 && len(i.Attrs) == 0 && len(i.Formats) == 0 && len(i.Enums) == 0 } //nolint:cyclop,gocognit // Splitting this will not make it simpler. @@ -155,6 +157,7 @@ func (g *Generator) gatherStructInfo(name string, typ *ast.StructType) (structIn Docs: map[string]string{}, Attrs: map[string]string{}, Formats: map[string]string{}, + Enums: map[string][]string{}, } for _, field := range typ.Fields.List { if field.Doc == nil { @@ -183,6 +186,17 @@ func (g *Generator) gatherStructInfo(name string, typ *ast.StructType) (structIn return info, fmt.Errorf("format directive should be in format openapi:format=, got %s", d) } info.Formats[fldName] = val + case strings.HasPrefix(d, directiveEnum): + _, val, found := strings.Cut(d, "=") + if !found { + return info, fmt.Errorf("enum directive should be in format openapi:enum=foo,bar, got %s", d) + } + // Split by comma and trim spaces + values := strings.Split(val, ",") + for i := range values { + values[i] = strings.TrimSpace(values[i]) + } + info.Enums[fldName] = values } } } @@ -289,5 +303,15 @@ func ({{ .Name }}) Formats() map[string]string { } } {{ end }} +{{- if .Enums }} +// Enums returns a set of possible enum values per property. +func ({{ .Name }}) Enums() map[string][]string { + return map[string][]string { + {{- range $k, $v := .Enums }} + "{{ $k }}": { {{- range $i, $val := $v }}{{if $i}}, {{end}}"{{ $val }}"{{- end }} }, + {{- end }} + } +} +{{ end }} {{ end }} ` diff --git a/cmd/oapi-gen/testdata/all.go b/cmd/oapi-gen/testdata/all.go index 5975caf..c26cfd3 100644 --- a/cmd/oapi-gen/testdata/all.go +++ b/cmd/oapi-gen/testdata/all.go @@ -22,6 +22,7 @@ func (TestOtherObject) Docs() map[string]string { return map[string]string{ "D": "D is another example field.", "E": "E is a formatted example field.", + "F": "F is a field that uses enum values.", "c": "C is an example field.", } } @@ -39,3 +40,10 @@ func (TestOtherObject) Formats() map[string]string { "E": "ipv4", } } + +// Enums returns a set of possible enum values per property. +func (TestOtherObject) Enums() map[string][]string { + return map[string][]string{ + "F": {"foo", "bar"}, + } +} diff --git a/cmd/oapi-gen/testdata/test.go b/cmd/oapi-gen/testdata/test.go index ff2ece2..4c14c51 100644 --- a/cmd/oapi-gen/testdata/test.go +++ b/cmd/oapi-gen/testdata/test.go @@ -29,4 +29,9 @@ type TestOtherObject struct { // //openapi:format=ipv4 // This should be ignored E string + + // F is a field that uses enum values. + // + //openapi:enum=foo,bar // This should be ignored + F string } diff --git a/gen.go b/gen.go index ef1ea45..a621e94 100644 --- a/gen.go +++ b/gen.go @@ -363,6 +363,10 @@ type formatable interface { Formats() map[string]string } +type enumerable interface { + Enums() map[string][]string +} + func customizer(name string, t reflect.Type, _ reflect.StructTag, schema *kin.Schema) error { v := reflect.New(t).Elem().Interface() @@ -392,6 +396,10 @@ func customizer(name string, t reflect.Type, _ reflect.StructTag, schema *kin.Sc applyFormats(schema, obj) } + if obj, ok := v.(enumerable); ok { + applyEnums(schema, obj) + } + return nil } @@ -470,3 +478,15 @@ func applyFormats(schema *kin.Schema, obj formatable) { prop.Value.WithFormat(fmt) } } + +func applyEnums(schema *kin.Schema, obj enumerable) { + enums := obj.Enums() + for k, prop := range schema.Properties { + enum := enums[k] + if len(enum) == 0 || prop.Value == nil { + continue + } + + prop.Value.WithEnum(enum) + } +} diff --git a/gen_test.go b/gen_test.go index 0f6c5b3..23b4c11 100644 --- a/gen_test.go +++ b/gen_test.go @@ -209,3 +209,9 @@ func (TestObject) Formats() map[string]string { "test4": "ipv4", } } + +func (TestObject) Enums() map[string][]string { + return map[string][]string{ + "test4": []string{"192.168.1.0", "192.168.1.1"}, + } +} diff --git a/testdata/spec-pkgseg0.json b/testdata/spec-pkgseg0.json index 38bdd75..942e34e 100644 --- a/testdata/spec-pkgseg0.json +++ b/testdata/spec-pkgseg0.json @@ -37,6 +37,12 @@ "type": "string" }, "test4": { + "enum": [ + [ + "192.168.1.0", + "192.168.1.1" + ] + ], "format": "ipv4", "type": "string" } diff --git a/testdata/spec.json b/testdata/spec.json index cc21d2c..741cea5 100644 --- a/testdata/spec.json +++ b/testdata/spec.json @@ -37,6 +37,12 @@ "type": "string" }, "test4": { + "enum": [ + [ + "192.168.1.0", + "192.168.1.1" + ] + ], "format": "ipv4", "type": "string" }