mirror of
https://github.com/beego/bee.git
synced 2024-11-22 05:00:54 +00:00
add parser
This commit is contained in:
parent
d63e07087c
commit
70c4a5f5f0
184
parser/parser.go
Normal file
184
parser/parser.go
Normal file
@ -0,0 +1,184 @@
|
|||||||
|
package beeParser
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"go/ast"
|
||||||
|
"go/parser"
|
||||||
|
"go/token"
|
||||||
|
)
|
||||||
|
|
||||||
|
// type to default value
|
||||||
|
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 {
|
||||||
|
Name string
|
||||||
|
Type string
|
||||||
|
NestedType *StructNode
|
||||||
|
Comment string
|
||||||
|
Doc string
|
||||||
|
Tag string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sf *StructField) IsBuiltInType() bool {
|
||||||
|
_, found := builtInTypeMap[sf.Type]
|
||||||
|
return found
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sf *StructField) Key() string {
|
||||||
|
return sf.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sf *StructField) Value() interface{} {
|
||||||
|
val, found := builtInTypeMap[sf.Type]
|
||||||
|
if found {
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
|
||||||
|
return sf.NestedType.ToKV()
|
||||||
|
}
|
||||||
|
|
||||||
|
type StructNode struct {
|
||||||
|
Name string
|
||||||
|
Fields []*StructField
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sn *StructNode) ToKV() map[string]interface{} {
|
||||||
|
value := map[string]interface{}{}
|
||||||
|
for _, field := range sn.Fields {
|
||||||
|
value[field.Key()] = field.Value()
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
type ConfigGenerator struct {
|
||||||
|
StructMap map[string]*StructNode // @todo key = {package}+{struct name}
|
||||||
|
RootStruct string //match with the key of StructMap
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewConfigGenerator(filePath string, src interface{}, rootStruct string) (*ConfigGenerator, error) {
|
||||||
|
fset := token.NewFileSet()
|
||||||
|
f, err := parser.ParseFile(fset, filePath, src, parser.ParseComments)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
structMap := map[string]*StructNode{}
|
||||||
|
|
||||||
|
ast.Inspect(f, func(n ast.Node) bool {
|
||||||
|
// ast.Print(nil, n)
|
||||||
|
ts, ok := n.(*ast.TypeSpec)
|
||||||
|
if !ok || ts.Type == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
structName := ts.Name.Name
|
||||||
|
if structName != rootStruct {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
s, ok := ts.Type.(*ast.StructType)
|
||||||
|
if !ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
structMap[structName] = ParseStruct(structName, s)
|
||||||
|
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
|
||||||
|
if _, found := structMap[rootStruct]; !found {
|
||||||
|
return nil, errors.New("non-exist root struct")
|
||||||
|
}
|
||||||
|
|
||||||
|
return &ConfigGenerator{
|
||||||
|
StructMap: structMap,
|
||||||
|
RootStruct: rootStruct,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cg *ConfigGenerator) ToJSON() ([]byte, error) {
|
||||||
|
rootStruct := cg.StructMap[cg.RootStruct]
|
||||||
|
value := rootStruct.ToKV()
|
||||||
|
return json.MarshalIndent(value, "", " ")
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseField(field *ast.Field) *StructField {
|
||||||
|
fieldName := field.Names[0].Name
|
||||||
|
fieldType := fmt.Sprint(field.Type)
|
||||||
|
|
||||||
|
fieldTag := ""
|
||||||
|
if field.Tag != nil {
|
||||||
|
fieldTag = field.Tag.Value
|
||||||
|
}
|
||||||
|
fieldComment := ""
|
||||||
|
if field.Comment != nil {
|
||||||
|
fieldComment = field.Comment.Text()
|
||||||
|
}
|
||||||
|
fieldDoc := ""
|
||||||
|
if field.Doc != nil {
|
||||||
|
fieldDoc = field.Doc.Text()
|
||||||
|
}
|
||||||
|
|
||||||
|
switch field.Type.(type) {
|
||||||
|
case *ast.Ident: // built-in or nested
|
||||||
|
isNested := (field.Type.(*ast.Ident).Obj != nil)
|
||||||
|
if !isNested {
|
||||||
|
return &StructField{
|
||||||
|
Name: fieldName,
|
||||||
|
Type: fieldType,
|
||||||
|
Tag: fieldTag,
|
||||||
|
Comment: fieldComment,
|
||||||
|
Doc: fieldDoc,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ts, ok := field.Type.(*ast.Ident).Obj.Decl.(*ast.TypeSpec)
|
||||||
|
if !ok || ts.Type == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
s, ok := ts.Type.(*ast.StructType)
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &StructField{
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseStruct(structName string, s *ast.StructType) *StructNode {
|
||||||
|
fields := []*StructField{}
|
||||||
|
for _, field := range s.Fields.List {
|
||||||
|
parsedField := ParseField(field)
|
||||||
|
if parsedField != nil {
|
||||||
|
fields = append(fields, parsedField)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &StructNode{
|
||||||
|
Name: structName,
|
||||||
|
Fields: fields,
|
||||||
|
}
|
||||||
|
}
|
39
parser/parser_test.go
Normal file
39
parser/parser_test.go
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
package beeParser
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ExampleConfigGenerator() {
|
||||||
|
const src = `
|
||||||
|
package p
|
||||||
|
import "http"
|
||||||
|
|
||||||
|
type StructB struct {
|
||||||
|
Field1 string
|
||||||
|
}
|
||||||
|
type StructA struct {
|
||||||
|
Field1 string
|
||||||
|
Field2 StructB
|
||||||
|
Field3 []string
|
||||||
|
Field4 map[string]string
|
||||||
|
Field5 http.SameSite
|
||||||
|
}
|
||||||
|
`
|
||||||
|
cg, err := NewConfigGenerator("./sample.go", src, "StructA")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
b, err := cg.ToJSON()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(string(b))
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// {
|
||||||
|
// }
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user