diff --git a/generics.go b/generics.go index 7a7ad0207..c0ec97e80 100644 --- a/generics.go +++ b/generics.go @@ -94,11 +94,8 @@ func (pkgDefs *PackagesDefinitions) parametrizeStruct(original *TypeSpecDef, ful Tag: field.Tag, Comment: field.Comment, } - if genTypeSpec, ok := genericParamTypeDefs[field.Type.(*ast.Ident).Name]; ok { - newField.Type = genTypeSpec.TypeSpec.Type - } else { - newField.Type = field.Type - } + + newField.Type = resolveType(field.Type, field, genericParamTypeDefs) newStructTypeDef.Fields.List = append(newStructTypeDef.Fields.List, newField) } @@ -107,3 +104,15 @@ func (pkgDefs *PackagesDefinitions) parametrizeStruct(original *TypeSpecDef, ful return parametrizedTypeSpec } + +func resolveType(expr ast.Expr, field *ast.Field, genericParamTypeDefs map[string]*TypeSpecDef) ast.Expr { + if asIdent, ok := expr.(*ast.Ident); ok { + if genTypeSpec, ok := genericParamTypeDefs[asIdent.Name]; ok { + return genTypeSpec.TypeSpec.Type + } + } else if asArray, ok := expr.(*ast.ArrayType); ok { + return &ast.ArrayType{Elt: resolveType(asArray.Elt, field, genericParamTypeDefs), Len: asArray.Len, Lbrack: asArray.Lbrack} + } + + return field.Type +} diff --git a/generics_test.go b/generics_test.go index 9a2975916..43837063e 100644 --- a/generics_test.go +++ b/generics_test.go @@ -207,3 +207,171 @@ func TestParseGenericsBasic(t *testing.T) { assert.NoError(t, err) assert.Equal(t, expected, string(b)) } + +func TestParseGenericsArrays(t *testing.T) { + t.Parallel() + + expected := `{ + "swagger": "2.0", + "info": { + "description": "This is a sample server Petstore server.", + "title": "Swagger Example API", + "contact": {}, + "version": "1.0" + }, + "host": "localhost:4000", + "basePath": "/api", + "paths": { + "/posts": { + "get": { + "description": "Get All of the Posts", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "summary": "List Posts", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/web.GenericListResponse-web_Post" + } + }, + "222": { + "description": "", + "schema": { + "$ref": "#/definitions/web.GenericListResponseMulti-web_Post-web_Post" + } + } + } + } + } + }, + "definitions": { + "web.GenericListResponse-web_Post": { + "type": "object", + "properties": { + "items": { + "description": "Items from the list response", + "type": "array", + "items": { + "type": "object", + "properties": { + "data": { + "description": "Post data", + "type": "object", + "properties": { + "name": { + "description": "Post tag", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "id": { + "type": "integer", + "format": "int64", + "example": 1 + }, + "name": { + "description": "Post name", + "type": "string", + "example": "poti" + } + } + } + }, + "status": { + "description": "Status of some other stuff", + "type": "string" + } + } + }, + "web.GenericListResponseMulti-web_Post-web_Post": { + "type": "object", + "properties": { + "itemsOne": { + "description": "ItemsOne is the first thing", + "type": "array", + "items": { + "type": "object", + "properties": { + "data": { + "description": "Post data", + "type": "object", + "properties": { + "name": { + "description": "Post tag", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "id": { + "type": "integer", + "format": "int64", + "example": 1 + }, + "name": { + "description": "Post name", + "type": "string", + "example": "poti" + } + } + } + }, + "itemsTwo": { + "description": "ItemsTwo is the second thing", + "type": "array", + "items": { + "type": "object", + "properties": { + "data": { + "description": "Post data", + "type": "object", + "properties": { + "name": { + "description": "Post tag", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "id": { + "type": "integer", + "format": "int64", + "example": 1 + }, + "name": { + "description": "Post name", + "type": "string", + "example": "poti" + } + } + } + }, + "status": { + "description": "Status of the things", + "type": "string" + } + } + } + } +}` + + searchDir := "testdata/generics_arrays" + p := New() + err := p.ParseAPI(searchDir, mainAPIFile, defaultParseDepth) + assert.NoError(t, err) + b, err := json.MarshalIndent(p.swagger, "", " ") + assert.NoError(t, err) + assert.Equal(t, expected, string(b)) +} diff --git a/testdata/generics_arrays/api/api.go b/testdata/generics_arrays/api/api.go new file mode 100644 index 000000000..20cca94f3 --- /dev/null +++ b/testdata/generics_arrays/api/api.go @@ -0,0 +1,18 @@ +package api + +import ( + "net/http" + + "github.com/swaggo/swag/testdata/generics_arrays/web" +) + +// @Summary List Posts +// @Description Get All of the Posts +// @Accept json +// @Produce json +// @Success 200 {object} web.GenericListResponse[web.Post] +// @Success 222 {object} web.GenericListResponseMulti[web.Post, web.Post] +// @Router /posts [get] +func GetPosts(w http.ResponseWriter, r *http.Request) { + _ = web.GenericListResponse[web.Post]{} +} diff --git a/testdata/generics_arrays/main.go b/testdata/generics_arrays/main.go new file mode 100644 index 000000000..cff47d013 --- /dev/null +++ b/testdata/generics_arrays/main.go @@ -0,0 +1,17 @@ +package main + +import ( + "net/http" + + "github.com/swaggo/swag/testdata/generics_basic/api" +) + +// @title Swagger Example API +// @version 1.0 +// @description This is a sample server Petstore server. +// @host localhost:4000 +// @basePath /api +func main() { + http.HandleFunc("/posts/", api.GetPost) + http.ListenAndServe(":8080", nil) +} diff --git a/testdata/generics_arrays/web/handler.go b/testdata/generics_arrays/web/handler.go new file mode 100644 index 000000000..88bd8b035 --- /dev/null +++ b/testdata/generics_arrays/web/handler.go @@ -0,0 +1,50 @@ +package web + +import ( + "time" +) + +// GenericListResponse[T] +// @Description Some Generic List Response +type GenericListResponse[T any] struct { + // Items from the list response + Items []T + // Status of some other stuff + Status string +} + +// GenericListResponseMulti[T, X] +// @Description this contains a few things +type GenericListResponseMulti[T any, X any] struct { + // ItemsOne is the first thing + ItemsOne []T + // ItemsTwo is the second thing + ItemsTwo []X + + // Status of the things + Status string +} + +type Post struct { + ID int `json:"id" example:"1" format:"int64"` + // Post name + Name string `json:"name" example:"poti"` + // Post data + Data struct { + // Post tag + Tag []string `json:"name"` + } `json:"data"` +} + +// APIError +// @Description API error +// @Description with information about it +// Other some summary +type APIError struct { + // Error an Api error + Error string // Error this is Line comment + // Error `number` tick comment + ErrorNo int64 + ErrorCtx string // Error `context` tick comment + CreatedAt time.Time // Error time +}