mirror of
https://github.com/beego/bee.git
synced 2024-11-24 13:30:53 +00:00
supports inline struct and optimized code
This commit is contained in:
parent
c3bb85f7b1
commit
aa69764e9f
128
parser/parser.go
128
parser/parser.go
@ -3,64 +3,45 @@ package beeParser
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"go/ast"
|
"go/ast"
|
||||||
|
"go/importer"
|
||||||
"go/parser"
|
"go/parser"
|
||||||
"go/token"
|
"go/token"
|
||||||
|
"go/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
// type to default value
|
// StructField defines struct field
|
||||||
var builtInTypeMap = map[string]interface{}{
|
|
||||||
"string": "",
|
|
||||||
"int": 0,
|
|
||||||
"int64": 0,
|
|
||||||
"int32": 0,
|
|
||||||
"uint": 0,
|
|
||||||
"uint32": 0,
|
|
||||||
"uint64": 0,
|
|
||||||
"bool": false,
|
|
||||||
// @todo add more type
|
|
||||||
}
|
|
||||||
|
|
||||||
type StructField struct {
|
type StructField struct {
|
||||||
Name string
|
Name string
|
||||||
Type ast.Expr
|
Type types.Type
|
||||||
NestedType *StructNode
|
NestedType *StructNode
|
||||||
Comment string
|
Comment string
|
||||||
Doc string
|
Doc string
|
||||||
Tag string
|
Tag string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sf *StructField) IsBuiltInType() bool {
|
// Key returns the key of the field
|
||||||
_, found := builtInTypeMap[fmt.Sprint(sf.Type)]
|
|
||||||
return found
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sf *StructField) Key() string {
|
func (sf *StructField) Key() string {
|
||||||
return sf.Name
|
return sf.Name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Value returns the value of the field
|
||||||
|
// if the field contains nested struct, it will return a nested result
|
||||||
func (sf *StructField) Value() interface{} {
|
func (sf *StructField) Value() interface{} {
|
||||||
switch sf.Type.(type) {
|
if sf.NestedType != nil {
|
||||||
case *ast.Ident:
|
|
||||||
val, found := builtInTypeMap[fmt.Sprint(sf.Type)]
|
|
||||||
if found {
|
|
||||||
return val
|
|
||||||
}
|
|
||||||
return sf.NestedType.ToKV()
|
return sf.NestedType.ToKV()
|
||||||
case *ast.ArrayType:
|
|
||||||
case *ast.MapType:
|
|
||||||
case *ast.SelectorExpr: // third party
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StructNode defines struct node
|
||||||
type StructNode struct {
|
type StructNode struct {
|
||||||
Name string
|
Name string
|
||||||
Fields []*StructField
|
Fields []*StructField
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ToKV transfers struct to key value pair
|
||||||
func (sn *StructNode) ToKV() map[string]interface{} {
|
func (sn *StructNode) ToKV() map[string]interface{} {
|
||||||
value := map[string]interface{}{}
|
value := map[string]interface{}{}
|
||||||
for _, field := range sn.Fields {
|
for _, field := range sn.Fields {
|
||||||
@ -69,19 +50,39 @@ func (sn *StructNode) ToKV() map[string]interface{} {
|
|||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
|
||||||
type ConfigGenerator struct {
|
// StructParser parses structs in given file or string
|
||||||
StructMap map[string]*StructNode // @todo key = {package}+{struct name}
|
type StructParser struct {
|
||||||
RootStruct string //match with the key of StructMap
|
MainStruct *StructNode
|
||||||
|
Info types.Info
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewConfigGenerator(filePath string, src interface{}, rootStruct string) (*ConfigGenerator, error) {
|
// NewStructParser is the constructor of StructParser
|
||||||
|
// filePath and src follow the same rule with go/parser.ParseFile
|
||||||
|
// If src != nil, ParseFile parses the source from src and the filename is only used when recording position information. The type of the argument for the src parameter must be string, []byte, or io.Reader. If src == nil, ParseFile parses the file specified by filename.
|
||||||
|
// rootStruct is the root struct we want to use
|
||||||
|
func NewStructParser(filePath string, src interface{}, rootStruct string) (*StructParser, error) {
|
||||||
fset := token.NewFileSet()
|
fset := token.NewFileSet()
|
||||||
f, err := parser.ParseFile(fset, filePath, src, parser.ParseComments)
|
f, err := parser.ParseFile(fset, filePath, src, parser.ParseComments)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
structMap := map[string]*StructNode{}
|
info := types.Info{
|
||||||
|
Types: make(map[ast.Expr]types.TypeAndValue),
|
||||||
|
Defs: make(map[*ast.Ident]types.Object),
|
||||||
|
Uses: make(map[*ast.Ident]types.Object),
|
||||||
|
}
|
||||||
|
conf := types.Config{
|
||||||
|
Importer: importer.ForCompiler(fset, "source", nil),
|
||||||
|
}
|
||||||
|
_, err = conf.Check("src", fset, []*ast.File{f}, &info)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
cg := &StructParser{
|
||||||
|
Info: info,
|
||||||
|
}
|
||||||
|
|
||||||
ast.Inspect(f, func(n ast.Node) bool {
|
ast.Inspect(f, func(n ast.Node) bool {
|
||||||
// ast.Print(nil, n)
|
// ast.Print(nil, n)
|
||||||
@ -100,30 +101,27 @@ func NewConfigGenerator(filePath string, src interface{}, rootStruct string) (*C
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
structMap[structName] = ParseStruct(structName, s)
|
cg.MainStruct = cg.ParseStruct(structName, s)
|
||||||
|
|
||||||
return false
|
return false
|
||||||
})
|
})
|
||||||
|
|
||||||
if _, found := structMap[rootStruct]; !found {
|
if cg.MainStruct == nil {
|
||||||
return nil, errors.New("non-exist root struct")
|
return nil, errors.New("non-exist root struct")
|
||||||
}
|
}
|
||||||
|
|
||||||
return &ConfigGenerator{
|
return cg, nil
|
||||||
StructMap: structMap,
|
|
||||||
RootStruct: rootStruct,
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cg *ConfigGenerator) ToJSON() ([]byte, error) {
|
func (cg *StructParser) ToJSON() ([]byte, error) {
|
||||||
rootStruct := cg.StructMap[cg.RootStruct]
|
value := cg.MainStruct.ToKV()
|
||||||
value := rootStruct.ToKV()
|
|
||||||
return json.MarshalIndent(value, "", " ")
|
return json.MarshalIndent(value, "", " ")
|
||||||
}
|
}
|
||||||
|
|
||||||
func ParseField(field *ast.Field) *StructField {
|
// ParseField parses struct field in nested way
|
||||||
|
func (cg *StructParser) ParseField(field *ast.Field) *StructField {
|
||||||
|
// ast.Print(nil, field)
|
||||||
fieldName := field.Names[0].Name
|
fieldName := field.Names[0].Name
|
||||||
fieldType := field.Type
|
fieldType := cg.Info.TypeOf(field.Type)
|
||||||
|
|
||||||
fieldTag := ""
|
fieldTag := ""
|
||||||
if field.Tag != nil {
|
if field.Tag != nil {
|
||||||
@ -138,18 +136,12 @@ func ParseField(field *ast.Field) *StructField {
|
|||||||
fieldDoc = field.Doc.Text()
|
fieldDoc = field.Doc.Text()
|
||||||
}
|
}
|
||||||
|
|
||||||
switch field.Type.(type) {
|
var nestedStruct *StructNode
|
||||||
case *ast.Ident: // built-in or nested
|
if s, isInlineStruct := field.Type.(*ast.StructType); isInlineStruct {
|
||||||
isNested := (field.Type.(*ast.Ident).Obj != nil)
|
nestedStruct = cg.ParseStruct("", s)
|
||||||
if !isNested {
|
|
||||||
return &StructField{
|
|
||||||
Name: fieldName,
|
|
||||||
Type: fieldType,
|
|
||||||
Tag: fieldTag,
|
|
||||||
Comment: fieldComment,
|
|
||||||
Doc: fieldDoc,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if _, isNamedStructorBasic := field.Type.(*ast.Ident); isNamedStructorBasic && field.Type.(*ast.Ident).Obj != nil {
|
||||||
ts, ok := field.Type.(*ast.Ident).Obj.Decl.(*ast.TypeSpec)
|
ts, ok := field.Type.(*ast.Ident).Obj.Decl.(*ast.TypeSpec)
|
||||||
if !ok || ts.Type == nil {
|
if !ok || ts.Type == nil {
|
||||||
return nil
|
return nil
|
||||||
@ -159,18 +151,12 @@ func ParseField(field *ast.Field) *StructField {
|
|||||||
if !ok {
|
if !ok {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return &StructField{
|
nestedStruct = cg.ParseStruct(ts.Name.Name, s)
|
||||||
Name: fieldName,
|
|
||||||
Type: fieldType,
|
|
||||||
Tag: fieldTag,
|
|
||||||
Comment: fieldComment,
|
|
||||||
Doc: fieldDoc,
|
|
||||||
NestedType: ParseStruct(ts.Name.Name, s),
|
|
||||||
}
|
|
||||||
case *ast.ArrayType:
|
|
||||||
case *ast.MapType:
|
|
||||||
case *ast.SelectorExpr: // third party
|
|
||||||
}
|
}
|
||||||
|
// fieldType.(*types.Basic) // basic type
|
||||||
|
// *ast.ArrayType:
|
||||||
|
// *ast.MapType:
|
||||||
|
// *ast.SelectorExpr: // third party
|
||||||
|
|
||||||
return &StructField{
|
return &StructField{
|
||||||
Name: fieldName,
|
Name: fieldName,
|
||||||
@ -178,13 +164,15 @@ func ParseField(field *ast.Field) *StructField {
|
|||||||
Tag: fieldTag,
|
Tag: fieldTag,
|
||||||
Comment: fieldComment,
|
Comment: fieldComment,
|
||||||
Doc: fieldDoc,
|
Doc: fieldDoc,
|
||||||
|
NestedType: nestedStruct,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func ParseStruct(structName string, s *ast.StructType) *StructNode {
|
// ParseStruct parses struct in nested way
|
||||||
|
func (cg *StructParser) ParseStruct(structName string, s *ast.StructType) *StructNode {
|
||||||
fields := []*StructField{}
|
fields := []*StructField{}
|
||||||
for _, field := range s.Fields.List {
|
for _, field := range s.Fields.List {
|
||||||
parsedField := ParseField(field)
|
parsedField := cg.ParseField(field)
|
||||||
if parsedField != nil {
|
if parsedField != nil {
|
||||||
fields = append(fields, parsedField)
|
fields = append(fields, parsedField)
|
||||||
}
|
}
|
||||||
|
@ -5,23 +5,32 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
)
|
)
|
||||||
|
|
||||||
func ExampleConfigGenerator() {
|
func ExampleStructParser() {
|
||||||
const src = `
|
const src = `
|
||||||
package p
|
package p
|
||||||
import "http"
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
type StructB struct {
|
type StructB struct {
|
||||||
Field1 string
|
Field1 string
|
||||||
}
|
}
|
||||||
type StructA struct {
|
type StructA struct {
|
||||||
Field1 string
|
Field1 string
|
||||||
Field2 StructB
|
Field2 struct{
|
||||||
|
a string
|
||||||
|
b string
|
||||||
|
}
|
||||||
Field3 []string
|
Field3 []string
|
||||||
Field4 map[string]string
|
Field4 map[string]string
|
||||||
Field5 http.SameSite
|
Field5 http.SameSite
|
||||||
|
Field6 func(int)
|
||||||
|
Field7 StructB
|
||||||
}
|
}
|
||||||
|
|
||||||
`
|
`
|
||||||
cg, err := NewConfigGenerator("./sample.go", src, "StructA")
|
cg, err := NewStructParser("src.go", src, "StructA")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -35,5 +44,17 @@ type StructA struct {
|
|||||||
|
|
||||||
// Output:
|
// Output:
|
||||||
// {
|
// {
|
||||||
|
// "Field1": "",
|
||||||
|
// "Field2": {
|
||||||
|
// "a": "",
|
||||||
|
// "b": ""
|
||||||
|
// },
|
||||||
|
// "Field3": "",
|
||||||
|
// "Field4": "",
|
||||||
|
// "Field5": "",
|
||||||
|
// "Field6": "",
|
||||||
|
// "Field7": {
|
||||||
|
// "Field1": ""
|
||||||
|
// }
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user