forked from ebhomengo/niki
1
0
Fork 0
niki/vendor/github.com/swaggo/swag/package.go

335 lines
5.9 KiB
Go
Raw Normal View History

2024-05-14 13:07:09 +00:00
package swag
import (
"go/ast"
"go/token"
"reflect"
"strconv"
"strings"
)
// PackageDefinitions files and definition in a package.
2024-05-14 13:07:09 +00:00
type PackageDefinitions struct {
2024-05-14 13:07:09 +00:00
// files in this package, map key is file's relative path starting package path
2024-05-14 13:07:09 +00:00
Files map[string]*ast.File
// definitions in this package, map key is typeName
2024-05-14 13:07:09 +00:00
TypeDefinitions map[string]*TypeSpecDef
// const variables in this package, map key is the name
2024-05-14 13:07:09 +00:00
ConstTable map[string]*ConstVariable
// const variables in order in this package
2024-05-14 13:07:09 +00:00
OrderedConst []*ConstVariable
// package name
2024-05-14 13:07:09 +00:00
Name string
// package path
2024-05-14 13:07:09 +00:00
Path string
}
// ConstVariableGlobalEvaluator an interface used to evaluate enums across packages
2024-05-14 13:07:09 +00:00
type ConstVariableGlobalEvaluator interface {
EvaluateConstValue(pkg *PackageDefinitions, cv *ConstVariable, recursiveStack map[string]struct{}) (interface{}, ast.Expr)
2024-05-14 13:07:09 +00:00
EvaluateConstValueByName(file *ast.File, pkgPath, constVariableName string, recursiveStack map[string]struct{}) (interface{}, ast.Expr)
2024-05-14 13:07:09 +00:00
FindTypeSpec(typeName string, file *ast.File) *TypeSpecDef
}
// NewPackageDefinitions new a PackageDefinitions object
2024-05-14 13:07:09 +00:00
func NewPackageDefinitions(name, pkgPath string) *PackageDefinitions {
2024-05-14 13:07:09 +00:00
return &PackageDefinitions{
Name: name,
Path: pkgPath,
Files: make(map[string]*ast.File),
2024-05-14 13:07:09 +00:00
TypeDefinitions: make(map[string]*TypeSpecDef),
ConstTable: make(map[string]*ConstVariable),
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}
// AddFile add a file
2024-05-14 13:07:09 +00:00
func (pkg *PackageDefinitions) AddFile(pkgPath string, file *ast.File) *PackageDefinitions {
2024-05-14 13:07:09 +00:00
pkg.Files[pkgPath] = file
2024-05-14 13:07:09 +00:00
return pkg
2024-05-14 13:07:09 +00:00
}
// AddTypeSpec add a type spec.
2024-05-14 13:07:09 +00:00
func (pkg *PackageDefinitions) AddTypeSpec(name string, typeSpec *TypeSpecDef) *PackageDefinitions {
2024-05-14 13:07:09 +00:00
pkg.TypeDefinitions[name] = typeSpec
2024-05-14 13:07:09 +00:00
return pkg
2024-05-14 13:07:09 +00:00
}
// AddConst add a const variable.
2024-05-14 13:07:09 +00:00
func (pkg *PackageDefinitions) AddConst(astFile *ast.File, valueSpec *ast.ValueSpec) *PackageDefinitions {
2024-05-14 13:07:09 +00:00
for i := 0; i < len(valueSpec.Names) && i < len(valueSpec.Values); i++ {
2024-05-14 13:07:09 +00:00
variable := &ConstVariable{
Name: valueSpec.Names[i],
Type: valueSpec.Type,
Value: valueSpec.Values[i],
2024-05-14 13:07:09 +00:00
Comment: valueSpec.Comment,
File: astFile,
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
pkg.ConstTable[valueSpec.Names[i].Name] = variable
2024-05-14 13:07:09 +00:00
pkg.OrderedConst = append(pkg.OrderedConst, variable)
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
return pkg
2024-05-14 13:07:09 +00:00
}
func (pkg *PackageDefinitions) evaluateConstValue(file *ast.File, iota int, expr ast.Expr, globalEvaluator ConstVariableGlobalEvaluator, recursiveStack map[string]struct{}) (interface{}, ast.Expr) {
2024-05-14 13:07:09 +00:00
switch valueExpr := expr.(type) {
2024-05-14 13:07:09 +00:00
case *ast.Ident:
2024-05-14 13:07:09 +00:00
if valueExpr.Name == "iota" {
2024-05-14 13:07:09 +00:00
return iota, nil
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
if pkg.ConstTable != nil {
2024-05-14 13:07:09 +00:00
if cv, ok := pkg.ConstTable[valueExpr.Name]; ok {
2024-05-14 13:07:09 +00:00
return globalEvaluator.EvaluateConstValue(pkg, cv, recursiveStack)
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
case *ast.SelectorExpr:
2024-05-14 13:07:09 +00:00
pkgIdent, ok := valueExpr.X.(*ast.Ident)
2024-05-14 13:07:09 +00:00
if !ok {
2024-05-14 13:07:09 +00:00
return nil, nil
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
return globalEvaluator.EvaluateConstValueByName(file, pkgIdent.Name, valueExpr.Sel.Name, recursiveStack)
2024-05-14 13:07:09 +00:00
case *ast.BasicLit:
2024-05-14 13:07:09 +00:00
switch valueExpr.Kind {
2024-05-14 13:07:09 +00:00
case token.INT:
2024-05-14 13:07:09 +00:00
// handle underscored number, such as 1_000_000
2024-05-14 13:07:09 +00:00
if strings.ContainsRune(valueExpr.Value, '_') {
2024-05-14 13:07:09 +00:00
valueExpr.Value = strings.Replace(valueExpr.Value, "_", "", -1)
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
if len(valueExpr.Value) >= 2 && valueExpr.Value[0] == '0' {
2024-05-14 13:07:09 +00:00
var start, base = 2, 8
2024-05-14 13:07:09 +00:00
switch valueExpr.Value[1] {
2024-05-14 13:07:09 +00:00
case 'x', 'X':
2024-05-14 13:07:09 +00:00
//hex
2024-05-14 13:07:09 +00:00
base = 16
2024-05-14 13:07:09 +00:00
case 'b', 'B':
2024-05-14 13:07:09 +00:00
//binary
2024-05-14 13:07:09 +00:00
base = 2
2024-05-14 13:07:09 +00:00
default:
2024-05-14 13:07:09 +00:00
//octet
2024-05-14 13:07:09 +00:00
start = 1
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
if x, err := strconv.ParseInt(valueExpr.Value[start:], base, 64); err == nil {
2024-05-14 13:07:09 +00:00
return int(x), nil
2024-05-14 13:07:09 +00:00
} else if x, err := strconv.ParseUint(valueExpr.Value[start:], base, 64); err == nil {
2024-05-14 13:07:09 +00:00
return x, nil
2024-05-14 13:07:09 +00:00
} else {
2024-05-14 13:07:09 +00:00
panic(err)
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}
//a basic literal integer is int type in default, or must have an explicit converting type in front
2024-05-14 13:07:09 +00:00
if x, err := strconv.ParseInt(valueExpr.Value, 10, 64); err == nil {
2024-05-14 13:07:09 +00:00
return int(x), nil
2024-05-14 13:07:09 +00:00
} else if x, err := strconv.ParseUint(valueExpr.Value, 10, 64); err == nil {
2024-05-14 13:07:09 +00:00
return x, nil
2024-05-14 13:07:09 +00:00
} else {
2024-05-14 13:07:09 +00:00
panic(err)
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
case token.STRING:
2024-05-14 13:07:09 +00:00
if valueExpr.Value[0] == '`' {
2024-05-14 13:07:09 +00:00
return valueExpr.Value[1 : len(valueExpr.Value)-1], nil
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
return EvaluateEscapedString(valueExpr.Value[1 : len(valueExpr.Value)-1]), nil
2024-05-14 13:07:09 +00:00
case token.CHAR:
2024-05-14 13:07:09 +00:00
return EvaluateEscapedChar(valueExpr.Value[1 : len(valueExpr.Value)-1]), nil
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
case *ast.UnaryExpr:
2024-05-14 13:07:09 +00:00
x, evalType := pkg.evaluateConstValue(file, iota, valueExpr.X, globalEvaluator, recursiveStack)
2024-05-14 13:07:09 +00:00
if x == nil {
2024-05-14 13:07:09 +00:00
return x, evalType
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
return EvaluateUnary(x, valueExpr.Op, evalType)
2024-05-14 13:07:09 +00:00
case *ast.BinaryExpr:
2024-05-14 13:07:09 +00:00
x, evalTypex := pkg.evaluateConstValue(file, iota, valueExpr.X, globalEvaluator, recursiveStack)
2024-05-14 13:07:09 +00:00
y, evalTypey := pkg.evaluateConstValue(file, iota, valueExpr.Y, globalEvaluator, recursiveStack)
2024-05-14 13:07:09 +00:00
if x == nil || y == nil {
2024-05-14 13:07:09 +00:00
return nil, nil
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
return EvaluateBinary(x, y, valueExpr.Op, evalTypex, evalTypey)
2024-05-14 13:07:09 +00:00
case *ast.ParenExpr:
2024-05-14 13:07:09 +00:00
return pkg.evaluateConstValue(file, iota, valueExpr.X, globalEvaluator, recursiveStack)
2024-05-14 13:07:09 +00:00
case *ast.CallExpr:
2024-05-14 13:07:09 +00:00
//data conversion
2024-05-14 13:07:09 +00:00
if len(valueExpr.Args) != 1 {
2024-05-14 13:07:09 +00:00
return nil, nil
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
arg := valueExpr.Args[0]
2024-05-14 13:07:09 +00:00
if ident, ok := valueExpr.Fun.(*ast.Ident); ok {
2024-05-14 13:07:09 +00:00
name := ident.Name
2024-05-14 13:07:09 +00:00
if name == "uintptr" {
2024-05-14 13:07:09 +00:00
name = "uint"
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
value, _ := pkg.evaluateConstValue(file, iota, arg, globalEvaluator, recursiveStack)
2024-05-14 13:07:09 +00:00
if IsGolangPrimitiveType(name) {
2024-05-14 13:07:09 +00:00
value = EvaluateDataConversion(value, name)
2024-05-14 13:07:09 +00:00
return value, nil
2024-05-14 13:07:09 +00:00
} else if name == "len" {
2024-05-14 13:07:09 +00:00
return reflect.ValueOf(value).Len(), nil
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
typeDef := globalEvaluator.FindTypeSpec(name, file)
2024-05-14 13:07:09 +00:00
if typeDef == nil {
2024-05-14 13:07:09 +00:00
return nil, nil
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
return value, valueExpr.Fun
2024-05-14 13:07:09 +00:00
} else if selector, ok := valueExpr.Fun.(*ast.SelectorExpr); ok {
2024-05-14 13:07:09 +00:00
typeDef := globalEvaluator.FindTypeSpec(fullTypeName(selector.X.(*ast.Ident).Name, selector.Sel.Name), file)
2024-05-14 13:07:09 +00:00
if typeDef == nil {
2024-05-14 13:07:09 +00:00
return nil, nil
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
return arg, typeDef.TypeSpec.Type
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
}
2024-05-14 13:07:09 +00:00
return nil, nil
2024-05-14 13:07:09 +00:00
}