Repository: swaggo/swag
Pull request #1894 (2024-10-02 06:49:30)

Flags to parse internal and dependency package
**Remove cannot find type definition error**
```go
package model

type PaginatedListResp[T any] struct {
	Data         []T  `json:"data"`
	TotalCount   int  `json:"total_count"`
	HasMore      bool `json:"has_more"`
	Page         int  `json:"page"`
	ItemsPerPage int  `json:"items_per_page"`
}

type User struct {
	ID          uuid.UUID `json:"id"`
	Name        string    `json:"name"`
	PhoneNum    string    `json:"phone_number"`
	Email       string    `json:"email"`
	Username    string    `json:"username"`
}
```

```go
package api
.
.
.
//	@Success	200	{object}	models.PaginatedListResp[models.User]
```


**Additional context**
If the model is part of an external dependency or internal package then flags mentioned in the PR can be used but were not mentioned in the generics section.

In some cases simple `import _ github.com/xxx/models` also works but if it does not then adding either `--parseDependency` or `--parseInternal` helps in solving the issue based on requirement.



Patch:
diff --git a/README.md b/README.md
index b453c41..7504f6e 100644
--- a/README.md
+++ b/README.md
@@ -31,7 +31,8 @@ Swag converts Go annotations to Swagger Documentation 2.0. We've created a varie
 	- [User defined structure with an array type](#user-defined-structure-with-an-array-type)
 	- [Function scoped struct declaration](#function-scoped-struct-declaration)
 	- [Model composition in response](#model-composition-in-response)
-	- [Add a headers in response](#add-a-headers-in-response)
+        - [Add request headers](#add-request-headers)
+	- [Add response headers](#add-response-headers)
 	- [Use multiple path params](#use-multiple-path-params)
 	- [Example value of struct](#example-value-of-struct)
 	- [SchemaExample of body](#schemaexample-of-body)
@@ -103,6 +104,7 @@ OPTIONS:
    --outputTypes value, --ot value        Output types of generated files (docs.go, swagger.json, swagger.yaml) like go,json,yaml (default: "go,json,yaml")
    --parseVendor                          Parse go files in 'vendor' folder, disabled by default (default: false)
    --parseDependency, --pd                Parse go files inside dependency folder, disabled by default (default: false)
+   --parseDependencyLevel, --pdl          Enhancement of '--parseDependency', parse go files inside dependency folder, 0 disabled, 1 only parse models, 2 only parse operations, 3 parse all (default: 0)
    --markdownFiles value, --md value      Parse folder containing markdown files to use as description, disabled by default
    --codeExampleFiles value, --cef value  Parse folder containing code example files to use for the x-codeSamples extension, disabled by default
    --parseInternal                        Parse go files in internal packages, disabled by default (default: false)
@@ -646,7 +648,14 @@ type DeepObject struct { //in `proto` package
 }
 @success 200 {object} jsonresult.JSONResult{data1=proto.Order{data=proto.DeepObject},data2=[]proto.Order{data=[]proto.DeepObject}} "desc"
 ```
-### Add a headers in response
+### Add response request
+
+```go
+// @Param        X-MyHeader	  header    string    true   	"MyHeader must be set for valid response"
+// @Param        X-API-VERSION    header    string    true   	"API version eg.: 1.0"
+```
+
+### Add response headers
 
 ```go
 // @Success      200              {string}  string    "ok"
@@ -947,6 +956,17 @@ swag init -g http/api.go -td "[[,]]"
 ```
 The new delimiter is a string with the format "`<left delimiter>`,`<right delimiter>`".
 
+### Parse Internal and Dependency Packages
+
+If the struct is defined in a dependency package, use `--parseDependency`.
+
+If the struct is defined in your main project, use `--parseInternal`.
+
+if you want to include both internal and from dependencies use both flags 
+```
+swag init --parseDependency --parseInternal
+```
+
 ## About the Project
 This project was inspired by [yvasiyarov/swagger](https://github.com/yvasiyarov/swagger) but we simplified the usage and added support a variety of [web frameworks](#supported-web-frameworks). Gopher image source is [tenntenn/gopher-stickers](https://github.com/tenntenn/gopher-stickers). It has licenses [creative commons licensing](http://creativecommons.org/licenses/by/3.0/deed.en).
 ## Contributors
diff --git a/README_zh-CN.md b/README_zh-CN.md
index 2b3d9d2..24a219c 100644
--- a/README_zh-CN.md
+++ b/README_zh-CN.md
@@ -90,6 +90,7 @@ OPTIONS:
    --output value, -o value               文件(swagger.json, swagger.yaml and doc.go)输出目录 (默认: "./docs")
    --parseVendor                          是否解析vendor目录里的go源文件，默认不
    --parseDependency                      是否解析依赖目录中的go源文件，默认不
+   --parseDependencyLevel, --pdl          对'--parseDependency'参数进行增强, 是否解析依赖目录中的go源文件, 0 不解析, 1 只解析对象模型, 2 只解析API, 3 对象模型和API都解析 (default: 0)
    --markdownFiles value, --md value      指定API的描述信息所使用的markdown文件所在的目录
    --generatedTime                        是否输出时间到输出文件docs.go的顶部，默认是
    --codeExampleFiles value, --cef value  解析包含用于 x-codeSamples 扩展的代码示例文件的文件夹，默认禁用
diff --git a/field_parser.go b/field_parser.go
index f2bef6f..de8945d 100644
--- a/field_parser.go
+++ b/field_parser.go
@@ -65,35 +65,38 @@ func (ps *tagBaseFieldParser) ShouldSkip() bool {
 	return false
 }
 
-func (ps *tagBaseFieldParser) FieldName() (string, error) {
-	var name string
+func (ps *tagBaseFieldParser) FieldNames() ([]string, error) {
+	if len(ps.field.Names) <= 1 {
+		// if embedded but with a json/form name ??
+		if ps.field.Tag != nil {
+			// json:"tag,hoge"
+			name := strings.TrimSpace(strings.Split(ps.tag.Get(jsonTag), ",")[0])
+			if name != "" {
+				return []string{name}, nil
+			}
 
-	if ps.field.Tag != nil {
-		// json:"tag,hoge"
-		name = strings.TrimSpace(strings.Split(ps.tag.Get(jsonTag), ",")[0])
-		if name != "" {
-			return name, nil
+			// use "form" tag over json tag
+			name = ps.FormName()
+			if name != "" {
+				return []string{name}, nil
+			}
 		}
-
-		// use "form" tag over json tag
-		name = ps.FormName()
-		if name != "" {
-			return name, nil
+		if len(ps.field.Names) == 0 {
+			return nil, nil
 		}
 	}
-
-	if ps.field.Names == nil {
-		return "", nil
-	}
-
-	switch ps.p.PropNamingStrategy {
-	case SnakeCase:
-		return toSnakeCase(ps.field.Names[0].Name), nil
-	case PascalCase:
-		return ps.field.Names[0].Name, nil
-	default:
-		return toLowerCamelCase(ps.field.Names[0].Name), nil
+	var names = make([]string, 0, len(ps.field.Names))
+	for _, name := range ps.field.Names {
+		switch ps.p.PropNamingStrategy {
+		case SnakeCase:
+			names = append(names, toSnakeCase(name.Name))
+		case PascalCase:
+			names = append(names, name.Name)
+		default:
+			names = append(names, toLowerCamelCase(name.Name))
+		}
 	}
+	return names, nil
 }
 
 func (ps *tagBaseFieldParser) firstTagValue(tag string) string {
diff --git a/parser.go b/parser.go
index eb5d96c..cd2a46c 100644
--- a/parser.go
+++ b/parser.go
@@ -190,7 +190,7 @@ type FieldParserFactory func(ps *Parser, field *ast.Field) FieldParser
 // FieldParser parse struct field.
 type FieldParser interface {
 	ShouldSkip() bool
-	FieldName() (string, error)
+	FieldNames() ([]string, error)
 	FormName() string
 	HeaderName() string
 	PathName() string
@@ -1527,20 +1527,20 @@ func (parser *Parser) parseStructField(file *ast.File, field *ast.Field) (map[st
 		return nil, nil, nil
 	}
 
-	fieldName, err := ps.FieldName()
+	fieldNames, err := ps.FieldNames()
 	if err != nil {
 		return nil, nil, err
 	}
 
-	if fieldName == "" {
+	if len(fieldNames) == 0 {
 		typeName, err := getFieldType(file, field.Type, nil)
 		if err != nil {
-			return nil, nil, fmt.Errorf("%s: %w", fieldName, err)
+			return nil, nil, err
 		}
 
 		schema, err := parser.getTypeSchema(typeName, file, false)
 		if err != nil {
-			return nil, nil, fmt.Errorf("%s: %w", fieldName, err)
+			return nil, nil, err
 		}
 
 		if len(schema.Type) > 0 && schema.Type[0] == OBJECT {
@@ -1562,7 +1562,7 @@ func (parser *Parser) parseStructField(file *ast.File, field *ast.Field) (map[st
 
 	schema, err := ps.CustomSchema()
 	if err != nil {
-		return nil, nil, fmt.Errorf("%s: %w", fieldName, err)
+		return nil, nil, fmt.Errorf("%v: %w", fieldNames, err)
 	}
 
 	if schema == nil {
@@ -1576,24 +1576,24 @@ func (parser *Parser) parseStructField(file *ast.File, field *ast.Field) (map[st
 		}
 
 		if err != nil {
-			return nil, nil, fmt.Errorf("%s: %w", fieldName, err)
+			return nil, nil, fmt.Errorf("%v: %w", fieldNames, err)
 		}
 	}
 
 	err = ps.ComplementSchema(schema)
 	if err != nil {
-		return nil, nil, fmt.Errorf("%s: %w", fieldName, err)
+		return nil, nil, fmt.Errorf("%v: %w", fieldNames, err)
 	}
 
 	var tagRequired []string
 
 	required, err := ps.IsRequired()
 	if err != nil {
-		return nil, nil, fmt.Errorf("%s: %w", fieldName, err)
+		return nil, nil, fmt.Errorf("%v: %w", fieldNames, err)
 	}
 
 	if required {
-		tagRequired = append(tagRequired, fieldName)
+		tagRequired = append(tagRequired, fieldNames...)
 	}
 
 	if schema.Extensions == nil {
@@ -1608,8 +1608,11 @@ func (parser *Parser) parseStructField(file *ast.File, field *ast.Field) (map[st
 	if pathName := ps.PathName(); len(pathName) > 0 {
 		schema.Extensions["path"] = pathName
 	}
-
-	return map[string]spec.Schema{fieldName: *schema}, tagRequired, nil
+	fields := make(map[string]spec.Schema)
+	for _, name := range fieldNames {
+		fields[name] = *schema
+	}
+	return fields, tagRequired, nil
 }
 
 func getFieldType(file *ast.File, field ast.Expr, genericParamTypeDefs map[string]*genericTypeSpec) (string, error) {


Test patch:
diff --git a/field_parser_test.go b/field_parser_test.go
index 0d8723f..28f8cdf 100644
--- a/field_parser_test.go
+++ b/field_parser_test.go
@@ -676,9 +676,11 @@ func TestValidTags(t *testing.T) {
 		schema.Type = []string{"integer"}
 		err = newTagBaseFieldParser(
 			&Parser{},
-			&ast.Field{Tag: &ast.BasicLit{
-				Value: `json:"test" validate:"required,oneof=one two"`,
-			}},
+			&ast.Field{
+				Names: []*ast.Ident{{Name: "Test"}},
+				Tag: &ast.BasicLit{
+					Value: `json:"test" validate:"required,oneof=one two"`,
+				}},
 		).ComplementSchema(&schema)
 		assert.NoError(t, err)
 		assert.Empty(t, schema.Enum)
@@ -687,22 +689,41 @@ func TestValidTags(t *testing.T) {
 	t.Run("Form Filed Name", func(t *testing.T) {
 		t.Parallel()
 
-		filedname, err := newTagBaseFieldParser(
+		filednames, err := newTagBaseFieldParser(
 			&Parser{},
-			&ast.Field{Tag: &ast.BasicLit{
-				Value: `form:"test[]"`,
-			}},
-		).FieldName()
+			&ast.Field{
+				Names: []*ast.Ident{{Name: "Test"}},
+				Tag: &ast.BasicLit{
+					Value: `form:"test[]"`,
+				}},
+		).FieldNames()
 		assert.NoError(t, err)
-		assert.Equal(t, "test", filedname)
+		assert.Equal(t, "test", filednames[0])
 
-		filedname, err = newTagBaseFieldParser(
+		filednames, err = newTagBaseFieldParser(
 			&Parser{},
-			&ast.Field{Tag: &ast.BasicLit{
-				Value: `form:"test"`,
-			}},
-		).FieldName()
+			&ast.Field{
+				Names: []*ast.Ident{{Name: "Test"}},
+				Tag: &ast.BasicLit{
+					Value: `form:"test"`,
+				}},
+		).FieldNames()
+		assert.NoError(t, err)
+		assert.Equal(t, "test", filednames[0])
+	})
+
+	t.Run("Two Names", func(t *testing.T) {
+		t.Parallel()
+
+		fieldnames, err := newTagBaseFieldParser(
+			&Parser{},
+			&ast.Field{
+				Names: []*ast.Ident{{Name: "X"}, {Name: "Y"}},
+			},
+		).FieldNames()
 		assert.NoError(t, err)
-		assert.Equal(t, "test", filedname)
+		assert.Equal(t, 2, len(fieldnames))
+		assert.Equal(t, "x", fieldnames[0])
+		assert.Equal(t, "y", fieldnames[1])
 	})
 }