Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* parse global enums
- Loading branch information
Showing
13 changed files
with
542 additions
and
39 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
package swag | ||
|
||
import ( | ||
"go/ast" | ||
"go/token" | ||
"strconv" | ||
) | ||
|
||
// ConstVariable a model to record an const variable | ||
type ConstVariable struct { | ||
Name *ast.Ident | ||
Type ast.Expr | ||
Value interface{} | ||
Comment *ast.CommentGroup | ||
} | ||
|
||
// EvaluateValue evaluate the value | ||
func (cv *ConstVariable) EvaluateValue(constTable map[string]*ConstVariable) interface{} { | ||
if expr, ok := cv.Value.(ast.Expr); ok { | ||
value, evalType := evaluateConstValue(cv.Name.Name, cv.Name.Obj.Data.(int), expr, constTable, make(map[string]struct{})) | ||
if cv.Type == nil && evalType != nil { | ||
cv.Type = evalType | ||
} | ||
if value != nil { | ||
cv.Value = value | ||
} | ||
return value | ||
} | ||
return cv.Value | ||
} | ||
|
||
func evaluateConstValue(name string, iota int, expr ast.Expr, constTable map[string]*ConstVariable, recursiveStack map[string]struct{}) (interface{}, ast.Expr) { | ||
if len(name) > 0 { | ||
if _, ok := recursiveStack[name]; ok { | ||
return nil, nil | ||
} | ||
recursiveStack[name] = struct{}{} | ||
} | ||
|
||
switch valueExpr := expr.(type) { | ||
case *ast.Ident: | ||
if valueExpr.Name == "iota" { | ||
return iota, nil | ||
} | ||
if constTable != nil { | ||
if cv, ok := constTable[valueExpr.Name]; ok { | ||
if expr, ok = cv.Value.(ast.Expr); ok { | ||
value, evalType := evaluateConstValue(valueExpr.Name, cv.Name.Obj.Data.(int), expr, constTable, recursiveStack) | ||
if cv.Type == nil { | ||
cv.Type = evalType | ||
} | ||
if value != nil { | ||
cv.Value = value | ||
} | ||
return value, evalType | ||
} | ||
return cv.Value, cv.Type | ||
} | ||
} | ||
case *ast.BasicLit: | ||
switch valueExpr.Kind { | ||
case token.INT: | ||
x, err := strconv.ParseInt(valueExpr.Value, 10, 64) | ||
if err != nil { | ||
return nil, nil | ||
} | ||
return int(x), nil | ||
case token.STRING, token.CHAR: | ||
return valueExpr.Value[1 : len(valueExpr.Value)-1], nil | ||
} | ||
case *ast.UnaryExpr: | ||
x, evalType := evaluateConstValue("", iota, valueExpr.X, constTable, recursiveStack) | ||
switch valueExpr.Op { | ||
case token.SUB: | ||
return -x.(int), evalType | ||
case token.XOR: | ||
return ^(x.(int)), evalType | ||
} | ||
case *ast.BinaryExpr: | ||
x, evalTypex := evaluateConstValue("", iota, valueExpr.X, constTable, recursiveStack) | ||
y, evalTypey := evaluateConstValue("", iota, valueExpr.Y, constTable, recursiveStack) | ||
evalType := evalTypex | ||
if evalType == nil { | ||
evalType = evalTypey | ||
} | ||
switch valueExpr.Op { | ||
case token.ADD: | ||
if ix, ok := x.(int); ok { | ||
return ix + y.(int), evalType | ||
} else if sx, ok := x.(string); ok { | ||
return sx + y.(string), evalType | ||
} | ||
case token.SUB: | ||
return x.(int) - y.(int), evalType | ||
case token.MUL: | ||
return x.(int) * y.(int), evalType | ||
case token.QUO: | ||
return x.(int) / y.(int), evalType | ||
case token.REM: | ||
return x.(int) % y.(int), evalType | ||
case token.AND: | ||
return x.(int) & y.(int), evalType | ||
case token.OR: | ||
return x.(int) | y.(int), evalType | ||
case token.XOR: | ||
return x.(int) ^ y.(int), evalType | ||
case token.SHL: | ||
return x.(int) << y.(int), evalType | ||
case token.SHR: | ||
return x.(int) >> y.(int), evalType | ||
} | ||
case *ast.ParenExpr: | ||
return evaluateConstValue("", iota, valueExpr.X, constTable, recursiveStack) | ||
case *ast.CallExpr: | ||
//data conversion | ||
if ident, ok := valueExpr.Fun.(*ast.Ident); ok && len(valueExpr.Args) == 1 && IsGolangPrimitiveType(ident.Name) { | ||
arg, _ := evaluateConstValue("", iota, valueExpr.Args[0], constTable, recursiveStack) | ||
return arg, nil | ||
} | ||
} | ||
return nil, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package swag | ||
|
||
const ( | ||
enumVarNamesExtension = "x-enum-varnames" | ||
enumCommentsExtension = "x-enum-comments" | ||
) | ||
|
||
// EnumValue a model to record an enum const variable | ||
type EnumValue struct { | ||
key string | ||
Value interface{} | ||
Comment string | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package swag | ||
|
||
import ( | ||
"encoding/json" | ||
"os" | ||
"path/filepath" | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
func TestParseGlobalEnums(t *testing.T) { | ||
searchDir := "testdata/enums" | ||
expected, err := os.ReadFile(filepath.Join(searchDir, "expected.json")) | ||
assert.NoError(t, err) | ||
|
||
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, string(expected), string(b)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
package swag | ||
|
||
import "go/ast" | ||
|
||
// PackageDefinitions files and definition in a package. | ||
type PackageDefinitions struct { | ||
// files in this package, map key is file's relative path starting package path | ||
Files map[string]*ast.File | ||
|
||
// definitions in this package, map key is typeName | ||
TypeDefinitions map[string]*TypeSpecDef | ||
|
||
// const variables in this package, map key is the name | ||
ConstTable map[string]*ConstVariable | ||
|
||
// const variables in order in this package | ||
OrderedConst []*ConstVariable | ||
|
||
// package name | ||
Name string | ||
} | ||
|
||
// NewPackageDefinitions new a PackageDefinitions object | ||
func NewPackageDefinitions(name string) *PackageDefinitions { | ||
return &PackageDefinitions{ | ||
Name: name, | ||
Files: make(map[string]*ast.File), | ||
TypeDefinitions: make(map[string]*TypeSpecDef), | ||
ConstTable: make(map[string]*ConstVariable), | ||
} | ||
} | ||
|
||
// AddFile add a file | ||
func (pkg *PackageDefinitions) AddFile(pkgPath string, file *ast.File) *PackageDefinitions { | ||
pkg.Files[pkgPath] = file | ||
return pkg | ||
} | ||
|
||
// AddTypeSpec add a type spec. | ||
func (pkg *PackageDefinitions) AddTypeSpec(name string, typeSpec *TypeSpecDef) *PackageDefinitions { | ||
pkg.TypeDefinitions[name] = typeSpec | ||
return pkg | ||
} | ||
|
||
// AddConst add a const variable. | ||
func (pkg *PackageDefinitions) AddConst(valueSpec *ast.ValueSpec) *PackageDefinitions { | ||
for i := 0; i < len(valueSpec.Names) && i < len(valueSpec.Values); i++ { | ||
variable := &ConstVariable{ | ||
Name: valueSpec.Names[i], | ||
Type: valueSpec.Type, | ||
Value: valueSpec.Values[i], | ||
Comment: valueSpec.Comment, | ||
} | ||
pkg.ConstTable[valueSpec.Names[i].Name] = variable | ||
pkg.OrderedConst = append(pkg.OrderedConst, variable) | ||
} | ||
return pkg | ||
} |
Oops, something went wrong.