func (parser *Parser) ParseDefinition(typeSpecDef *TypeSpecDef) (*Schema, error) {
	typeName := typeSpecDef.FullName()
	refTypeName := TypeDocName(typeName, typeSpecDef.TypeSpec)
	schema, ok := parser.parsedSchemas[typeSpecDef]
	if ok {
		parser.debug.Printf("Skipping '%s', already parsed.", typeName)
		return schema, nil
	}
	if parser.isInStructStack(typeSpecDef) {
		parser.debug.Printf("Skipping '%s', recursion detected.", typeName)
		return &Schema{
				Name:    refTypeName,
				PkgPath: typeSpecDef.PkgPath,
				Schema:  PrimitiveSchema(OBJECT),
			},
			ErrRecursiveParseStruct
	}
	parser.structStack = append(parser.structStack, typeSpecDef)
	parser.debug.Printf("Generating %s", typeName)
	definition, err := parser.parseTypeExpr(typeSpecDef.File, typeSpecDef.TypeSpec.Type, false)
	if err != nil {
		return nil, err
	}
	s := Schema{
		Name:    refTypeName,
		PkgPath: typeSpecDef.PkgPath,
		Schema:  definition,
	}
	parser.parsedSchemas[typeSpecDef] = &s
	s2, ok := parser.outputSchemas[typeSpecDef]
	if ok {
		parser.swagger.Definitions[s2.Name] = *definition
	}
	return &s, nil
}
ParseDefinition parses given type spec that corresponds to the type under
given name and package, and populates swagger schema definitions registry
with a schema for the given type
package swag
import (
	"encoding/json"
	"errors"
	"fmt"
	"go/ast"
	"go/build"
	goparser "go/parser"
	"go/token"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"os"
	"os/exec"
	"path/filepath"
	"reflect"
	"sort"
	"strconv"
	"strings"
	"unicode"
	"github.com/KyleBanks/depth"
	"github.com/go-openapi/spec"
)
const (
	CamelCase = "camelcase"
	PascalCase = "pascalcase"
	SnakeCase = "snakecase"
	acceptAttr      = "@accept"
	produceAttr     = "@produce"
	scopeAttrPrefix = "@scope."
)
var (
	ErrRecursiveParseStruct = errors.New("recursively parsing struct")
	ErrFuncTypeField = errors.New("field type is func")
	ErrFailedConvertPrimitiveType = errors.New("swag property: failed convert primitive type")
)
type Parser struct {
	swagger *spec.Swagger
	packages *PackagesDefinitions
	parsedSchemas map[*TypeSpecDef]*Schema
	outputSchemas map[*TypeSpecDef]*Schema
	existSchemaNames map[string]*Schema
	toBeRenamedSchemas map[string]string
	toBeRenamedRefURLs []*url.URL
	PropNamingStrategy string
	ParseVendor bool
	ParseDependency bool
	ParseInternal bool
	Strict bool
	structStack []*TypeSpecDef
	markdownFileDir string
	codeExampleFilesDir string
	collectionFormatInQuery string
	excludes map[string]bool
	debug Debugger
}
type Debugger interface {
	Printf(format string, v ...interface{})
}
func New(options ...func(*Parser)) *Parser {
	parser := &Parser{
		swagger: &spec.Swagger{
			SwaggerProps: spec.SwaggerProps{
				Info: &spec.Info{
					InfoProps: spec.InfoProps{
						Contact: &spec.ContactInfo{},
						License: nil,
					},
					VendorExtensible: spec.VendorExtensible{
						Extensions: spec.Extensions{},
					},
				},
				Paths: &spec.Paths{
					Paths: make(map[string]spec.PathItem),
				},
				Definitions:         make(map[string]spec.Schema),
				SecurityDefinitions: make(map[string]*spec.SecurityScheme),
			},
		},
		packages:           NewPackagesDefinitions(),
		debug:              log.New(os.Stdout, "", log.LstdFlags),
		parsedSchemas:      make(map[*TypeSpecDef]*Schema),
		outputSchemas:      make(map[*TypeSpecDef]*Schema),
		existSchemaNames:   make(map[string]*Schema),
		toBeRenamedSchemas: make(map[string]string),
		excludes:           make(map[string]bool),
	}
	for _, option := range options {
		option(parser)
	}
	return parser
}
func SetMarkdownFileDirectory(directoryPath string) func(*Parser) {
	return func(p *Parser) {
		p.markdownFileDir = directoryPath
	}
}
func SetCodeExamplesDirectory(directoryPath string) func(*Parser) {
	return func(p *Parser) {
		p.codeExampleFilesDir = directoryPath
	}
}
func SetExcludedDirsAndFiles(excludes string) func(*Parser) {
	return func(p *Parser) {
		for _, f := range strings.Split(excludes, ",") {
			f = strings.TrimSpace(f)
			if f != "" {
				f = filepath.Clean(f)
				p.excludes[f] = true
			}
		}
	}
}
func SetStrict(strict bool) func(*Parser) {
	return func(p *Parser) {
		p.Strict = strict
	}
}
func SetDebugger(logger Debugger) func(parser *Parser) {
	return func(p *Parser) {
		p.debug = logger
	}
}
func (parser *Parser) ParseAPI(searchDir string, mainAPIFile string, parseDepth int) error {
	return parser.ParseAPIMultiSearchDir([]string{searchDir}, mainAPIFile, parseDepth)
}
func (parser *Parser) ParseAPIMultiSearchDir(searchDirs []string, mainAPIFile string, parseDepth int) error {
	for _, searchDir := range searchDirs {
		parser.debug.Printf("Generate general API Info, search dir:%s", searchDir)
		packageDir, err := getPkgName(searchDir)
		if err != nil {
			parser.debug.Printf("warning: failed to get package name in dir: %s, error: %s", searchDir, err.Error())
		}
		err = parser.getAllGoFileInfo(packageDir, searchDir)
		if err != nil {
			return err
		}
	}
	absMainAPIFilePath, err := filepath.Abs(filepath.Join(searchDirs[0], mainAPIFile))
	if err != nil {
		return err
	}
	if parser.ParseDependency {
		var t depth.Tree
		t.ResolveInternal = true
		t.MaxDepth = parseDepth
		pkgName, err := getPkgName(filepath.Dir(absMainAPIFilePath))
		if err != nil {
			return err
		}
		err = t.Resolve(pkgName)
		if err != nil {
			return fmt.Errorf("pkg %s cannot find all dependencies, %s", pkgName, err)
		}
		for i := 0; i < len(t.Root.Deps); i++ {
			err := parser.getAllGoFileInfoFromDeps(&t.Root.Deps[i])
			if err != nil {
				return err
			}
		}
	}
	err = parser.ParseGeneralAPIInfo(absMainAPIFilePath)
	if err != nil {
		return err
	}
	parser.parsedSchemas, err = parser.packages.ParseTypes()
	if err != nil {
		return err
	}
	err = parser.packages.RangeFiles(parser.ParseRouterAPIInfo)
	if err != nil {
		return err
	}
	parser.renameRefSchemas()
	return parser.checkOperationIDUniqueness()
}
func getPkgName(searchDir string) (string, error) {
	cmd := exec.Command("go", "list", "-f={{.ImportPath}}")
	cmd.Dir = searchDir
	var stdout, stderr strings.Builder
	cmd.Stdout = &stdout
	cmd.Stderr = &stderr
	if err := cmd.Run(); err != nil {
		return "", fmt.Errorf("execute go list command, %s, stdout:%s, stderr:%s", err, stdout.String(), stderr.String())
	}
	outStr, _ := stdout.String(), stderr.String()
	if outStr[0] == '_' {  
		outStr = strings.TrimPrefix(outStr, "_"+build.Default.GOPATH+"/src/")
	}
	f := strings.Split(outStr, "\n")
	outStr = f[0]
	return outStr, nil
}
func initIfEmpty(license *spec.License) *spec.License {
	if license == nil {
		return new(spec.License)
	}
	return license
}
func (parser *Parser) ParseGeneralAPIInfo(mainAPIFile string) error {
	fileTree, err := goparser.ParseFile(token.NewFileSet(), mainAPIFile, nil, goparser.ParseComments)
	if err != nil {
		return fmt.Errorf("cannot parse source files %s: %s", mainAPIFile, err)
	}
	parser.swagger.Swagger = "2.0"
	for _, comment := range fileTree.Comments {
		comments := strings.Split(comment.Text(), "\n")
		if !isGeneralAPIComment(comments) {
			continue
		}
		err := parseGeneralAPIInfo(parser, comments)
		if err != nil {
			return err
		}
	}
	return nil
}
func parseGeneralAPIInfo(parser *Parser, comments []string) error {
	previousAttribute := ""
	for i, commentLine := range comments {
		attribute := strings.Split(commentLine, " ")[0]
		value := strings.TrimSpace(commentLine[len(attribute):])
		multilineBlock := false
		if previousAttribute == attribute {
			multilineBlock = true
		}
		switch strings.ToLower(attribute) {
		case "@version":
			parser.swagger.Info.Version = value
		case "@title":
			parser.swagger.Info.Title = value
		case "@description":
			if multilineBlock {
				parser.swagger.Info.Description += "\n" + value
				continue
			}
			parser.swagger.Info.Description = value
		case "@description.markdown":
			commentInfo, err := getMarkdownForTag("api", parser.markdownFileDir)
			if err != nil {
				return err
			}
			parser.swagger.Info.Description = string(commentInfo)
		case "@termsofservice":
			parser.swagger.Info.TermsOfService = value
		case "@contact.name":
			parser.swagger.Info.Contact.Name = value
		case "@contact.email":
			parser.swagger.Info.Contact.Email = value
		case "@contact.url":
			parser.swagger.Info.Contact.URL = value
		case "@license.name":
			parser.swagger.Info.License = initIfEmpty(parser.swagger.Info.License)
			parser.swagger.Info.License.Name = value
		case "@license.url":
			parser.swagger.Info.License = initIfEmpty(parser.swagger.Info.License)
			parser.swagger.Info.License.URL = value
		case "@host":
			parser.swagger.Host = value
		case "@basepath":
			parser.swagger.BasePath = value
		case acceptAttr:
			err := parser.ParseAcceptComment(value)
			if err != nil {
				return err
			}
		case produceAttr:
			err := parser.ParseProduceComment(value)
			if err != nil {
				return err
			}
		case "@schemes":
			parser.swagger.Schemes = getSchemes(commentLine)
		case "@tag.name":
			parser.swagger.Tags = append(parser.swagger.Tags, spec.Tag{
				TagProps: spec.TagProps{
					Name: value,
				},
			})
		case "@tag.description":
			tag := parser.swagger.Tags[len(parser.swagger.Tags)-1]
			tag.TagProps.Description = value
			replaceLastTag(parser.swagger.Tags, tag)
		case "@tag.description.markdown":
			tag := parser.swagger.Tags[len(parser.swagger.Tags)-1]
			commentInfo, err := getMarkdownForTag(tag.TagProps.Name, parser.markdownFileDir)
			if err != nil {
				return err
			}
			tag.TagProps.Description = string(commentInfo)
			replaceLastTag(parser.swagger.Tags, tag)
		case "@tag.docs.url":
			tag := parser.swagger.Tags[len(parser.swagger.Tags)-1]
			tag.TagProps.ExternalDocs = &spec.ExternalDocumentation{
				URL: value,
			}
			replaceLastTag(parser.swagger.Tags, tag)
		case "@tag.docs.description":
			tag := parser.swagger.Tags[len(parser.swagger.Tags)-1]
			if tag.TagProps.ExternalDocs == nil {
				return fmt.Errorf("%s needs to come after a @tags.docs.url", attribute)
			}
			tag.TagProps.ExternalDocs.Description = value
			replaceLastTag(parser.swagger.Tags, tag)
		case "@securitydefinitions.basic":
			parser.swagger.SecurityDefinitions[value] = spec.BasicAuth()
		case "@securitydefinitions.apikey":
			attrMap, _, _, err := parseSecAttr(attribute, []string{"@in", "@name"}, comments[i+1:])
			if err != nil {
				return err
			}
			parser.swagger.SecurityDefinitions[value] = spec.APIKeyAuth(attrMap["@name"], attrMap["@in"])
		case "@securitydefinitions.oauth2.application":
			attrMap, scopes, extensions, err := parseSecAttr(attribute, []string{"@tokenurl"}, comments[i+1:])
			if err != nil {
				return err
			}
			parser.swagger.SecurityDefinitions[value] = secOAuth2Application(attrMap["@tokenurl"], scopes, extensions)
		case "@securitydefinitions.oauth2.implicit":
			attrs, scopes, ext, err := parseSecAttr(attribute, []string{"@authorizationurl"}, comments[i+1:])
			if err != nil {
				return err
			}
			parser.swagger.SecurityDefinitions[value] = secOAuth2Implicit(attrs["@authorizationurl"], scopes, ext)
		case "@securitydefinitions.oauth2.password":
			attrs, scopes, ext, err := parseSecAttr(attribute, []string{"@tokenurl"}, comments[i+1:])
			if err != nil {
				return err
			}
			parser.swagger.SecurityDefinitions[value] = secOAuth2Password(attrs["@tokenurl"], scopes, ext)
		case "@securitydefinitions.oauth2.accesscode":
			attrs, scopes, ext, err := parseSecAttr(attribute, []string{"@tokenurl", "@authorizationurl"}, comments[i+1:])
			if err != nil {
				return err
			}
			parser.swagger.SecurityDefinitions[value] = secOAuth2AccessToken(attrs["@authorizationurl"], attrs["@tokenurl"], scopes, ext)
		case "@query.collection.format":
			parser.collectionFormatInQuery = value
		default:
			prefixExtension := "@x-"
			if len(attribute) > 5 && attribute[:len(prefixExtension)] == prefixExtension {
				extExistsInSecurityDef := false
				for _, v := range parser.swagger.SecurityDefinitions {
					_, extExistsInSecurityDef = v.VendorExtensible.Extensions.GetString(attribute[1:])
					if extExistsInSecurityDef {
						break
					}
				}
				if extExistsInSecurityDef {
					break
				}
				var valueJSON interface{}
				split := strings.SplitAfter(commentLine, attribute+" ")
				if len(split) < 2 {
					return fmt.Errorf("annotation %s need a value", attribute)
				}
				extensionName := "x-" + strings.SplitAfter(attribute, prefixExtension)[1]
				err := json.Unmarshal([]byte(split[1]), &valueJSON)
				if err != nil {
					return fmt.Errorf("annotation %s need a valid json value", attribute)
				}
				if strings.Contains(extensionName, "logo") {
					parser.swagger.Info.Extensions.Add(extensionName, valueJSON)
				} else {
					if parser.swagger.Extensions == nil {
						parser.swagger.Extensions = make(map[string]interface{})
					}
					parser.swagger.Extensions[attribute[1:]] = valueJSON
				}
			}
		}
		previousAttribute = attribute
	}
	return nil
}
func (parser *Parser) ParseAcceptComment(commentLine string) error {
	return parseMimeTypeList(commentLine, &parser.swagger.Consumes, "%v accept type can't be accepted")
}
func (parser *Parser) ParseProduceComment(commentLine string) error {
	return parseMimeTypeList(commentLine, &parser.swagger.Produces, "%v produce type can't be accepted")
}
func isGeneralAPIComment(comments []string) bool {
	for _, commentLine := range comments {
		attribute := strings.ToLower(strings.Split(commentLine, " ")[0])
		switch attribute {
		case "@summary", "@router", "@success", "@failure", "@response":
			return false
		}
	}
	return true
}
func parseSecAttr(context string, search []string, lines []string) (map[string]string, map[string]string, map[string]interface{}, error) {
	attrMap := map[string]string{}
	scopes := map[string]string{}
	extensions := map[string]interface{}{}
	for _, v := range lines {
		securityAttr := strings.ToLower(strings.Split(v, " ")[0])
		for _, findterm := range search {
			if securityAttr == findterm {
				attrMap[securityAttr] = strings.TrimSpace(v[len(securityAttr):])
				continue
			}
		}
		isExists, err := isExistsScope(securityAttr)
		if err != nil {
			return nil, nil, nil, err
		}
		if isExists {
			scopes[securityAttr[len(scopeAttrPrefix):]] = v[len(securityAttr):]
		}
		if strings.HasPrefix(securityAttr, "@x-") {
			extensions[securityAttr[1:]] = strings.TrimSpace(v[len(securityAttr):])
		}
		if strings.Index(securityAttr, "@securitydefinitions.") == 0 {
			break
		}
	}
	if len(attrMap) != len(search) {
		return nil, nil, nil, fmt.Errorf("%s is %v required", context, search)
	}
	return attrMap, scopes, extensions, nil
}
func secOAuth2Application(tokenURL string, scopes map[string]string,
	extensions map[string]interface{}) *spec.SecurityScheme {
	securityScheme := spec.OAuth2Application(tokenURL)
	securityScheme.VendorExtensible.Extensions = handleSecuritySchemaExtensions(extensions)
	for scope, description := range scopes {
		securityScheme.AddScope(scope, description)
	}
	return securityScheme
}
func secOAuth2Implicit(authorizationURL string, scopes map[string]string,
	extensions map[string]interface{}) *spec.SecurityScheme {
	securityScheme := spec.OAuth2Implicit(authorizationURL)
	securityScheme.VendorExtensible.Extensions = handleSecuritySchemaExtensions(extensions)
	for scope, description := range scopes {
		securityScheme.AddScope(scope, description)
	}
	return securityScheme
}
func secOAuth2Password(tokenURL string, scopes map[string]string,
	extensions map[string]interface{}) *spec.SecurityScheme {
	securityScheme := spec.OAuth2Password(tokenURL)
	securityScheme.VendorExtensible.Extensions = handleSecuritySchemaExtensions(extensions)
	for scope, description := range scopes {
		securityScheme.AddScope(scope, description)
	}
	return securityScheme
}
func secOAuth2AccessToken(authorizationURL, tokenURL string,
	scopes map[string]string, extensions map[string]interface{}) *spec.SecurityScheme {
	securityScheme := spec.OAuth2AccessToken(authorizationURL, tokenURL)
	securityScheme.VendorExtensible.Extensions = handleSecuritySchemaExtensions(extensions)
	for scope, description := range scopes {
		securityScheme.AddScope(scope, description)
	}
	return securityScheme
}
func handleSecuritySchemaExtensions(providedExtensions map[string]interface{}) spec.Extensions {
	var extensions spec.Extensions
	if len(providedExtensions) > 0 {
		extensions = make(map[string]interface{}, len(providedExtensions))
		for key, value := range providedExtensions {
			extensions[key] = value
		}
	}
	return extensions
}
func getMarkdownForTag(tagName string, dirPath string) ([]byte, error) {
	filesInfos, err := ioutil.ReadDir(dirPath)
	if err != nil {
		return nil, err
	}
	for _, fileInfo := range filesInfos {
		if fileInfo.IsDir() {
			continue
		}
		fileName := fileInfo.Name()
		if !strings.Contains(fileName, ".md") {
			continue
		}
		if strings.Contains(fileName, tagName) {
			fullPath := filepath.Join(dirPath, fileName)
			commentInfo, err := ioutil.ReadFile(fullPath)
			if err != nil {
				return nil, fmt.Errorf("Failed to read markdown file %s error: %s ", fullPath, err)
			}
			return commentInfo, nil
		}
	}
	return nil, fmt.Errorf("Unable to find markdown file for tag %s in the given directory", tagName)
}
func isExistsScope(scope string) (bool, error) {
	s := strings.Fields(scope)
	for _, v := range s {
		if strings.Contains(v, scopeAttrPrefix) {
			if strings.Contains(v, ",") {
				return false, fmt.Errorf("@scope can't use comma(,) get=" + v)
			}
		}
	}
	return strings.Contains(scope, scopeAttrPrefix), nil
}
func getSchemes(commentLine string) []string {
	attribute := strings.ToLower(strings.Split(commentLine, " ")[0])
	return strings.Split(strings.TrimSpace(commentLine[len(attribute):]), " ")
}
func (parser *Parser) ParseRouterAPIInfo(fileName string, astFile *ast.File) error {
	for _, astDescription := range astFile.Decls {
		astDeclaration, ok := astDescription.(*ast.FuncDecl)
		if ok && astDeclaration.Doc != nil && astDeclaration.Doc.List != nil {
			operation := NewOperation(parser, SetCodeExampleFilesDirectory(parser.codeExampleFilesDir))
			for _, comment := range astDeclaration.Doc.List {
				err := operation.ParseComment(comment.Text, astFile)
				if err != nil {
					return fmt.Errorf("ParseComment error in file %s :%+v", fileName, err)
				}
			}
			for _, routeProperties := range operation.RouterProperties {
				var pathItem spec.PathItem
				var ok bool
				pathItem, ok = parser.swagger.Paths.Paths[routeProperties.Path]
				if !ok {
					pathItem = spec.PathItem{}
				}
				if hasRouteMethodOp(pathItem, routeProperties.HTTPMethod) {
					err := fmt.Errorf("route %s %s is declared multiple times", routeProperties.HTTPMethod, routeProperties.Path)
					if parser.Strict {
						return err
					}
					parser.debug.Printf("warning: %s\n", err)
				}
				setRouteMethodOp(&pathItem, routeProperties.HTTPMethod, &operation.Operation)
				parser.swagger.Paths.Paths[routeProperties.Path] = pathItem
			}
		}
	}
	return nil
}
func setRouteMethodOp(pathItem *spec.PathItem, method string, op *spec.Operation) {
	switch strings.ToUpper(method) {
	case http.MethodGet:
		pathItem.Get = op
	case http.MethodPost:
		pathItem.Post = op
	case http.MethodDelete:
		pathItem.Delete = op
	case http.MethodPut:
		pathItem.Put = op
	case http.MethodPatch:
		pathItem.Patch = op
	case http.MethodHead:
		pathItem.Head = op
	case http.MethodOptions:
		pathItem.Options = op
	}
}
func hasRouteMethodOp(pathItem spec.PathItem, method string) bool {
	switch strings.ToUpper(method) {
	case http.MethodGet:
		return pathItem.Get != nil
	case http.MethodPost:
		return pathItem.Post != nil
	case http.MethodDelete:
		return pathItem.Delete != nil
	case http.MethodPut:
		return pathItem.Put != nil
	case http.MethodPatch:
		return pathItem.Patch != nil
	case http.MethodHead:
		return pathItem.Head != nil
	case http.MethodOptions:
		return pathItem.Options != nil
	}
	return false
}
func convertFromSpecificToPrimitive(typeName string) (string, error) {
	name := typeName
	if strings.ContainsRune(name, '.') {
		name = strings.Split(name, ".")[1]
	}
	switch strings.ToUpper(name) {
	case "TIME", "OBJECTID", "UUID":
		return STRING, nil
	case "DECIMAL":
		return NUMBER, nil
	}
	return typeName, ErrFailedConvertPrimitiveType
}
func (parser *Parser) getTypeSchema(typeName string, file *ast.File, ref bool) (*spec.Schema, error) {
	if IsGolangPrimitiveType(typeName) {
		return PrimitiveSchema(TransToValidSchemeType(typeName)), nil
	}
	schemaType, err := convertFromSpecificToPrimitive(typeName)
	if err == nil {
		return PrimitiveSchema(schemaType), nil
	}
	typeSpecDef := parser.packages.FindTypeSpec(typeName, file, parser.ParseDependency)
	if typeSpecDef == nil {
		return nil, fmt.Errorf("cannot find type definition: %s", typeName)
	}
	schema, ok := parser.parsedSchemas[typeSpecDef]
	if !ok {
		var err error
		schema, err = parser.ParseDefinition(typeSpecDef)
		if err != nil {
			if err == ErrRecursiveParseStruct && ref {
				return parser.getRefTypeSchema(typeSpecDef, schema), nil
			}
			return nil, err
		}
	}
	if ref && len(schema.Schema.Type) > 0 && schema.Schema.Type[0] == OBJECT {
		return parser.getRefTypeSchema(typeSpecDef, schema), nil
	}
	return schema.Schema, nil
}
func (parser *Parser) renameRefSchemas() {
	if len(parser.toBeRenamedSchemas) == 0 {
		return
	}
	for name, pkgPath := range parser.toBeRenamedSchemas {
		if schema, ok := parser.swagger.Definitions[name]; ok {
			delete(parser.swagger.Definitions, name)
			name = parser.renameSchema(name, pkgPath)
			parser.swagger.Definitions[name] = schema
		}
	}
	for _, refURL := range parser.toBeRenamedRefURLs {
		parts := strings.Split(refURL.Fragment, "/")
		name := parts[len(parts)-1]
		if pkgPath, ok := parser.toBeRenamedSchemas[name]; ok {
			parts[len(parts)-1] = parser.renameSchema(name, pkgPath)
			refURL.Fragment = strings.Join(parts, "/")
		}
	}
}
func (parser *Parser) renameSchema(name, pkgPath string) string {
	parts := strings.Split(name, ".")
	name = fullTypeName(pkgPath, parts[len(parts)-1])
	name = strings.ReplaceAll(name, "/", "_")
	return name
}
func (parser *Parser) getRefTypeSchema(typeSpecDef *TypeSpecDef, schema *Schema) *spec.Schema {
	_, ok := parser.outputSchemas[typeSpecDef]
	if !ok {
		existSchema, ok := parser.existSchemaNames[schema.Name]
		if ok {
			_, ok = parser.toBeRenamedSchemas[existSchema.Name]
			if !ok {
				parser.toBeRenamedSchemas[existSchema.Name] = existSchema.PkgPath
			}
			schema.Name = parser.renameSchema(schema.Name, schema.PkgPath)
		} else {
			parser.existSchemaNames[schema.Name] = schema
		}
		parser.swagger.Definitions[schema.Name] = spec.Schema{}
		if schema.Schema != nil {
			parser.swagger.Definitions[schema.Name] = *schema.Schema
		}
		parser.outputSchemas[typeSpecDef] = schema
	}
	refSchema := RefSchema(schema.Name)
	parser.toBeRenamedRefURLs = append(parser.toBeRenamedRefURLs, refSchema.Ref.Ref.GetURL())
	return refSchema
}
func (parser *Parser) isInStructStack(typeSpecDef *TypeSpecDef) bool {
	for _, specDef := range parser.structStack {
		if typeSpecDef == specDef {
			return true
		}
	}
	return false
}