后端重构
This commit is contained in:
@@ -0,0 +1,423 @@
|
||||
package ast
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"log"
|
||||
)
|
||||
|
||||
// AddImport 增加 import 方法
|
||||
func AddImport(astNode ast.Node, imp string) {
|
||||
impStr := fmt.Sprintf("\"%s\"", imp)
|
||||
ast.Inspect(astNode, func(node ast.Node) bool {
|
||||
if genDecl, ok := node.(*ast.GenDecl); ok {
|
||||
if genDecl.Tok == token.IMPORT {
|
||||
for i := range genDecl.Specs {
|
||||
if impNode, ok := genDecl.Specs[i].(*ast.ImportSpec); ok {
|
||||
if impNode.Path.Value == impStr {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
genDecl.Specs = append(genDecl.Specs, &ast.ImportSpec{
|
||||
Path: &ast.BasicLit{
|
||||
Kind: token.STRING,
|
||||
Value: impStr,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
// FindFunction 查询特定function方法
|
||||
func FindFunction(astNode ast.Node, FunctionName string) *ast.FuncDecl {
|
||||
var funcDeclP *ast.FuncDecl
|
||||
ast.Inspect(astNode, func(node ast.Node) bool {
|
||||
if funcDecl, ok := node.(*ast.FuncDecl); ok {
|
||||
if funcDecl.Name.String() == FunctionName {
|
||||
funcDeclP = funcDecl
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
return funcDeclP
|
||||
}
|
||||
|
||||
// FindArray 查询特定数组方法
|
||||
func FindArray(astNode ast.Node, identName, selectorExprName string) *ast.CompositeLit {
|
||||
var assignStmt *ast.CompositeLit
|
||||
ast.Inspect(astNode, func(n ast.Node) bool {
|
||||
switch node := n.(type) {
|
||||
case *ast.AssignStmt:
|
||||
for _, expr := range node.Rhs {
|
||||
if exprType, ok := expr.(*ast.CompositeLit); ok {
|
||||
if arrayType, ok := exprType.Type.(*ast.ArrayType); ok {
|
||||
sel, ok1 := arrayType.Elt.(*ast.SelectorExpr)
|
||||
x, ok2 := sel.X.(*ast.Ident)
|
||||
if ok1 && ok2 && x.Name == identName && sel.Sel.Name == selectorExprName {
|
||||
assignStmt = exprType
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
return assignStmt
|
||||
}
|
||||
|
||||
// CheckImport 检查是否存在Import
|
||||
func CheckImport(file *ast.File, importPath string) bool {
|
||||
for _, imp := range file.Imports {
|
||||
path := imp.Path.Value[1 : len(imp.Path.Value)-1]
|
||||
if path == importPath {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ClearPosition 清除AST节点位置信息
|
||||
func ClearPosition(astNode ast.Node) {
|
||||
ast.Inspect(astNode, func(n ast.Node) bool {
|
||||
switch node := n.(type) {
|
||||
case *ast.Ident:
|
||||
node.NamePos = token.NoPos
|
||||
case *ast.CallExpr:
|
||||
node.Lparen = token.NoPos
|
||||
node.Rparen = token.NoPos
|
||||
case *ast.BasicLit:
|
||||
node.ValuePos = token.NoPos
|
||||
case *ast.SelectorExpr:
|
||||
node.Sel.NamePos = token.NoPos
|
||||
case *ast.BinaryExpr:
|
||||
node.OpPos = token.NoPos
|
||||
case *ast.UnaryExpr:
|
||||
node.OpPos = token.NoPos
|
||||
case *ast.StarExpr:
|
||||
node.Star = token.NoPos
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
// CreateStmt 创建语句
|
||||
func CreateStmt(statement string) *ast.ExprStmt {
|
||||
expr, err := parser.ParseExpr(statement)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
ClearPosition(expr)
|
||||
return &ast.ExprStmt{X: expr}
|
||||
}
|
||||
|
||||
// IsBlockStmt 判断是否为块语句
|
||||
func IsBlockStmt(node ast.Node) bool {
|
||||
_, ok := node.(*ast.BlockStmt)
|
||||
return ok
|
||||
}
|
||||
|
||||
// VariableExistsInBlock 检查变量是否存在于块中
|
||||
func VariableExistsInBlock(block *ast.BlockStmt, varName string) bool {
|
||||
exists := false
|
||||
ast.Inspect(block, func(n ast.Node) bool {
|
||||
switch node := n.(type) {
|
||||
case *ast.AssignStmt:
|
||||
for _, expr := range node.Lhs {
|
||||
if ident, ok := expr.(*ast.Ident); ok && ident.Name == varName {
|
||||
exists = true
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
return exists
|
||||
}
|
||||
|
||||
// RemoveImport 移除 import
|
||||
func RemoveImport(file *ast.File, importPath string) {
|
||||
impStr := fmt.Sprintf("\"%s\"", importPath)
|
||||
for i := 0; i < len(file.Decls); i++ {
|
||||
if genDecl, ok := file.Decls[i].(*ast.GenDecl); ok && genDecl.Tok == token.IMPORT {
|
||||
for j := 0; j < len(genDecl.Specs); j++ {
|
||||
if impSpec, ok := genDecl.Specs[j].(*ast.ImportSpec); ok {
|
||||
if impSpec.Path.Value == impStr {
|
||||
genDecl.Specs = append(genDecl.Specs[:j], genDecl.Specs[j+1:]...)
|
||||
if len(genDecl.Specs) == 0 {
|
||||
file.Decls = append(file.Decls[:i], file.Decls[i+1:]...)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// AddStructField 添加结构体字段
|
||||
func AddStructField(file *ast.File, structName, fieldName, fieldType string) {
|
||||
ast.Inspect(file, func(node ast.Node) bool {
|
||||
if typeSpec, ok := node.(*ast.TypeSpec); ok && typeSpec.Name.Name == structName {
|
||||
if structType, ok := typeSpec.Type.(*ast.StructType); ok {
|
||||
// 检查字段是否已存在
|
||||
for _, field := range structType.Fields.List {
|
||||
for _, name := range field.Names {
|
||||
if name.Name == fieldName {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
// 添加新字段
|
||||
newField := &ast.Field{
|
||||
Names: []*ast.Ident{{Name: fieldName}},
|
||||
Type: &ast.Ident{Name: fieldType},
|
||||
}
|
||||
structType.Fields.List = append(structType.Fields.List, newField)
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
// RemoveStructField 移除结构体字段
|
||||
func RemoveStructField(file *ast.File, structName, fieldName string) {
|
||||
ast.Inspect(file, func(node ast.Node) bool {
|
||||
if typeSpec, ok := node.(*ast.TypeSpec); ok && typeSpec.Name.Name == structName {
|
||||
if structType, ok := typeSpec.Type.(*ast.StructType); ok {
|
||||
for i, field := range structType.Fields.List {
|
||||
for _, name := range field.Names {
|
||||
if name.Name == fieldName {
|
||||
structType.Fields.List = append(structType.Fields.List[:i], structType.Fields.List[i+1:]...)
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
// AddFuncCall 在函数体中添加函数调用
|
||||
func AddFuncCall(file *ast.File, funcName, callExpr string) {
|
||||
funcDecl := FindFunction(file, funcName)
|
||||
if funcDecl == nil || funcDecl.Body == nil {
|
||||
return
|
||||
}
|
||||
stmt := CreateStmt(callExpr)
|
||||
funcDecl.Body.List = append(funcDecl.Body.List, stmt)
|
||||
}
|
||||
|
||||
// RemoveFuncCall 从函数体中移除函数调用
|
||||
func RemoveFuncCall(file *ast.File, funcName, targetFunc string) {
|
||||
funcDecl := FindFunction(file, funcName)
|
||||
if funcDecl == nil || funcDecl.Body == nil {
|
||||
return
|
||||
}
|
||||
for i := 0; i < len(funcDecl.Body.List); i++ {
|
||||
if exprStmt, ok := funcDecl.Body.List[i].(*ast.ExprStmt); ok {
|
||||
if callExpr, ok := exprStmt.X.(*ast.CallExpr); ok {
|
||||
if selExpr, ok := callExpr.Fun.(*ast.SelectorExpr); ok {
|
||||
if selExpr.Sel.Name == targetFunc {
|
||||
funcDecl.Body.List = append(funcDecl.Body.List[:i], funcDecl.Body.List[i+1:]...)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// AddAutoMigrateModel 添加 AutoMigrate 模型
|
||||
func AddAutoMigrateModel(file *ast.File, modelExpr string) {
|
||||
ast.Inspect(file, func(node ast.Node) bool {
|
||||
if callExpr, ok := node.(*ast.CallExpr); ok {
|
||||
if selExpr, ok := callExpr.Fun.(*ast.SelectorExpr); ok {
|
||||
if selExpr.Sel.Name == "AutoMigrate" {
|
||||
// 检查是否已存在
|
||||
for _, arg := range callExpr.Args {
|
||||
if unaryExpr, ok := arg.(*ast.UnaryExpr); ok {
|
||||
if compLit, ok := unaryExpr.X.(*ast.CompositeLit); ok {
|
||||
if selType, ok := compLit.Type.(*ast.SelectorExpr); ok {
|
||||
existingModel := fmt.Sprintf("&%s.%s{}", selType.X.(*ast.Ident).Name, selType.Sel.Name)
|
||||
if existingModel == modelExpr {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 添加新模型
|
||||
expr, err := parser.ParseExpr(modelExpr)
|
||||
if err == nil {
|
||||
ClearPosition(expr)
|
||||
callExpr.Args = append(callExpr.Args, expr)
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
// RemoveAutoMigrateModel 移除 AutoMigrate 模型
|
||||
func RemoveAutoMigrateModel(file *ast.File, modelName string) {
|
||||
ast.Inspect(file, func(node ast.Node) bool {
|
||||
if callExpr, ok := node.(*ast.CallExpr); ok {
|
||||
if selExpr, ok := callExpr.Fun.(*ast.SelectorExpr); ok {
|
||||
if selExpr.Sel.Name == "AutoMigrate" {
|
||||
for i := 0; i < len(callExpr.Args); i++ {
|
||||
if unaryExpr, ok := callExpr.Args[i].(*ast.UnaryExpr); ok {
|
||||
if compLit, ok := unaryExpr.X.(*ast.CompositeLit); ok {
|
||||
if selType, ok := compLit.Type.(*ast.SelectorExpr); ok {
|
||||
existingModel := fmt.Sprintf("%s.%s", selType.X.(*ast.Ident).Name, selType.Sel.Name)
|
||||
if existingModel == modelName {
|
||||
callExpr.Args = append(callExpr.Args[:i], callExpr.Args[i+1:]...)
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
// AddGormGenModel 向 GORM Gen 的 g.ApplyBasic 调用中添加新模型
|
||||
// 用于自动更新 cmd/gen/main.go 文件
|
||||
func AddGormGenModel(file *ast.File, tableName string) {
|
||||
ast.Inspect(file, func(node ast.Node) bool {
|
||||
if callExpr, ok := node.(*ast.CallExpr); ok {
|
||||
if selExpr, ok := callExpr.Fun.(*ast.SelectorExpr); ok {
|
||||
// 查找 g.ApplyBasic 调用
|
||||
if ident, ok := selExpr.X.(*ast.Ident); ok && ident.Name == "g" && selExpr.Sel.Name == "ApplyBasic" {
|
||||
// 检查是否已存在该表
|
||||
for _, arg := range callExpr.Args {
|
||||
if call, ok := arg.(*ast.CallExpr); ok {
|
||||
if sel, ok := call.Fun.(*ast.SelectorExpr); ok {
|
||||
if sel.Sel.Name == "GenerateModel" && len(call.Args) > 0 {
|
||||
if lit, ok := call.Args[0].(*ast.BasicLit); ok {
|
||||
existingTable := lit.Value[1 : len(lit.Value)-1] // 去掉引号
|
||||
if existingTable == tableName {
|
||||
return false // 已存在,不需要添加
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 添加新模型: g.GenerateModel("tableName", fieldOpts...)
|
||||
newArg := &ast.CallExpr{
|
||||
Fun: &ast.SelectorExpr{
|
||||
X: &ast.Ident{Name: "g"},
|
||||
Sel: &ast.Ident{Name: "GenerateModel"},
|
||||
},
|
||||
Args: []ast.Expr{
|
||||
&ast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf("\"%s\"", tableName)},
|
||||
&ast.Ident{Name: "fieldOpts..."},
|
||||
},
|
||||
}
|
||||
callExpr.Args = append(callExpr.Args, newArg)
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
// RemoveGormGenModel 从 GORM Gen 的 g.ApplyBasic 调用中移除模型
|
||||
func RemoveGormGenModel(file *ast.File, tableName string) {
|
||||
ast.Inspect(file, func(node ast.Node) bool {
|
||||
if callExpr, ok := node.(*ast.CallExpr); ok {
|
||||
if selExpr, ok := callExpr.Fun.(*ast.SelectorExpr); ok {
|
||||
if ident, ok := selExpr.X.(*ast.Ident); ok && ident.Name == "g" && selExpr.Sel.Name == "ApplyBasic" {
|
||||
newArgs := make([]ast.Expr, 0, len(callExpr.Args))
|
||||
for _, arg := range callExpr.Args {
|
||||
if call, ok := arg.(*ast.CallExpr); ok {
|
||||
if sel, ok := call.Fun.(*ast.SelectorExpr); ok {
|
||||
if sel.Sel.Name == "GenerateModel" && len(call.Args) > 0 {
|
||||
if lit, ok := call.Args[0].(*ast.BasicLit); ok {
|
||||
existingTable := lit.Value[1 : len(lit.Value)-1]
|
||||
if existingTable == tableName {
|
||||
continue // 跳过要移除的模型
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
newArgs = append(newArgs, arg)
|
||||
}
|
||||
callExpr.Args = newArgs
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
// AddGormGenModelToSlice 向 models 切片中添加新模型
|
||||
// 用于处理 cmd/gen/main.go 中的 models = append(models, g.GenerateModel(...)) 模式
|
||||
func AddGormGenModelToSlice(file *ast.File, tableName string) bool {
|
||||
var found bool
|
||||
ast.Inspect(file, func(node ast.Node) bool {
|
||||
// 查找 for 循环中的 models = append(models, ...) 语句
|
||||
if forStmt, ok := node.(*ast.ForStmt); ok {
|
||||
ast.Inspect(forStmt.Body, func(n ast.Node) bool {
|
||||
if assignStmt, ok := n.(*ast.AssignStmt); ok {
|
||||
for _, rhs := range assignStmt.Rhs {
|
||||
if callExpr, ok := rhs.(*ast.CallExpr); ok {
|
||||
if ident, ok := callExpr.Fun.(*ast.Ident); ok && ident.Name == "append" {
|
||||
// 找到 append 调用,检查是否是 models 切片
|
||||
if len(callExpr.Args) >= 1 {
|
||||
if argIdent, ok := callExpr.Args[0].(*ast.Ident); ok && argIdent.Name == "models" {
|
||||
found = true
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
return true
|
||||
})
|
||||
return found
|
||||
}
|
||||
|
||||
// CheckGormGenModelExists 检查 GORM Gen 模型是否已存在
|
||||
func CheckGormGenModelExists(file *ast.File, tableName string) bool {
|
||||
var exists bool
|
||||
ast.Inspect(file, func(node ast.Node) bool {
|
||||
if callExpr, ok := node.(*ast.CallExpr); ok {
|
||||
if selExpr, ok := callExpr.Fun.(*ast.SelectorExpr); ok {
|
||||
if selExpr.Sel.Name == "GenerateModel" && len(callExpr.Args) > 0 {
|
||||
if lit, ok := callExpr.Args[0].(*ast.BasicLit); ok {
|
||||
existingTable := lit.Value[1 : len(lit.Value)-1]
|
||||
if existingTable == tableName {
|
||||
exists = true
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
return exists
|
||||
}
|
||||
@@ -0,0 +1,518 @@
|
||||
package ast
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TestAddImport 测试添加导入
|
||||
func TestAddImport(t *testing.T) {
|
||||
testCode := `package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {}
|
||||
`
|
||||
fset := token.NewFileSet()
|
||||
file, err := parser.ParseFile(fset, "test.go", testCode, parser.ParseComments)
|
||||
if err != nil {
|
||||
t.Fatalf("解析测试代码失败: %v", err)
|
||||
}
|
||||
|
||||
// 添加新导入
|
||||
AddImport(file, "kra/internal/biz/article")
|
||||
|
||||
// 验证导入是否添加
|
||||
var found bool
|
||||
ast.Inspect(file, func(node ast.Node) bool {
|
||||
if genDecl, ok := node.(*ast.GenDecl); ok && genDecl.Tok == token.IMPORT {
|
||||
for _, spec := range genDecl.Specs {
|
||||
if impSpec, ok := spec.(*ast.ImportSpec); ok {
|
||||
if impSpec.Path.Value == "\"kra/internal/biz/article\"" {
|
||||
found = true
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
if !found {
|
||||
t.Error("应该添加 kra/internal/biz/article 导入")
|
||||
}
|
||||
}
|
||||
|
||||
// TestAddImport_Duplicate 测试重复添加导入
|
||||
func TestAddImport_Duplicate(t *testing.T) {
|
||||
testCode := `package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"kra/internal/biz/article"
|
||||
)
|
||||
|
||||
func main() {}
|
||||
`
|
||||
fset := token.NewFileSet()
|
||||
file, err := parser.ParseFile(fset, "test.go", testCode, parser.ParseComments)
|
||||
if err != nil {
|
||||
t.Fatalf("解析测试代码失败: %v", err)
|
||||
}
|
||||
|
||||
// 统计初始导入数量
|
||||
initialCount := 0
|
||||
ast.Inspect(file, func(node ast.Node) bool {
|
||||
if genDecl, ok := node.(*ast.GenDecl); ok && genDecl.Tok == token.IMPORT {
|
||||
initialCount = len(genDecl.Specs)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
// 尝试添加已存在的导入
|
||||
AddImport(file, "kra/internal/biz/article")
|
||||
|
||||
// 验证导入数量没有增加
|
||||
finalCount := 0
|
||||
ast.Inspect(file, func(node ast.Node) bool {
|
||||
if genDecl, ok := node.(*ast.GenDecl); ok && genDecl.Tok == token.IMPORT {
|
||||
finalCount = len(genDecl.Specs)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
if finalCount != initialCount {
|
||||
t.Errorf("重复添加导入后数量应保持 %d, 实际为 %d", initialCount, finalCount)
|
||||
}
|
||||
}
|
||||
|
||||
// TestRemoveImport 测试移除导入
|
||||
func TestRemoveImport(t *testing.T) {
|
||||
testCode := `package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"kra/internal/biz/article"
|
||||
)
|
||||
|
||||
func main() {}
|
||||
`
|
||||
fset := token.NewFileSet()
|
||||
file, err := parser.ParseFile(fset, "test.go", testCode, parser.ParseComments)
|
||||
if err != nil {
|
||||
t.Fatalf("解析测试代码失败: %v", err)
|
||||
}
|
||||
|
||||
// 移除导入
|
||||
RemoveImport(file, "kra/internal/biz/article")
|
||||
|
||||
// 验证导入是否移除
|
||||
var found bool
|
||||
ast.Inspect(file, func(node ast.Node) bool {
|
||||
if genDecl, ok := node.(*ast.GenDecl); ok && genDecl.Tok == token.IMPORT {
|
||||
for _, spec := range genDecl.Specs {
|
||||
if impSpec, ok := spec.(*ast.ImportSpec); ok {
|
||||
if impSpec.Path.Value == "\"kra/internal/biz/article\"" {
|
||||
found = true
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
if found {
|
||||
t.Error("应该移除 kra/internal/biz/article 导入")
|
||||
}
|
||||
}
|
||||
|
||||
// TestAddStructField 测试添加结构体字段
|
||||
func TestAddStructField(t *testing.T) {
|
||||
testCode := `package main
|
||||
|
||||
type MyStruct struct {
|
||||
Name string
|
||||
}
|
||||
`
|
||||
fset := token.NewFileSet()
|
||||
file, err := parser.ParseFile(fset, "test.go", testCode, parser.ParseComments)
|
||||
if err != nil {
|
||||
t.Fatalf("解析测试代码失败: %v", err)
|
||||
}
|
||||
|
||||
// 添加新字段
|
||||
AddStructField(file, "MyStruct", "Age", "int")
|
||||
|
||||
// 验证字段是否添加
|
||||
var found bool
|
||||
ast.Inspect(file, func(node ast.Node) bool {
|
||||
if typeSpec, ok := node.(*ast.TypeSpec); ok && typeSpec.Name.Name == "MyStruct" {
|
||||
if structType, ok := typeSpec.Type.(*ast.StructType); ok {
|
||||
for _, field := range structType.Fields.List {
|
||||
for _, name := range field.Names {
|
||||
if name.Name == "Age" {
|
||||
found = true
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
if !found {
|
||||
t.Error("应该添加 Age 字段")
|
||||
}
|
||||
}
|
||||
|
||||
// TestAddStructField_Duplicate 测试重复添加结构体字段
|
||||
func TestAddStructField_Duplicate(t *testing.T) {
|
||||
testCode := `package main
|
||||
|
||||
type MyStruct struct {
|
||||
Name string
|
||||
Age int
|
||||
}
|
||||
`
|
||||
fset := token.NewFileSet()
|
||||
file, err := parser.ParseFile(fset, "test.go", testCode, parser.ParseComments)
|
||||
if err != nil {
|
||||
t.Fatalf("解析测试代码失败: %v", err)
|
||||
}
|
||||
|
||||
// 统计初始字段数量
|
||||
initialCount := 0
|
||||
ast.Inspect(file, func(node ast.Node) bool {
|
||||
if typeSpec, ok := node.(*ast.TypeSpec); ok && typeSpec.Name.Name == "MyStruct" {
|
||||
if structType, ok := typeSpec.Type.(*ast.StructType); ok {
|
||||
initialCount = len(structType.Fields.List)
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
// 尝试添加已存在的字段
|
||||
AddStructField(file, "MyStruct", "Age", "int")
|
||||
|
||||
// 验证字段数量没有增加
|
||||
finalCount := 0
|
||||
ast.Inspect(file, func(node ast.Node) bool {
|
||||
if typeSpec, ok := node.(*ast.TypeSpec); ok && typeSpec.Name.Name == "MyStruct" {
|
||||
if structType, ok := typeSpec.Type.(*ast.StructType); ok {
|
||||
finalCount = len(structType.Fields.List)
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
if finalCount != initialCount {
|
||||
t.Errorf("重复添加字段后数量应保持 %d, 实际为 %d", initialCount, finalCount)
|
||||
}
|
||||
}
|
||||
|
||||
// TestRemoveStructField 测试移除结构体字段
|
||||
func TestRemoveStructField(t *testing.T) {
|
||||
testCode := `package main
|
||||
|
||||
type MyStruct struct {
|
||||
Name string
|
||||
Age int
|
||||
}
|
||||
`
|
||||
fset := token.NewFileSet()
|
||||
file, err := parser.ParseFile(fset, "test.go", testCode, parser.ParseComments)
|
||||
if err != nil {
|
||||
t.Fatalf("解析测试代码失败: %v", err)
|
||||
}
|
||||
|
||||
// 移除字段
|
||||
RemoveStructField(file, "MyStruct", "Age")
|
||||
|
||||
// 验证字段是否移除
|
||||
var found bool
|
||||
ast.Inspect(file, func(node ast.Node) bool {
|
||||
if typeSpec, ok := node.(*ast.TypeSpec); ok && typeSpec.Name.Name == "MyStruct" {
|
||||
if structType, ok := typeSpec.Type.(*ast.StructType); ok {
|
||||
for _, field := range structType.Fields.List {
|
||||
for _, name := range field.Names {
|
||||
if name.Name == "Age" {
|
||||
found = true
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
if found {
|
||||
t.Error("应该移除 Age 字段")
|
||||
}
|
||||
}
|
||||
|
||||
// TestFindFunction 测试查找函数
|
||||
func TestFindFunction(t *testing.T) {
|
||||
testCode := `package main
|
||||
|
||||
func Hello() {}
|
||||
|
||||
func World() {}
|
||||
`
|
||||
fset := token.NewFileSet()
|
||||
file, err := parser.ParseFile(fset, "test.go", testCode, parser.ParseComments)
|
||||
if err != nil {
|
||||
t.Fatalf("解析测试代码失败: %v", err)
|
||||
}
|
||||
|
||||
// 查找存在的函数
|
||||
funcDecl := FindFunction(file, "Hello")
|
||||
if funcDecl == nil {
|
||||
t.Error("应该找到 Hello 函数")
|
||||
}
|
||||
|
||||
// 查找不存在的函数
|
||||
funcDecl = FindFunction(file, "NotExist")
|
||||
if funcDecl != nil {
|
||||
t.Error("不应该找到 NotExist 函数")
|
||||
}
|
||||
}
|
||||
|
||||
// TestCheckImport 测试检查导入是否存在
|
||||
func TestCheckImport(t *testing.T) {
|
||||
testCode := `package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"kra/internal/biz/article"
|
||||
)
|
||||
|
||||
func main() {}
|
||||
`
|
||||
fset := token.NewFileSet()
|
||||
file, err := parser.ParseFile(fset, "test.go", testCode, parser.ParseComments)
|
||||
if err != nil {
|
||||
t.Fatalf("解析测试代码失败: %v", err)
|
||||
}
|
||||
|
||||
// 检查存在的导入
|
||||
if !CheckImport(file, "fmt") {
|
||||
t.Error("应该找到 fmt 导入")
|
||||
}
|
||||
|
||||
// 检查存在的导入(完整路径)
|
||||
if !CheckImport(file, "kra/internal/biz/article") {
|
||||
t.Error("应该找到 kra/internal/biz/article 导入")
|
||||
}
|
||||
|
||||
// 检查不存在的导入
|
||||
if CheckImport(file, "not/exist") {
|
||||
t.Error("不应该找到 not/exist 导入")
|
||||
}
|
||||
}
|
||||
|
||||
// TestAddFuncCall 测试在函数体中添加函数调用
|
||||
func TestAddFuncCall(t *testing.T) {
|
||||
testCode := `package main
|
||||
|
||||
func initRouter() {
|
||||
fmt.Println("init")
|
||||
}
|
||||
`
|
||||
fset := token.NewFileSet()
|
||||
file, err := parser.ParseFile(fset, "test.go", testCode, parser.ParseComments)
|
||||
if err != nil {
|
||||
t.Fatalf("解析测试代码失败: %v", err)
|
||||
}
|
||||
|
||||
// 添加函数调用
|
||||
AddFuncCall(file, "initRouter", "router.Article.InitArticleRouter(group)")
|
||||
|
||||
// 验证函数调用是否添加
|
||||
funcDecl := FindFunction(file, "initRouter")
|
||||
if funcDecl == nil || funcDecl.Body == nil {
|
||||
t.Fatal("找不到 initRouter 函数")
|
||||
}
|
||||
|
||||
// 检查函数体中是否包含新的调用
|
||||
if len(funcDecl.Body.List) < 2 {
|
||||
t.Error("应该添加新的函数调用")
|
||||
}
|
||||
}
|
||||
|
||||
// TestRemoveFuncCall 测试从函数体中移除函数调用
|
||||
func TestRemoveFuncCall(t *testing.T) {
|
||||
testCode := `package main
|
||||
|
||||
func initRouter() {
|
||||
fmt.Println("init")
|
||||
router.Article.InitArticleRouter(group)
|
||||
}
|
||||
`
|
||||
fset := token.NewFileSet()
|
||||
file, err := parser.ParseFile(fset, "test.go", testCode, parser.ParseComments)
|
||||
if err != nil {
|
||||
t.Fatalf("解析测试代码失败: %v", err)
|
||||
}
|
||||
|
||||
// 移除函数调用
|
||||
RemoveFuncCall(file, "initRouter", "InitArticleRouter")
|
||||
|
||||
// 验证函数调用是否移除
|
||||
funcDecl := FindFunction(file, "initRouter")
|
||||
if funcDecl == nil || funcDecl.Body == nil {
|
||||
t.Fatal("找不到 initRouter 函数")
|
||||
}
|
||||
|
||||
// 检查函数体中是否还包含该调用
|
||||
for _, stmt := range funcDecl.Body.List {
|
||||
if exprStmt, ok := stmt.(*ast.ExprStmt); ok {
|
||||
if callExpr, ok := exprStmt.X.(*ast.CallExpr); ok {
|
||||
if selExpr, ok := callExpr.Fun.(*ast.SelectorExpr); ok {
|
||||
if selExpr.Sel.Name == "InitArticleRouter" {
|
||||
t.Error("应该移除 InitArticleRouter 调用")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestCreateStmt 测试创建语句
|
||||
func TestCreateStmt(t *testing.T) {
|
||||
stmt := CreateStmt("fmt.Println(\"hello\")")
|
||||
if stmt == nil {
|
||||
t.Error("应该创建语句")
|
||||
}
|
||||
if stmt.X == nil {
|
||||
t.Error("语句表达式不应为空")
|
||||
}
|
||||
}
|
||||
|
||||
// TestVariableExistsInBlock 测试检查变量是否存在于块中
|
||||
func TestVariableExistsInBlock(t *testing.T) {
|
||||
testCode := `package main
|
||||
|
||||
func test() {
|
||||
x := 1
|
||||
y := 2
|
||||
}
|
||||
`
|
||||
fset := token.NewFileSet()
|
||||
file, err := parser.ParseFile(fset, "test.go", testCode, parser.ParseComments)
|
||||
if err != nil {
|
||||
t.Fatalf("解析测试代码失败: %v", err)
|
||||
}
|
||||
|
||||
funcDecl := FindFunction(file, "test")
|
||||
if funcDecl == nil || funcDecl.Body == nil {
|
||||
t.Fatal("找不到 test 函数")
|
||||
}
|
||||
|
||||
// 检查存在的变量
|
||||
if !VariableExistsInBlock(funcDecl.Body, "x") {
|
||||
t.Error("应该找到变量 x")
|
||||
}
|
||||
|
||||
// 检查不存在的变量
|
||||
if VariableExistsInBlock(funcDecl.Body, "z") {
|
||||
t.Error("不应该找到变量 z")
|
||||
}
|
||||
}
|
||||
|
||||
// TestBase_RelativePath 测试相对路径转换
|
||||
func TestBase_RelativePath(t *testing.T) {
|
||||
base := &Base{
|
||||
AutoCodeRoot: "/home/user/project",
|
||||
AutoCodeServer: "server",
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
input string
|
||||
expected string
|
||||
}{
|
||||
{"/home/user/project/server/internal/biz/article.go", "internal/biz/article.go"},
|
||||
{"/other/path/file.go", "/other/path/file.go"},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
result := base.RelativePath(tc.input)
|
||||
// 由于路径分隔符可能不同,只检查是否包含关键部分
|
||||
if !strings.Contains(result, "article.go") && tc.input != "/other/path/file.go" {
|
||||
t.Errorf("RelativePath(%s) 结果不正确: %s", tc.input, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestAddAutoMigrateModel 测试添加 AutoMigrate 模型
|
||||
func TestAddAutoMigrateModel(t *testing.T) {
|
||||
testCode := `package main
|
||||
|
||||
import "gorm.io/gorm"
|
||||
|
||||
func migrate(db *gorm.DB) {
|
||||
db.AutoMigrate(&User{})
|
||||
}
|
||||
`
|
||||
fset := token.NewFileSet()
|
||||
file, err := parser.ParseFile(fset, "test.go", testCode, parser.ParseComments)
|
||||
if err != nil {
|
||||
t.Fatalf("解析测试代码失败: %v", err)
|
||||
}
|
||||
|
||||
// 添加新模型
|
||||
AddAutoMigrateModel(file, "&article.Article{}")
|
||||
|
||||
// 验证模型是否添加
|
||||
var found bool
|
||||
ast.Inspect(file, func(node ast.Node) bool {
|
||||
if callExpr, ok := node.(*ast.CallExpr); ok {
|
||||
if selExpr, ok := callExpr.Fun.(*ast.SelectorExpr); ok {
|
||||
if selExpr.Sel.Name == "AutoMigrate" {
|
||||
// 检查参数数量是否增加
|
||||
if len(callExpr.Args) > 1 {
|
||||
found = true
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
if !found {
|
||||
t.Error("应该添加新的 AutoMigrate 模型")
|
||||
}
|
||||
}
|
||||
|
||||
// TestCheckGormGenModelExists 测试检查 GORM Gen 模型是否存在
|
||||
func TestCheckGormGenModelExists(t *testing.T) {
|
||||
testCode := `package main
|
||||
|
||||
func main() {
|
||||
g.ApplyBasic(
|
||||
g.GenerateModel("users"),
|
||||
g.GenerateModel("articles"),
|
||||
)
|
||||
}
|
||||
`
|
||||
fset := token.NewFileSet()
|
||||
file, err := parser.ParseFile(fset, "test.go", testCode, parser.ParseComments)
|
||||
if err != nil {
|
||||
t.Fatalf("解析测试代码失败: %v", err)
|
||||
}
|
||||
|
||||
// 检查存在的模型
|
||||
if !CheckGormGenModelExists(file, "users") {
|
||||
t.Error("应该找到 users 模型")
|
||||
}
|
||||
|
||||
// 检查不存在的模型
|
||||
if CheckGormGenModelExists(file, "orders") {
|
||||
t.Error("不应该找到 orders 模型")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,217 @@
|
||||
package ast
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"go/ast"
|
||||
"go/format"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"log"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/text/cases"
|
||||
"golang.org/x/text/language"
|
||||
)
|
||||
|
||||
// Visitor AST访问器
|
||||
type Visitor struct {
|
||||
ImportCode string
|
||||
StructName string
|
||||
PackageName string
|
||||
GroupName string
|
||||
}
|
||||
|
||||
func (vi *Visitor) Visit(node ast.Node) ast.Visitor {
|
||||
switch n := node.(type) {
|
||||
case *ast.GenDecl:
|
||||
if n.Tok == token.IMPORT && vi.ImportCode != "" {
|
||||
vi.addImport(n)
|
||||
return nil
|
||||
}
|
||||
if n.Tok == token.TYPE && vi.StructName != "" && vi.PackageName != "" && vi.GroupName != "" {
|
||||
vi.addStruct(n)
|
||||
return nil
|
||||
}
|
||||
case *ast.FuncDecl:
|
||||
if n.Name.Name == "Routers" {
|
||||
vi.addFuncBodyVar(n)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return vi
|
||||
}
|
||||
|
||||
func (vi *Visitor) addStruct(genDecl *ast.GenDecl) ast.Visitor {
|
||||
for i := range genDecl.Specs {
|
||||
switch n := genDecl.Specs[i].(type) {
|
||||
case *ast.TypeSpec:
|
||||
if strings.Index(n.Name.Name, "Group") > -1 {
|
||||
switch t := n.Type.(type) {
|
||||
case *ast.StructType:
|
||||
f := &ast.Field{
|
||||
Names: []*ast.Ident{
|
||||
{
|
||||
Name: vi.StructName,
|
||||
Obj: &ast.Object{
|
||||
Kind: ast.Var,
|
||||
Name: vi.StructName,
|
||||
},
|
||||
},
|
||||
},
|
||||
Type: &ast.SelectorExpr{
|
||||
X: &ast.Ident{
|
||||
Name: vi.PackageName,
|
||||
},
|
||||
Sel: &ast.Ident{
|
||||
Name: vi.GroupName,
|
||||
},
|
||||
},
|
||||
}
|
||||
t.Fields.List = append(t.Fields.List, f)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return vi
|
||||
}
|
||||
|
||||
func (vi *Visitor) addImport(genDecl *ast.GenDecl) ast.Visitor {
|
||||
hasImported := false
|
||||
for _, v := range genDecl.Specs {
|
||||
importSpec := v.(*ast.ImportSpec)
|
||||
if importSpec.Path.Value == strconv.Quote(vi.ImportCode) {
|
||||
hasImported = true
|
||||
}
|
||||
}
|
||||
if !hasImported {
|
||||
genDecl.Specs = append(genDecl.Specs, &ast.ImportSpec{
|
||||
Path: &ast.BasicLit{
|
||||
Kind: token.STRING,
|
||||
Value: strconv.Quote(vi.ImportCode),
|
||||
},
|
||||
})
|
||||
}
|
||||
return vi
|
||||
}
|
||||
|
||||
func (vi *Visitor) addFuncBodyVar(funDecl *ast.FuncDecl) ast.Visitor {
|
||||
hasVar := false
|
||||
for _, v := range funDecl.Body.List {
|
||||
switch varSpec := v.(type) {
|
||||
case *ast.AssignStmt:
|
||||
for i := range varSpec.Lhs {
|
||||
switch nn := varSpec.Lhs[i].(type) {
|
||||
case *ast.Ident:
|
||||
if nn.Name == vi.PackageName+"Router" {
|
||||
hasVar = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if !hasVar {
|
||||
assignStmt := &ast.AssignStmt{
|
||||
Lhs: []ast.Expr{
|
||||
&ast.Ident{
|
||||
Name: vi.PackageName + "Router",
|
||||
Obj: &ast.Object{
|
||||
Kind: ast.Var,
|
||||
Name: vi.PackageName + "Router",
|
||||
},
|
||||
},
|
||||
},
|
||||
Tok: token.DEFINE,
|
||||
Rhs: []ast.Expr{
|
||||
&ast.SelectorExpr{
|
||||
X: &ast.SelectorExpr{
|
||||
X: &ast.Ident{
|
||||
Name: "router",
|
||||
},
|
||||
Sel: &ast.Ident{
|
||||
Name: "RouterGroupApp",
|
||||
},
|
||||
},
|
||||
Sel: &ast.Ident{
|
||||
Name: cases.Title(language.English).String(vi.PackageName),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
funDecl.Body.List = append(funDecl.Body.List, funDecl.Body.List[1])
|
||||
index := 1
|
||||
copy(funDecl.Body.List[index+1:], funDecl.Body.List[index:])
|
||||
funDecl.Body.List[index] = assignStmt
|
||||
}
|
||||
return vi
|
||||
}
|
||||
|
||||
// ImportReference 导入引用
|
||||
func ImportReference(filepath, importCode, structName, packageName, groupName string) error {
|
||||
fSet := token.NewFileSet()
|
||||
fParser, err := parser.ParseFile(fSet, filepath, nil, parser.ParseComments)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
importCode = strings.TrimSpace(importCode)
|
||||
v := &Visitor{
|
||||
ImportCode: importCode,
|
||||
StructName: structName,
|
||||
PackageName: packageName,
|
||||
GroupName: groupName,
|
||||
}
|
||||
if importCode == "" {
|
||||
ast.Print(fSet, fParser)
|
||||
}
|
||||
|
||||
ast.Walk(v, fParser)
|
||||
|
||||
var output []byte
|
||||
buffer := bytes.NewBuffer(output)
|
||||
err = format.Node(buffer, fSet, fParser)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
return os.WriteFile(filepath, buffer.Bytes(), 0o600)
|
||||
}
|
||||
|
||||
// ImportForAutoEnter 自动导入
|
||||
func ImportForAutoEnter(path string, funcName string, code string) {
|
||||
src, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
fileSet := token.NewFileSet()
|
||||
astFile, err := parser.ParseFile(fileSet, "", src, 0)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
ast.Inspect(astFile, func(node ast.Node) bool {
|
||||
if typeSpec, ok := node.(*ast.TypeSpec); ok {
|
||||
if typeSpec.Name.Name == funcName {
|
||||
if st, ok := typeSpec.Type.(*ast.StructType); ok {
|
||||
for i := range st.Fields.List {
|
||||
if t, ok := st.Fields.List[i].Type.(*ast.Ident); ok {
|
||||
if t.Name == code {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
sn := &ast.Field{
|
||||
Type: &ast.Ident{Name: code},
|
||||
}
|
||||
st.Fields.List = append(st.Fields.List, sn)
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
var out []byte
|
||||
bf := bytes.NewBuffer(out)
|
||||
err = format.Node(bf, fileSet, astFile)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_ = os.WriteFile(path, bf.Bytes(), 0666)
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
package ast
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/parser"
|
||||
"go/printer"
|
||||
"go/token"
|
||||
"os"
|
||||
)
|
||||
|
||||
// AddRegisterTablesAst 自动为 gorm.go 注册一个自动迁移
|
||||
func AddRegisterTablesAst(path, funcName, pk, varName, dbName, model, modelImportPath string) {
|
||||
src, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
fileSet := token.NewFileSet()
|
||||
astFile, err := parser.ParseFile(fileSet, "", src, 0)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
AddImport(astFile, modelImportPath)
|
||||
FuncNode := FindFunction(astFile, funcName)
|
||||
if FuncNode != nil {
|
||||
addDBVar(FuncNode.Body, varName, dbName)
|
||||
addAutoMigrate(FuncNode.Body, varName, pk, model)
|
||||
}
|
||||
var out []byte
|
||||
bf := bytes.NewBuffer(out)
|
||||
printer.Fprint(bf, fileSet, astFile)
|
||||
os.WriteFile(path, bf.Bytes(), 0666)
|
||||
}
|
||||
|
||||
// addDBVar 增加一个 db库变量
|
||||
func addDBVar(astBody *ast.BlockStmt, varName, dbName string) {
|
||||
if dbName == "" {
|
||||
return
|
||||
}
|
||||
dbStr := fmt.Sprintf("\"%s\"", dbName)
|
||||
|
||||
for i := range astBody.List {
|
||||
if assignStmt, ok := astBody.List[i].(*ast.AssignStmt); ok {
|
||||
if ident, ok := assignStmt.Lhs[0].(*ast.Ident); ok {
|
||||
if ident.Name == varName {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
assignNode := &ast.AssignStmt{
|
||||
Lhs: []ast.Expr{
|
||||
&ast.Ident{Name: varName},
|
||||
},
|
||||
Tok: token.DEFINE,
|
||||
Rhs: []ast.Expr{
|
||||
&ast.CallExpr{
|
||||
Fun: &ast.SelectorExpr{
|
||||
X: &ast.Ident{Name: "global"},
|
||||
Sel: &ast.Ident{Name: "GetGlobalDBByDBName"},
|
||||
},
|
||||
Args: []ast.Expr{
|
||||
&ast.BasicLit{Kind: token.STRING, Value: dbStr},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
astBody.List = append([]ast.Stmt{assignNode}, astBody.List...)
|
||||
}
|
||||
|
||||
// addAutoMigrate 为db库变量增加 AutoMigrate 方法
|
||||
func addAutoMigrate(astBody *ast.BlockStmt, dbname string, pk string, model string) {
|
||||
if dbname == "" {
|
||||
dbname = "db"
|
||||
}
|
||||
flag := true
|
||||
ast.Inspect(astBody, func(node ast.Node) bool {
|
||||
switch n := node.(type) {
|
||||
case *ast.CallExpr:
|
||||
if s, ok := n.Fun.(*ast.SelectorExpr); ok {
|
||||
if x, ok := s.X.(*ast.Ident); ok {
|
||||
if s.Sel.Name == "AutoMigrate" && x.Name == dbname {
|
||||
flag = false
|
||||
if !NeedAppendModel(n, pk, model) {
|
||||
return false
|
||||
}
|
||||
n.Args = append(n.Args, &ast.CompositeLit{
|
||||
Type: &ast.SelectorExpr{
|
||||
X: &ast.Ident{Name: pk},
|
||||
Sel: &ast.Ident{Name: model},
|
||||
},
|
||||
})
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
if flag {
|
||||
exprStmt := &ast.ExprStmt{
|
||||
X: &ast.CallExpr{
|
||||
Fun: &ast.SelectorExpr{
|
||||
X: &ast.Ident{Name: dbname},
|
||||
Sel: &ast.Ident{Name: "AutoMigrate"},
|
||||
},
|
||||
Args: []ast.Expr{
|
||||
&ast.CompositeLit{
|
||||
Type: &ast.SelectorExpr{
|
||||
X: &ast.Ident{Name: pk},
|
||||
Sel: &ast.Ident{Name: model},
|
||||
},
|
||||
},
|
||||
},
|
||||
}}
|
||||
astBody.List = append(astBody.List, exprStmt)
|
||||
}
|
||||
}
|
||||
|
||||
// NeedAppendModel 为automigrate增加实参
|
||||
func NeedAppendModel(callNode ast.Node, pk string, model string) bool {
|
||||
flag := true
|
||||
ast.Inspect(callNode, func(node ast.Node) bool {
|
||||
switch n := node.(type) {
|
||||
case *ast.SelectorExpr:
|
||||
if x, ok := n.X.(*ast.Ident); ok {
|
||||
if n.Sel.Name == model && x.Name == pk {
|
||||
flag = false
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
return flag
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
package ast
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Import 导入操作
|
||||
type Import struct {
|
||||
Base
|
||||
ImportPath string
|
||||
}
|
||||
|
||||
// NewImport 创建导入操作
|
||||
func NewImport(importPath string) *Import {
|
||||
return &Import{ImportPath: importPath}
|
||||
}
|
||||
|
||||
func (a *Import) Parse(filename string, writer io.Writer) (file *ast.File, err error) {
|
||||
return a.Base.Parse(filename, writer)
|
||||
}
|
||||
|
||||
func (a *Import) Rollback(file *ast.File) error {
|
||||
if a.ImportPath == "" {
|
||||
return nil
|
||||
}
|
||||
for i := 0; i < len(file.Decls); i++ {
|
||||
v1, o1 := file.Decls[i].(*ast.GenDecl)
|
||||
if o1 {
|
||||
if v1.Tok != token.IMPORT {
|
||||
break
|
||||
}
|
||||
for j := 0; j < len(v1.Specs); j++ {
|
||||
v2, o2 := v1.Specs[j].(*ast.ImportSpec)
|
||||
if o2 && strings.HasSuffix(a.ImportPath, v2.Path.Value) {
|
||||
v1.Specs = append(v1.Specs[:j], v1.Specs[j+1:]...)
|
||||
if len(v1.Specs) == 0 {
|
||||
file.Decls = append(file.Decls[:i], file.Decls[i+1:]...)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *Import) Injection(file *ast.File) error {
|
||||
if a.ImportPath == "" {
|
||||
return nil
|
||||
}
|
||||
var has bool
|
||||
for i := 0; i < len(file.Decls); i++ {
|
||||
v1, o1 := file.Decls[i].(*ast.GenDecl)
|
||||
if o1 {
|
||||
if v1.Tok != token.IMPORT {
|
||||
break
|
||||
}
|
||||
for j := 0; j < len(v1.Specs); j++ {
|
||||
v2, o2 := v1.Specs[j].(*ast.ImportSpec)
|
||||
if o2 && strings.HasSuffix(a.ImportPath, v2.Path.Value) {
|
||||
has = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !has {
|
||||
spec := &ast.ImportSpec{
|
||||
Path: &ast.BasicLit{Kind: token.STRING, Value: a.ImportPath},
|
||||
}
|
||||
v1.Specs = append(v1.Specs, spec)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
if !has {
|
||||
decls := file.Decls
|
||||
file.Decls = make([]ast.Decl, 0, len(file.Decls)+1)
|
||||
decl := &ast.GenDecl{
|
||||
Tok: token.IMPORT,
|
||||
Specs: []ast.Spec{
|
||||
&ast.ImportSpec{
|
||||
Path: &ast.BasicLit{Kind: token.STRING, Value: a.ImportPath},
|
||||
},
|
||||
},
|
||||
}
|
||||
file.Decls = append(file.Decls, decl)
|
||||
file.Decls = append(file.Decls, decls...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *Import) Format(filename string, writer io.Writer, file *ast.File) error {
|
||||
return a.Base.Format(filename, writer, file)
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package ast
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/format"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"io"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// Ast AST操作接口
|
||||
type Ast interface {
|
||||
Parse(filename string, writer io.Writer) (file *ast.File, err error)
|
||||
Rollback(file *ast.File) error
|
||||
Injection(file *ast.File) error
|
||||
Format(filename string, writer io.Writer, file *ast.File) error
|
||||
}
|
||||
|
||||
// Base 基础AST操作
|
||||
type Base struct {
|
||||
AutoCodeRoot string
|
||||
AutoCodeServer string
|
||||
}
|
||||
|
||||
func (a *Base) Parse(filename string, writer io.Writer) (file *ast.File, err error) {
|
||||
fileSet := token.NewFileSet()
|
||||
if writer != nil {
|
||||
file, err = parser.ParseFile(fileSet, filename, nil, parser.ParseComments)
|
||||
} else {
|
||||
file, err = parser.ParseFile(fileSet, filename, writer, parser.ParseComments)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "[filepath:%s]打开/解析文件失败!", filename)
|
||||
}
|
||||
return file, nil
|
||||
}
|
||||
|
||||
func (a *Base) Rollback(file *ast.File) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *Base) Injection(file *ast.File) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *Base) Format(filename string, writer io.Writer, file *ast.File) error {
|
||||
fileSet := token.NewFileSet()
|
||||
if writer == nil {
|
||||
open, err := os.OpenFile(filename, os.O_WRONLY|os.O_TRUNC, 0666)
|
||||
defer open.Close()
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "[filepath:%s]打开文件失败!", filename)
|
||||
}
|
||||
writer = open
|
||||
}
|
||||
err := format.Node(writer, fileSet, file)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "[filepath:%s]注入失败!", filename)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RelativePath 绝对路径转相对路径
|
||||
func (a *Base) RelativePath(filePath string) string {
|
||||
server := filepath.Join(a.AutoCodeRoot, a.AutoCodeServer)
|
||||
hasServer := strings.Index(filePath, server)
|
||||
if hasServer != -1 {
|
||||
filePath = strings.TrimPrefix(filePath, server)
|
||||
keys := strings.Split(filePath, string(filepath.Separator))
|
||||
filePath = path.Join(keys...)
|
||||
}
|
||||
return filePath
|
||||
}
|
||||
|
||||
// AbsolutePath 相对路径转绝对路径
|
||||
func (a *Base) AbsolutePath(filePath string) string {
|
||||
server := filepath.Join(a.AutoCodeRoot, a.AutoCodeServer)
|
||||
keys := strings.Split(filePath, "/")
|
||||
filePath = filepath.Join(keys...)
|
||||
filePath = filepath.Join(server, filePath)
|
||||
return filePath
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
package ast
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/parser"
|
||||
"go/printer"
|
||||
"go/token"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// AppendNodeToList 向语句列表中插入节点
|
||||
func AppendNodeToList(stmts []ast.Stmt, stmt ast.Stmt, index int) []ast.Stmt {
|
||||
return append(stmts[:index], append([]ast.Stmt{stmt}, stmts[index:]...)...)
|
||||
}
|
||||
|
||||
// AddRouterCode 添加路由代码
|
||||
func AddRouterCode(path, funcName, pk, model string) {
|
||||
src, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
fileSet := token.NewFileSet()
|
||||
astFile, err := parser.ParseFile(fileSet, "", src, parser.ParseComments)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
FuncNode := FindFunction(astFile, funcName)
|
||||
if FuncNode == nil {
|
||||
return
|
||||
}
|
||||
|
||||
pkName := strings.ToUpper(pk[:1]) + pk[1:]
|
||||
routerName := fmt.Sprintf("%sRouter", pk)
|
||||
modelName := fmt.Sprintf("Init%sRouter", model)
|
||||
var bloctPre *ast.BlockStmt
|
||||
for i := len(FuncNode.Body.List) - 1; i >= 0; i-- {
|
||||
if block, ok := FuncNode.Body.List[i].(*ast.BlockStmt); ok {
|
||||
bloctPre = block
|
||||
}
|
||||
}
|
||||
|
||||
if ok, b := needAppendRouter(FuncNode, pk); ok {
|
||||
routerNode := &ast.BlockStmt{
|
||||
List: []ast.Stmt{
|
||||
&ast.AssignStmt{
|
||||
Lhs: []ast.Expr{
|
||||
&ast.Ident{Name: routerName},
|
||||
},
|
||||
Tok: token.DEFINE,
|
||||
Rhs: []ast.Expr{
|
||||
&ast.SelectorExpr{
|
||||
X: &ast.SelectorExpr{
|
||||
X: &ast.Ident{Name: "router"},
|
||||
Sel: &ast.Ident{Name: "RouterGroupApp"},
|
||||
},
|
||||
Sel: &ast.Ident{Name: pkName},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
FuncNode.Body.List = AppendNodeToList(FuncNode.Body.List, routerNode, len(FuncNode.Body.List)-1)
|
||||
bloctPre = routerNode
|
||||
} else {
|
||||
bloctPre = b
|
||||
}
|
||||
|
||||
if needAppendInit(FuncNode, routerName, modelName) {
|
||||
bloctPre.List = append(bloctPre.List,
|
||||
&ast.ExprStmt{
|
||||
X: &ast.CallExpr{
|
||||
Fun: &ast.SelectorExpr{
|
||||
X: &ast.Ident{Name: routerName},
|
||||
Sel: &ast.Ident{Name: modelName},
|
||||
},
|
||||
Args: []ast.Expr{
|
||||
&ast.Ident{Name: "privateGroup"},
|
||||
&ast.Ident{Name: "publicGroup"},
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
var out []byte
|
||||
bf := bytes.NewBuffer(out)
|
||||
printer.Fprint(bf, fileSet, astFile)
|
||||
os.WriteFile(path, bf.Bytes(), 0666)
|
||||
}
|
||||
|
||||
func needAppendRouter(funcNode ast.Node, pk string) (bool, *ast.BlockStmt) {
|
||||
flag := true
|
||||
var block *ast.BlockStmt
|
||||
ast.Inspect(funcNode, func(node ast.Node) bool {
|
||||
switch n := node.(type) {
|
||||
case *ast.BlockStmt:
|
||||
for i := range n.List {
|
||||
if assignNode, ok := n.List[i].(*ast.AssignStmt); ok {
|
||||
if identNode, ok := assignNode.Lhs[0].(*ast.Ident); ok {
|
||||
if identNode.Name == fmt.Sprintf("%sRouter", pk) {
|
||||
flag = false
|
||||
block = n
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
return flag, block
|
||||
}
|
||||
|
||||
func needAppendInit(funcNode ast.Node, routerName string, modelName string) bool {
|
||||
flag := true
|
||||
ast.Inspect(funcNode, func(node ast.Node) bool {
|
||||
switch n := funcNode.(type) {
|
||||
case *ast.CallExpr:
|
||||
if selectNode, ok := n.Fun.(*ast.SelectorExpr); ok {
|
||||
x, xok := selectNode.X.(*ast.Ident)
|
||||
if xok && x.Name == routerName && selectNode.Sel.Name == modelName {
|
||||
flag = false
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
return flag
|
||||
}
|
||||
@@ -0,0 +1,255 @@
|
||||
package ast
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/token"
|
||||
)
|
||||
|
||||
// Type AST类型
|
||||
type Type string
|
||||
|
||||
func (r Type) String() string {
|
||||
return string(r)
|
||||
}
|
||||
|
||||
func (r Type) Group() string {
|
||||
switch r {
|
||||
case TypePackageApiEnter:
|
||||
return "ApiGroup"
|
||||
case TypePackageRouterEnter:
|
||||
return "RouterGroup"
|
||||
case TypePackageServiceEnter:
|
||||
return "ServiceGroup"
|
||||
case TypePackageApiModuleEnter:
|
||||
return "ApiGroup"
|
||||
case TypePackageRouterModuleEnter:
|
||||
return "RouterGroup"
|
||||
case TypePackageServiceModuleEnter:
|
||||
return "ServiceGroup"
|
||||
case TypePluginApiEnter:
|
||||
return "api"
|
||||
case TypePluginRouterEnter:
|
||||
return "router"
|
||||
case TypePluginServiceEnter:
|
||||
return "service"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
TypePackageApiEnter = "PackageApiEnter"
|
||||
TypePackageRouterEnter = "PackageRouterEnter"
|
||||
TypePackageServiceEnter = "PackageServiceEnter"
|
||||
TypePackageApiModuleEnter = "PackageApiModuleEnter"
|
||||
TypePackageRouterModuleEnter = "PackageRouterModuleEnter"
|
||||
TypePackageServiceModuleEnter = "PackageServiceModuleEnter"
|
||||
TypePackageInitializeGorm = "PackageInitializeGorm"
|
||||
TypePackageInitializeRouter = "PackageInitializeRouter"
|
||||
TypePluginGen = "PluginGen"
|
||||
TypePluginApiEnter = "PluginApiEnter"
|
||||
TypePluginInitializeV1 = "PluginInitializeV1"
|
||||
TypePluginInitializeV2 = "PluginInitializeV2"
|
||||
TypePluginRouterEnter = "PluginRouterEnter"
|
||||
TypePluginServiceEnter = "PluginServiceEnter"
|
||||
TypePluginInitializeApi = "PluginInitializeApi"
|
||||
TypePluginInitializeGorm = "PluginInitializeGorm"
|
||||
TypePluginInitializeMenu = "PluginInitializeMenu"
|
||||
TypePluginInitializeRouter = "PluginInitializeRouter"
|
||||
)
|
||||
|
||||
// SysBaseMenu 菜单结构(用于AST生成)
|
||||
type SysBaseMenu struct {
|
||||
ParentId uint
|
||||
Path string
|
||||
Name string
|
||||
Hidden bool
|
||||
Component string
|
||||
Sort int
|
||||
Title string
|
||||
Icon string
|
||||
Parameters []SysBaseMenuParameter
|
||||
MenuBtn []SysBaseMenuBtn
|
||||
}
|
||||
|
||||
// SysBaseMenuParameter 菜单参数
|
||||
type SysBaseMenuParameter struct {
|
||||
Type string
|
||||
Key string
|
||||
Value string
|
||||
}
|
||||
|
||||
// SysBaseMenuBtn 菜单按钮
|
||||
type SysBaseMenuBtn struct {
|
||||
Name string
|
||||
Desc string
|
||||
}
|
||||
|
||||
// SysApi API结构
|
||||
type SysApi struct {
|
||||
Path string
|
||||
Description string
|
||||
ApiGroup string
|
||||
Method string
|
||||
}
|
||||
|
||||
// CreateMenuStructAst 创建菜单结构AST
|
||||
func CreateMenuStructAst(menus []SysBaseMenu) *[]ast.Expr {
|
||||
var menuElts []ast.Expr
|
||||
for i := range menus {
|
||||
elts := []ast.Expr{
|
||||
&ast.KeyValueExpr{
|
||||
Key: &ast.Ident{Name: "ParentId"},
|
||||
Value: &ast.BasicLit{Kind: token.INT, Value: "0"},
|
||||
},
|
||||
&ast.KeyValueExpr{
|
||||
Key: &ast.Ident{Name: "Path"},
|
||||
Value: &ast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf("\"%s\"", menus[i].Path)},
|
||||
},
|
||||
&ast.KeyValueExpr{
|
||||
Key: &ast.Ident{Name: "Name"},
|
||||
Value: &ast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf("\"%s\"", menus[i].Name)},
|
||||
},
|
||||
&ast.KeyValueExpr{
|
||||
Key: &ast.Ident{Name: "Hidden"},
|
||||
Value: &ast.Ident{Name: "false"},
|
||||
},
|
||||
&ast.KeyValueExpr{
|
||||
Key: &ast.Ident{Name: "Component"},
|
||||
Value: &ast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf("\"%s\"", menus[i].Component)},
|
||||
},
|
||||
&ast.KeyValueExpr{
|
||||
Key: &ast.Ident{Name: "Sort"},
|
||||
Value: &ast.BasicLit{Kind: token.INT, Value: fmt.Sprintf("%d", menus[i].Sort)},
|
||||
},
|
||||
&ast.KeyValueExpr{
|
||||
Key: &ast.Ident{Name: "Meta"},
|
||||
Value: &ast.CompositeLit{
|
||||
Type: &ast.SelectorExpr{
|
||||
X: &ast.Ident{Name: "model"},
|
||||
Sel: &ast.Ident{Name: "Meta"},
|
||||
},
|
||||
Elts: []ast.Expr{
|
||||
&ast.KeyValueExpr{
|
||||
Key: &ast.Ident{Name: "Title"},
|
||||
Value: &ast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf("\"%s\"", menus[i].Title)},
|
||||
},
|
||||
&ast.KeyValueExpr{
|
||||
Key: &ast.Ident{Name: "Icon"},
|
||||
Value: &ast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf("\"%s\"", menus[i].Icon)},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if len(menus[i].Parameters) > 0 {
|
||||
var paramElts []ast.Expr
|
||||
for _, param := range menus[i].Parameters {
|
||||
paramElts = append(paramElts, &ast.CompositeLit{
|
||||
Type: &ast.SelectorExpr{
|
||||
X: &ast.Ident{Name: "model"},
|
||||
Sel: &ast.Ident{Name: "SysBaseMenuParameter"},
|
||||
},
|
||||
Elts: []ast.Expr{
|
||||
&ast.KeyValueExpr{
|
||||
Key: &ast.Ident{Name: "Type"},
|
||||
Value: &ast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf("\"%s\"", param.Type)},
|
||||
},
|
||||
&ast.KeyValueExpr{
|
||||
Key: &ast.Ident{Name: "Key"},
|
||||
Value: &ast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf("\"%s\"", param.Key)},
|
||||
},
|
||||
&ast.KeyValueExpr{
|
||||
Key: &ast.Ident{Name: "Value"},
|
||||
Value: &ast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf("\"%s\"", param.Value)},
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
elts = append(elts, &ast.KeyValueExpr{
|
||||
Key: &ast.Ident{Name: "Parameters"},
|
||||
Value: &ast.CompositeLit{
|
||||
Type: &ast.ArrayType{
|
||||
Elt: &ast.SelectorExpr{
|
||||
X: &ast.Ident{Name: "model"},
|
||||
Sel: &ast.Ident{Name: "SysBaseMenuParameter"},
|
||||
},
|
||||
},
|
||||
Elts: paramElts,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
if len(menus[i].MenuBtn) > 0 {
|
||||
var btnElts []ast.Expr
|
||||
for _, btn := range menus[i].MenuBtn {
|
||||
btnElts = append(btnElts, &ast.CompositeLit{
|
||||
Type: &ast.SelectorExpr{
|
||||
X: &ast.Ident{Name: "model"},
|
||||
Sel: &ast.Ident{Name: "SysBaseMenuBtn"},
|
||||
},
|
||||
Elts: []ast.Expr{
|
||||
&ast.KeyValueExpr{
|
||||
Key: &ast.Ident{Name: "Name"},
|
||||
Value: &ast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf("\"%s\"", btn.Name)},
|
||||
},
|
||||
&ast.KeyValueExpr{
|
||||
Key: &ast.Ident{Name: "Desc"},
|
||||
Value: &ast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf("\"%s\"", btn.Desc)},
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
elts = append(elts, &ast.KeyValueExpr{
|
||||
Key: &ast.Ident{Name: "MenuBtn"},
|
||||
Value: &ast.CompositeLit{
|
||||
Type: &ast.ArrayType{
|
||||
Elt: &ast.SelectorExpr{
|
||||
X: &ast.Ident{Name: "model"},
|
||||
Sel: &ast.Ident{Name: "SysBaseMenuBtn"},
|
||||
},
|
||||
},
|
||||
Elts: btnElts,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
menuElts = append(menuElts, &ast.CompositeLit{
|
||||
Type: nil,
|
||||
Elts: elts,
|
||||
})
|
||||
}
|
||||
return &menuElts
|
||||
}
|
||||
|
||||
// CreateApiStructAst 创建API结构AST
|
||||
func CreateApiStructAst(apis []SysApi) *[]ast.Expr {
|
||||
var apiElts []ast.Expr
|
||||
for i := range apis {
|
||||
elts := []ast.Expr{
|
||||
&ast.KeyValueExpr{
|
||||
Key: &ast.Ident{Name: "Path"},
|
||||
Value: &ast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf("\"%s\"", apis[i].Path)},
|
||||
},
|
||||
&ast.KeyValueExpr{
|
||||
Key: &ast.Ident{Name: "Description"},
|
||||
Value: &ast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf("\"%s\"", apis[i].Description)},
|
||||
},
|
||||
&ast.KeyValueExpr{
|
||||
Key: &ast.Ident{Name: "ApiGroup"},
|
||||
Value: &ast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf("\"%s\"", apis[i].ApiGroup)},
|
||||
},
|
||||
&ast.KeyValueExpr{
|
||||
Key: &ast.Ident{Name: "Method"},
|
||||
Value: &ast.BasicLit{Kind: token.STRING, Value: fmt.Sprintf("\"%s\"", apis[i].Method)},
|
||||
},
|
||||
}
|
||||
apiElts = append(apiElts, &ast.CompositeLit{
|
||||
Type: nil,
|
||||
Elts: elts,
|
||||
})
|
||||
}
|
||||
return &apiElts
|
||||
}
|
||||
Reference in New Issue
Block a user