Files

119 lines
2.7 KiB
Go

package pkg
import (
"go/ast"
"go/build"
"go/parser"
"go/token"
"log"
"strings"
"github.com/jinzhu/gorm"
)
// The Parser is used to parse a directory and expose information about the structs defined in the files of this directory.
type Parser struct {
dir string
pkg *build.Package
parsedFiles []*ast.File
}
// NewParser create a new parser instance.
func NewParser(dir string) *Parser {
return &Parser{
dir: dir,
}
}
// getPackage parse dir get go file and package
func (p *Parser) getPackage() {
pkg, err := build.Default.ImportDir(p.dir, build.ImportComment)
if err != nil {
log.Fatalf("cannot process directory %s: %s", p.dir, err)
}
p.pkg = pkg
}
// parseGoFiles parse go file
func (p *Parser) parseGoFiles() {
var parsedFiles []*ast.File
fs := token.NewFileSet()
for _, file := range p.pkg.GoFiles {
file = p.dir + "/" + file
parsedFile, err := parser.ParseFile(fs, file, nil, 0)
if err != nil {
log.Fatalf("parsing package: %s: %s\n", file, err)
}
parsedFiles = append(parsedFiles, parsedFile)
}
p.parsedFiles = parsedFiles
}
// parseTypes parse type of struct
func (p *Parser) parseTypes(file *ast.File) (ret []structConfig) {
ast.Inspect(file, func(n ast.Node) bool {
decl, ok := n.(*ast.GenDecl)
if !ok || decl.Tok != token.TYPE {
return true
}
for _, spec := range decl.Specs {
var (
data structConfig
)
typeSpec, _ok := spec.(*ast.TypeSpec)
if !_ok {
continue
}
// We only care about struct declaration (for now)
var structType *ast.StructType
if structType, ok = typeSpec.Type.(*ast.StructType); !ok {
continue
}
data.StructName = typeSpec.Name.Name
for _, v := range structType.Fields.List {
var (
optionField fieldConfig
)
if t, _ok := v.Type.(*ast.Ident); _ok {
optionField.FieldType = t.String()
} else {
if v.Tag != nil {
if strings.Contains(v.Tag.Value, "gorm") && strings.Contains(v.Tag.Value, "time") {
optionField.FieldType = "time.Time"
}
}
}
if len(v.Names) > 0 {
optionField.FieldName = v.Names[0].String()
optionField.ColumnName = gorm.ToDBName(optionField.FieldName)
optionField.HumpName = SQLColumnToHumpStyle(optionField.ColumnName)
}
data.OptionFields = append(data.OptionFields, optionField)
}
ret = append(ret, data)
}
return true
})
return
}
// Parse should be called before any type querying for the parser. It takes the directory to be parsed and extracts all the structs defined in this directory.
func (p *Parser) Parse() (ret []structConfig) {
var (
data []structConfig
)
p.getPackage()
p.parseGoFiles()
for _, f := range p.parsedFiles {
data = append(data, p.parseTypes(f)...)
}
return data
}