2021-05-22 14:14:59 +00:00
package beeParser
import (
"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
2021-06-12 20:06:48 +00:00
type Formatter interface {
FieldFormatFunc ( field * StructField ) ( [ ] byte , error )
StructFormatFunc ( node * StructNode ) ( [ ] byte , error )
Marshal ( root * StructNode ) ( [ ] byte , error )
2021-05-23 14:04:23 +00:00
}
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-06-12 20:06:48 +00:00
FormatFunc func ( field * StructField ) ( [ ] byte , error )
2021-05-22 14:14:59 +00:00
}
2021-06-12 20:06:48 +00:00
func ( sf * StructField ) MarshalText ( ) ( [ ] byte , error ) {
if sf . FormatFunc == nil {
return nil , errors . New ( "format func is missing" )
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-06-25 15:15:46 +00:00
func ( sf * StructField ) MarshalJSON ( ) ( [ ] byte , error ) {
if sf . FormatFunc == nil {
return nil , errors . New ( "format func is missing" )
}
return sf . FormatFunc ( sf )
}
2021-05-23 02:34:39 +00:00
// StructNode defines struct node
2021-05-22 14:14:59 +00:00
type StructNode struct {
2021-06-12 20:06:48 +00:00
Name string
Fields [ ] * StructField
FormatFunc func ( node * StructNode ) ( [ ] byte , error )
2021-05-22 14:14:59 +00:00
}
2021-06-12 20:06:48 +00:00
func ( sn * StructNode ) MarshalText ( ) ( [ ] byte , error ) {
if sn . FormatFunc == nil {
return nil , errors . New ( "format func is missing" )
2021-05-22 14:14:59 +00:00
}
2021-06-12 20:06:48 +00:00
return sn . FormatFunc ( sn )
2021-05-22 14:14:59 +00:00
}
2021-06-25 15:15:46 +00:00
func ( sn * StructNode ) MarshalJSON ( ) ( [ ] byte , error ) {
if sn . FormatFunc == nil {
return nil , errors . New ( "format func is missing" )
}
return sn . FormatFunc ( sn )
}
2021-05-23 02:34:39 +00:00
// StructParser parses structs in given file or string
type StructParser struct {
2021-06-12 20:06:48 +00:00
MainStruct * StructNode
Info types . Info
Formatter Formatter
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-06-12 20:06:48 +00:00
func NewStructParser ( filePath string , src interface { } , rootStruct string , formatter Formatter ) ( * 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
}
2021-05-23 14:19:48 +00:00
sp := & StructParser {
2021-06-12 20:06:48 +00:00
Formatter : 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 14:19:48 +00:00
sp . MainStruct = sp . ParseStruct ( structName , s )
2021-05-22 14:14:59 +00:00
return false
} )
2021-05-23 14:19:48 +00:00
if sp . MainStruct == nil {
2021-05-22 14:14:59 +00:00
return nil , errors . New ( "non-exist root struct" )
}
2021-05-23 14:19:48 +00:00
return sp , nil
2021-05-22 14:14:59 +00:00
}
2021-05-23 02:34:39 +00:00
// ParseField parses struct field in nested way
2021-05-23 14:19:48 +00:00
func ( sp * StructParser ) ParseField ( field * ast . Field ) * StructField {
2021-05-23 02:34:39 +00:00
// ast.Print(nil, field)
2021-05-22 14:14:59 +00:00
fieldName := field . Names [ 0 ] . Name
2021-05-23 14:19:48 +00:00
fieldType := sp . 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 {
2021-05-23 14:19:48 +00:00
nestedStruct = sp . ParseStruct ( "" , s )
2021-05-23 02:34:39 +00:00
}
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 14:19:48 +00:00
nestedStruct = sp . 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-06-12 20:06:48 +00:00
FormatFunc : sp . Formatter . FieldFormatFunc ,
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
2021-05-23 14:19:48 +00:00
func ( sp * 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 14:19:48 +00:00
parsedField := sp . ParseField ( field )
2021-05-22 14:14:59 +00:00
if parsedField != nil {
fields = append ( fields , parsedField )
}
}
return & StructNode {
2021-06-12 20:06:48 +00:00
Name : structName ,
Fields : fields ,
FormatFunc : sp . Formatter . StructFormatFunc ,
2021-05-22 14:14:59 +00:00
}
}
2021-06-12 20:06:48 +00:00
func ( sp * StructParser ) Marshal ( ) ( [ ] byte , error ) {
return sp . Formatter . Marshal ( sp . MainStruct )
}