From c79bf0d44134d161cebde3e1df557cba51517a74 Mon Sep 17 00:00:00 2001 From: "Daniel G. Taylor" Date: Wed, 30 Aug 2023 21:23:31 -0700 Subject: [PATCH] fix: better support of generic named types --- registry.go | 13 +++++++++---- schema_test.go | 14 ++++++++++++++ 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/registry.go b/registry.go index 8b6356cf..9773be95 100644 --- a/registry.go +++ b/registry.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" "reflect" + "strings" ) // Registry creates and stores schemas and their references, and supports @@ -16,18 +17,22 @@ type Registry interface { SchemaFromRef(ref string) *Schema TypeFromRef(ref string) reflect.Type Map() map[string]*Schema - MarshalJSON() ([]byte, error) - MarshalYAML() (interface{}, error) } // DefaultSchemaNamer provides schema names for types. It uses the type name -// when possible, ignoring the package name. If the type is unnamed, then -// the name hint is used. +// when possible, ignoring the package name. If the type is generic, e.g. +// `MyType[SubType]`, then the brackets are removed like `MyTypeSubType`. +// If the type is unnamed, then the name hint is used. // Note: if you plan to use types with the same name from different packages, // you should implement your own namer function to prevent issues. Nested // anonymous types can also present naming issues. func DefaultSchemaNamer(t reflect.Type, hint string) string { name := deref(t).Name() + + // Fix up generics, if used, for nicer refs & URLs. + name = strings.ReplaceAll(name, "[", "") + name = strings.ReplaceAll(name, "]", "") + if name == "" { name = hint } diff --git a/schema_test.go b/schema_test.go index f6649dc7..c04968b1 100644 --- a/schema_test.go +++ b/schema_test.go @@ -425,6 +425,20 @@ func TestSchemaOld(t *testing.T) { // fmt.Println(string(b)) } +func TestSchemaGenericNaming(t *testing.T) { + type SchemaGeneric[T any] struct { + Value T `json:"value"` + } + + r := NewMapRegistry("#/components/schemas/", DefaultSchemaNamer) + s := r.Schema(reflect.TypeOf(SchemaGeneric[int]{}), true, "") + + b, _ := json.Marshal(s) + assert.JSONEq(t, `{ + "$ref": "#/components/schemas/SchemaGenericint" + }`, string(b)) +} + type BenchSub struct { Visible bool `json:"visible" default:"true"` Metrics []float64 `json:"metrics" maxItems:"31"`