394 lines
9.8 KiB
Go
394 lines
9.8 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"go/ast"
|
|
"go/token"
|
|
"golang.org/x/tools/go/packages"
|
|
"log"
|
|
"mongo.games.com/mongoctl/template"
|
|
"os"
|
|
"path/filepath"
|
|
"reflect"
|
|
"strings"
|
|
)
|
|
|
|
const (
|
|
symbolBacktick = "`"
|
|
)
|
|
|
|
const (
|
|
symbolBacktickKey = "SymbolBacktick"
|
|
)
|
|
|
|
const (
|
|
varPackagesKey = "VarPackages"
|
|
varModelClassNameKey = "VarModelClassName"
|
|
varModelPackageNameKey = "VarModelPackageName"
|
|
varModelPackagePathKey = "VarModelPackagePath"
|
|
varModelVariableNameKey = "VarModelVariableName"
|
|
varModelColumnsDefineKey = "VarModelColumnsDefine"
|
|
varModelColumnsInstanceKey = "VarModelColumnsInstance"
|
|
varDaoClassNameKey = "VarDaoClassName"
|
|
varDaoVariableNameKey = "VarDaoVariableName"
|
|
varDaoPackageNameKey = "VarDaoPackageName"
|
|
varDaoPackagePathKey = "VarDaoPackagePath"
|
|
varDaoPrefixNameKey = "VarDaoPrefixName"
|
|
varCollectionNameKey = "VarCollectionName"
|
|
varAutofillCodeKey = "VarAutofillCode"
|
|
)
|
|
|
|
const defaultCounterName = "Counter"
|
|
|
|
type options struct {
|
|
modelDir string //
|
|
modelPkgPath string //
|
|
modelPkgAlias string //
|
|
modelNames []string //
|
|
daoDir string
|
|
daoPkgPath string
|
|
subPkgEnable bool
|
|
subPkgStyle style
|
|
counterName string
|
|
fileNameStyle style
|
|
}
|
|
|
|
type generator struct {
|
|
opts *options
|
|
counter *counter
|
|
modelNames map[string]struct{}
|
|
}
|
|
|
|
func newGenerator(opts *options) *generator {
|
|
modelNames := make(map[string]struct{}, len(opts.modelNames))
|
|
for _, modelName := range opts.modelNames {
|
|
if isExportable(modelName) { // 是否导出字段
|
|
modelNames[modelName] = struct{}{}
|
|
}
|
|
}
|
|
|
|
if len(modelNames) == 0 {
|
|
log.Fatalf("error: %d model type names found", len(modelNames))
|
|
}
|
|
|
|
if opts.counterName == "" {
|
|
opts.counterName = defaultCounterName
|
|
}
|
|
|
|
return &generator{
|
|
opts: opts,
|
|
counter: newCounter(opts),
|
|
modelNames: modelNames,
|
|
}
|
|
}
|
|
|
|
func (g *generator) makeDao() {
|
|
models := g.parseModels()
|
|
|
|
for _, m := range models {
|
|
g.makeModelInternalDao(m)
|
|
|
|
g.makeModelExternalDao(m)
|
|
|
|
fmt.Printf("%s's dao file generated successfully\n", m.modelName)
|
|
|
|
if !m.isDependCounter {
|
|
continue
|
|
}
|
|
|
|
g.makeCounterInternalDao()
|
|
|
|
g.makeCounterExternalDao()
|
|
}
|
|
}
|
|
|
|
// generate an internal dao file based on model
|
|
func (g *generator) makeModelInternalDao(m *model) {
|
|
replaces := make(map[string]string)
|
|
replaces[varModelClassNameKey] = m.modelClassName
|
|
replaces[varModelPackageNameKey] = m.modelPkgName
|
|
replaces[varModelPackagePathKey] = m.modelPkgPath
|
|
replaces[varModelVariableNameKey] = m.modelVariableName
|
|
replaces[varDaoPrefixNameKey] = m.daoPrefixName
|
|
replaces[varDaoClassNameKey] = m.daoClassName
|
|
replaces[varDaoVariableNameKey] = m.daoVariableName
|
|
replaces[varCollectionNameKey] = m.collectionName
|
|
replaces[varModelColumnsDefineKey] = m.modelColumnsDefined()
|
|
replaces[varModelColumnsInstanceKey] = m.modelColumnsInstance()
|
|
replaces[varAutofillCodeKey] = m.autoFillCode()
|
|
replaces[varPackagesKey] = m.packages()
|
|
|
|
file := m.daoOutputDir + "/internal/" + m.daoOutputFile
|
|
|
|
err := doWrite(file, template.InternalTemplate, replaces)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
}
|
|
|
|
// generate an external dao file based on model
|
|
func (g *generator) makeModelExternalDao(m *model) {
|
|
file := m.daoOutputDir + "/" + m.daoOutputFile
|
|
|
|
_, err := os.Stat(file)
|
|
if err != nil {
|
|
switch {
|
|
case os.IsNotExist(err):
|
|
// ignore
|
|
case os.IsExist(err):
|
|
return
|
|
default:
|
|
log.Fatal(err)
|
|
}
|
|
} else {
|
|
return
|
|
}
|
|
|
|
replaces := make(map[string]string)
|
|
replaces[varDaoClassNameKey] = m.daoClassName
|
|
replaces[varDaoPrefixNameKey] = m.daoPrefixName
|
|
replaces[varDaoPackageNameKey] = m.daoPkgName
|
|
replaces[varDaoPackagePathKey] = m.daoPkgPath
|
|
|
|
err = doWrite(file, template.ExternalTemplate, replaces)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
}
|
|
|
|
// generate an internal dao file based on counter model
|
|
func (g *generator) makeCounterInternalDao() {
|
|
replaces := make(map[string]string)
|
|
replaces[varDaoClassNameKey] = g.counter.daoClassName
|
|
replaces[varDaoPrefixNameKey] = g.counter.daoPrefixName
|
|
replaces[varDaoVariableNameKey] = g.counter.daoVariableName
|
|
replaces[varCollectionNameKey] = g.counter.collectionName
|
|
replaces[symbolBacktickKey] = symbolBacktick
|
|
|
|
file := g.counter.daoOutputDir + "/internal/" + g.counter.daoOutputFile
|
|
|
|
err := doWrite(file, template.CounterInternalTemplate, replaces)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
}
|
|
|
|
// generate an external dao file based on counter model
|
|
func (g *generator) makeCounterExternalDao() {
|
|
file := g.counter.daoOutputDir + "/" + g.counter.daoOutputFile
|
|
|
|
_, err := os.Stat(file)
|
|
if err != nil {
|
|
switch {
|
|
case os.IsNotExist(err):
|
|
// ignore
|
|
case os.IsExist(err):
|
|
return
|
|
default:
|
|
log.Fatal(err)
|
|
}
|
|
} else {
|
|
return
|
|
}
|
|
|
|
replaces := make(map[string]string)
|
|
replaces[varDaoClassNameKey] = g.counter.daoClassName
|
|
replaces[varDaoPackageNameKey] = g.counter.daoPkgName
|
|
replaces[varDaoPackagePathKey] = g.counter.daoPkgPath
|
|
|
|
err = doWrite(file, template.CounterExternalTemplate, replaces)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
}
|
|
|
|
// parse multiple models from the go file
|
|
func (g *generator) parseModels() []*model {
|
|
var (
|
|
pkg = g.loadPackage()
|
|
models = make([]*model, 0, len(pkg.Syntax))
|
|
daoPkgPath = g.opts.daoPkgPath
|
|
modelPkgPath = g.opts.modelPkgPath
|
|
modelPkgName = g.opts.modelPkgAlias
|
|
)
|
|
|
|
if g.opts.daoPkgPath == "" && pkg.Module != nil {
|
|
outPath, err := filepath.Abs(g.opts.daoDir)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
daoPkgPath = pkg.Module.Path + outPath[len(pkg.Module.Dir):]
|
|
}
|
|
|
|
daoPkgPath = strings.ReplaceAll(daoPkgPath, `\`, `/`)
|
|
|
|
g.counter.setDaoPkgPath(daoPkgPath)
|
|
|
|
for _, file := range pkg.Syntax {
|
|
if g.opts.modelPkgPath == "" && pkg.Module != nil && pkg.Fset != nil {
|
|
filePath := filepath.Dir(pkg.Fset.Position(file.Package).Filename)
|
|
modelPkgPath = pkg.Module.Path + filePath[len(pkg.Module.Dir):]
|
|
}
|
|
|
|
modelPkgPath = strings.ReplaceAll(modelPkgPath, `\`, `/`)
|
|
modelPkgName = file.Name.Name
|
|
|
|
ast.Inspect(file, func(node ast.Node) bool {
|
|
decl, ok := node.(*ast.GenDecl)
|
|
if !ok || decl.Tok != token.TYPE {
|
|
return true
|
|
}
|
|
|
|
for _, s := range decl.Specs {
|
|
spec, ok := s.(*ast.TypeSpec)
|
|
if !ok {
|
|
continue
|
|
}
|
|
|
|
_, ok = g.modelNames[spec.Name.Name]
|
|
if !ok {
|
|
continue
|
|
}
|
|
|
|
st, ok := spec.Type.(*ast.StructType)
|
|
if !ok {
|
|
continue
|
|
}
|
|
|
|
model := newModel(g.opts)
|
|
model.setModelName(spec.Name.Name)
|
|
model.setModelPkg(modelPkgName, modelPkgPath)
|
|
model.setDaoPkgPath(daoPkgPath)
|
|
|
|
for _, item := range st.Fields.List {
|
|
name := item.Names[0].Name
|
|
|
|
if !isExportable(name) {
|
|
continue
|
|
}
|
|
|
|
field := &field{name: name, column: name}
|
|
|
|
if item.Tag != nil && len(item.Tag.Value) > 2 {
|
|
runes := []rune(item.Tag.Value)
|
|
if runes[0] != '`' || runes[len(runes)-1] != '`' {
|
|
continue
|
|
}
|
|
|
|
tag := reflect.StructTag(runes[1 : len(runes)-1])
|
|
|
|
if column := tag.Get("bson"); column != "" {
|
|
field.column = column
|
|
}
|
|
|
|
val, ok := tag.Lookup("gen")
|
|
if ok {
|
|
parts := strings.Split(val, ";")
|
|
for _, part := range parts {
|
|
if part == "" {
|
|
continue
|
|
}
|
|
|
|
switch eles := strings.SplitN(part, ":", 2); eles[0] {
|
|
case "autoFill":
|
|
expr, ok := item.Type.(*ast.SelectorExpr)
|
|
if !ok {
|
|
continue
|
|
}
|
|
|
|
switch fmt.Sprintf("%s.%s", expr.X.(*ast.Ident).Name, expr.Sel.Name) {
|
|
case "primitive.ObjectID":
|
|
field.autoFill = objectID
|
|
model.addImport(pkg3)
|
|
case "primitive.DateTime":
|
|
field.autoFill = dateTime
|
|
model.addImport(pkg1)
|
|
model.addImport(pkg3)
|
|
}
|
|
case "autoIncr":
|
|
if len(eles) != 2 || eles[1] == "" {
|
|
continue
|
|
}
|
|
|
|
expr, ok := item.Type.(*ast.Ident)
|
|
if !ok {
|
|
continue
|
|
}
|
|
|
|
switch expr.Name {
|
|
case "int", "int8", "int16", "int32", "int64", "uint", "uint8", "uint16", "uint32", "uint64":
|
|
field.autoFill = autoIncr
|
|
field.autoIncrFieldName = eles[1]
|
|
|
|
if g.opts.subPkgEnable {
|
|
model.addImport(g.counter.daoPkgPath)
|
|
}
|
|
|
|
switch expr.Name {
|
|
case "int":
|
|
field.autoIncrFieldKind = reflect.Int
|
|
case "int8":
|
|
field.autoIncrFieldKind = reflect.Int8
|
|
case "int16":
|
|
field.autoIncrFieldKind = reflect.Int16
|
|
case "int32":
|
|
field.autoIncrFieldKind = reflect.Int32
|
|
case "int64":
|
|
field.autoIncrFieldKind = reflect.Int64
|
|
case "uint":
|
|
field.autoIncrFieldKind = reflect.Uint
|
|
case "uint8":
|
|
field.autoIncrFieldKind = reflect.Uint8
|
|
case "uint16":
|
|
field.autoIncrFieldKind = reflect.Uint16
|
|
case "uint32":
|
|
field.autoIncrFieldKind = reflect.Uint32
|
|
case "uint64":
|
|
field.autoIncrFieldKind = reflect.Uint64
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if item.Doc != nil {
|
|
field.documents = make([]string, 0, len(item.Doc.List))
|
|
for _, doc := range item.Doc.List {
|
|
field.documents = append(field.documents, doc.Text)
|
|
}
|
|
}
|
|
|
|
if item.Comment != nil {
|
|
field.comment = item.Comment.List[0].Text
|
|
}
|
|
|
|
model.addFields(field)
|
|
}
|
|
|
|
models = append(models, model)
|
|
}
|
|
|
|
return true
|
|
})
|
|
}
|
|
|
|
return models
|
|
}
|
|
|
|
func (g *generator) loadPackage() *packages.Package {
|
|
cfg := &packages.Config{
|
|
Mode: packages.NeedName | packages.NeedFiles | packages.NeedCompiledGoFiles | packages.NeedImports | packages.NeedTypes | packages.NeedTypesSizes | packages.NeedSyntax | packages.NeedTypesInfo | packages.NeedModule,
|
|
Tests: false,
|
|
}
|
|
pkgs, err := packages.Load(cfg, g.opts.modelDir)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
if len(pkgs) != 1 {
|
|
log.Fatalf("error: %d packages found", len(pkgs))
|
|
}
|
|
|
|
return pkgs[0]
|
|
}
|