2021-05-22 14:14:59 +00:00
package beeParser
import (
"encoding/json"
"errors"
"go/ast"
2021-05-23 02:34:39 +00:00
"go/importer"
2021-05-22 14:14:59 +00:00
"go/parser"
"go/token"
2021-05-23 02:34:39 +00:00
"go/types"
2021-05-22 14:14:59 +00:00
)
2021-05-23 14:04:23 +00:00
// FieldFormatter transfers the field value to expected format
type FieldFormatter interface {
Format ( field * StructField ) string
}
2021-05-23 02:34:39 +00:00
// StructField defines struct field
2021-05-22 14:14:59 +00:00
type StructField struct {
Name string
2021-05-23 02:34:39 +00:00
Type types . Type
2021-05-22 14:14:59 +00:00
NestedType * StructNode
Comment string
Doc string
Tag string
2021-05-23 14:04:23 +00:00
FormatFunc func ( field * StructField ) string
2021-05-22 14:14:59 +00:00
}
2021-05-23 02:34:39 +00:00
// Key returns the key of the field
2021-05-22 14:14:59 +00:00
func ( sf * StructField ) Key ( ) string {
return sf . Name
}
2021-05-23 02:34:39 +00:00
// Value returns the value of the field
// if the field contains nested struct, it will return a nested result
2021-05-22 14:14:59 +00:00
func ( sf * StructField ) Value ( ) interface { } {
2021-05-23 02:34:39 +00:00
if sf . NestedType != nil {
2021-05-22 14:26:33 +00:00
return sf . NestedType . ToKV ( )
2021-05-22 14:14:59 +00:00
}
2021-05-23 14:04:23 +00:00
return sf . FormatFunc ( sf )
2021-05-22 14:14:59 +00:00
}
2021-05-23 02:34:39 +00:00
// StructNode defines struct node
2021-05-22 14:14:59 +00:00
type StructNode struct {
Name string
Fields [ ] * StructField
}
2021-05-23 02:34:39 +00:00
// ToKV transfers struct to key value pair
2021-05-22 14:14:59 +00:00
func ( sn * StructNode ) ToKV ( ) map [ string ] interface { } {
value := map [ string ] interface { } { }
for _ , field := range sn . Fields {
value [ field . Key ( ) ] = field . Value ( )
}
return value
}
2021-05-23 02:34:39 +00:00
// StructParser parses structs in given file or string
type StructParser struct {
2021-05-23 14:04:23 +00:00
MainStruct * StructNode
Info types . Info
FieldFormatter FieldFormatter
2021-05-22 14:14:59 +00:00
}
2021-05-23 02:34:39 +00:00
// 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
2021-05-23 14:04:23 +00:00
func NewStructParser ( filePath string , src interface { } , rootStruct string , formatter FieldFormatter ) ( * StructParser , error ) {
2021-05-22 14:14:59 +00:00
fset := token . NewFileSet ( )
f , err := parser . ParseFile ( fset , filePath , src , parser . ParseComments )
if err != nil {
return nil , err
}
2021-05-23 02:34:39 +00:00
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 {
2021-05-23 14:04:23 +00:00
FieldFormatter : formatter ,
Info : info ,
2021-05-23 02:34:39 +00:00
}
2021-05-22 14:14:59 +00:00
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
}
2021-05-23 02:34:39 +00:00
cg . MainStruct = cg . ParseStruct ( structName , s )
2021-05-22 14:14:59 +00:00
return false
} )
2021-05-23 02:34:39 +00:00
if cg . MainStruct == nil {
2021-05-22 14:14:59 +00:00
return nil , errors . New ( "non-exist root struct" )
}
2021-05-23 02:34:39 +00:00
return cg , nil
2021-05-22 14:14:59 +00:00
}
2021-05-23 02:34:39 +00:00
func ( cg * StructParser ) ToJSON ( ) ( [ ] byte , error ) {
value := cg . MainStruct . ToKV ( )
2021-05-22 14:14:59 +00:00
return json . MarshalIndent ( value , "" , " " )
}
2021-05-23 02:34:39 +00:00
// ParseField parses struct field in nested way
func ( cg * StructParser ) ParseField ( field * ast . Field ) * StructField {
// ast.Print(nil, field)
2021-05-22 14:14:59 +00:00
fieldName := field . Names [ 0 ] . Name
2021-05-23 02:34:39 +00:00
fieldType := cg . Info . TypeOf ( field . Type )
2021-05-22 14:14:59 +00:00
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 ( )
}
2021-05-23 02:34:39 +00:00
var nestedStruct * StructNode
if s , isInlineStruct := field . Type . ( * ast . StructType ) ; isInlineStruct {
nestedStruct = cg . ParseStruct ( "" , s )
}
if _ , isNamedStructorBasic := field . Type . ( * ast . Ident ) ; isNamedStructorBasic && field . Type . ( * ast . Ident ) . Obj != nil {
2021-05-22 14:14:59 +00:00
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
}
2021-05-23 02:34:39 +00:00
nestedStruct = cg . ParseStruct ( ts . Name . Name , s )
2021-05-22 14:14:59 +00:00
}
2021-05-23 02:34:39 +00:00
// fieldType.(*types.Basic) // basic type
// *ast.ArrayType:
// *ast.MapType:
// *ast.SelectorExpr: // third party
2021-05-22 14:14:59 +00:00
2021-05-22 14:26:33 +00:00
return & StructField {
2021-05-23 02:34:39 +00:00
Name : fieldName ,
Type : fieldType ,
Tag : fieldTag ,
Comment : fieldComment ,
Doc : fieldDoc ,
NestedType : nestedStruct ,
2021-05-23 14:04:23 +00:00
FormatFunc : cg . FieldFormatter . Format ,
2021-05-22 14:26:33 +00:00
}
2021-05-22 14:14:59 +00:00
}
2021-05-23 02:34:39 +00:00
// ParseStruct parses struct in nested way
func ( cg * StructParser ) ParseStruct ( structName string , s * ast . StructType ) * StructNode {
2021-05-22 14:14:59 +00:00
fields := [ ] * StructField { }
for _ , field := range s . Fields . List {
2021-05-23 02:34:39 +00:00
parsedField := cg . ParseField ( field )
2021-05-22 14:14:59 +00:00
if parsedField != nil {
fields = append ( fields , parsedField )
}
}
return & StructNode {
Name : structName ,
Fields : fields ,
}
}