diff --git a/parser/parser.go b/parser/parser.go index 15d0fe6..8559e6b 100644 --- a/parser/parser.go +++ b/parser/parser.go @@ -1,7 +1,6 @@ package beeParser import ( - "encoding/json" "errors" "go/ast" "go/importer" @@ -11,8 +10,10 @@ import ( ) // FieldFormatter transfers the field value to expected format -type FieldFormatter interface { - Format(field *StructField) string +type Formatter interface { + FieldFormatFunc(field *StructField) ([]byte, error) + StructFormatFunc(node *StructNode) ([]byte, error) + Marshal(root *StructNode) ([]byte, error) } // StructField defines struct field @@ -23,51 +24,43 @@ type StructField struct { Comment string Doc string Tag string - FormatFunc func(field *StructField) string + FormatFunc func(field *StructField) ([]byte, error) } -// Key returns the key of the field -func (sf *StructField) Key() string { - 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{} { - if sf.NestedType != nil { - return sf.NestedType.ToKV() +func (sf *StructField) MarshalText() ([]byte, error) { + if sf.FormatFunc == nil { + return nil, errors.New("format func is missing") } - return sf.FormatFunc(sf) } // StructNode defines struct node type StructNode struct { - Name string - Fields []*StructField + Name string + Fields []*StructField + FormatFunc func(node *StructNode) ([]byte, error) } -// ToKV transfers struct to key value pair -func (sn *StructNode) ToKV() map[string]interface{} { - value := map[string]interface{}{} - for _, field := range sn.Fields { - value[field.Key()] = field.Value() +func (sn *StructNode) MarshalText() ([]byte, error) { + if sn.FormatFunc == nil { + return nil, errors.New("format func is missing") } - return value + + return sn.FormatFunc(sn) } // StructParser parses structs in given file or string type StructParser struct { - MainStruct *StructNode - Info types.Info - FieldFormatter FieldFormatter + MainStruct *StructNode + Info types.Info + Formatter Formatter } // 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, formatter FieldFormatter) (*StructParser, error) { +func NewStructParser(filePath string, src interface{}, rootStruct string, formatter Formatter) (*StructParser, error) { fset := token.NewFileSet() f, err := parser.ParseFile(fset, filePath, src, parser.ParseComments) if err != nil { @@ -88,8 +81,8 @@ func NewStructParser(filePath string, src interface{}, rootStruct string, format } sp := &StructParser{ - FieldFormatter: formatter, - Info: info, + Formatter: formatter, + Info: info, } ast.Inspect(f, func(n ast.Node) bool { @@ -120,11 +113,6 @@ func NewStructParser(filePath string, src interface{}, rootStruct string, format return sp, nil } -func (sp *StructParser) ToJSON() ([]byte, error) { - value := sp.MainStruct.ToKV() - return json.MarshalIndent(value, "", " ") -} - // ParseField parses struct field in nested way func (sp *StructParser) ParseField(field *ast.Field) *StructField { // ast.Print(nil, field) @@ -173,7 +161,7 @@ func (sp *StructParser) ParseField(field *ast.Field) *StructField { Comment: fieldComment, Doc: fieldDoc, NestedType: nestedStruct, - FormatFunc: sp.FieldFormatter.Format, + FormatFunc: sp.Formatter.FieldFormatFunc, } } @@ -188,7 +176,12 @@ func (sp *StructParser) ParseStruct(structName string, s *ast.StructType) *Struc } return &StructNode{ - Name: structName, - Fields: fields, + Name: structName, + Fields: fields, + FormatFunc: sp.Formatter.StructFormatFunc, } } + +func (sp *StructParser) Marshal() ([]byte, error) { + return sp.Formatter.Marshal(sp.MainStruct) +} diff --git a/parser/parser_test.go b/parser/parser_test.go index 9413765..02c852b 100644 --- a/parser/parser_test.go +++ b/parser/parser_test.go @@ -1,18 +1,44 @@ package beeParser import ( + "encoding/json" "fmt" "log" ) type sampleFormatter struct { + Annotation Annotation } -func (f *sampleFormatter) Format(field *StructField) string { - return "" +func (f *sampleFormatter) FieldFormatFunc(field *StructField) ([]byte, error) { + // @todo update annotationResult by parsing with annotation struct + annotationResult := field.Comment + field.Doc + return json.Marshal(&struct { + Key string + Annotation string + NestedType *StructNode `json:"NestedType,omitempty"` + }{ + Key: field.Name, + Annotation: annotationResult, + NestedType: field.NestedType, + }) } -func ExampleStructParser() { +func (f *sampleFormatter) StructFormatFunc(node *StructNode) ([]byte, error) { + return json.Marshal(&struct { + Key string + Fields []*StructField `json:"Fields,omitempty"` + }{ + Key: node.Name, + Fields: node.Fields, + }) +} + +func (f *sampleFormatter) Marshal(node *StructNode) ([]byte, error) { + return json.Marshal(node) +} + +func ExamplesampleFormatter() { const src = ` package p @@ -24,7 +50,11 @@ type StructB struct { Field1 string } type StructA struct { - Field1 string + // doc + Field1 string //comment + // @Name Field1 + // @Path https://github.com/beego/bee + // https://github.com/beego Field2 struct{ a string b string @@ -43,7 +73,7 @@ type StructA struct { log.Fatal(err) } - b, err := sp.ToJSON() + b, err := sp.Marshal() if err != nil { log.Fatal(err) } @@ -51,18 +81,5 @@ type StructA struct { fmt.Println(string(b)) // Output: - // { - // "Field1": "", - // "Field2": { - // "a": "", - // "b": "" - // }, - // "Field3": "", - // "Field4": "", - // "Field5": "", - // "Field6": "", - // "Field7": { - // "Field1": "" - // } - // } + // "{\"Key\":\"StructA\",\"Fields\":[\"{\\\"Key\\\":\\\"Field1\\\",\\\"Annotation\\\":\\\"comment\\\\ndoc\\\\n\\\"}\",\"{\\\"Key\\\":\\\"Field2\\\",\\\"Annotation\\\":\\\"@Name Field1\\\\n@Path https://github.com/beego/bee\\\\n\\\\t\\\\t https://github.com/beego\\\\n\\\",\\\"NestedType\\\":\\\"{\\\\\\\"Key\\\\\\\":\\\\\\\"\\\\\\\",\\\\\\\"Fields\\\\\\\":[\\\\\\\"{\\\\\\\\\\\\\\\"Key\\\\\\\\\\\\\\\":\\\\\\\\\\\\\\\"a\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"Annotation\\\\\\\\\\\\\\\":\\\\\\\\\\\\\\\"\\\\\\\\\\\\\\\"}\\\\\\\",\\\\\\\"{\\\\\\\\\\\\\\\"Key\\\\\\\\\\\\\\\":\\\\\\\\\\\\\\\"b\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"Annotation\\\\\\\\\\\\\\\":\\\\\\\\\\\\\\\"\\\\\\\\\\\\\\\"}\\\\\\\"]}\\\"}\",\"{\\\"Key\\\":\\\"Field3\\\",\\\"Annotation\\\":\\\"\\\"}\",\"{\\\"Key\\\":\\\"Field4\\\",\\\"Annotation\\\":\\\"\\\"}\",\"{\\\"Key\\\":\\\"Field5\\\",\\\"Annotation\\\":\\\"\\\"}\",\"{\\\"Key\\\":\\\"Field6\\\",\\\"Annotation\\\":\\\"\\\"}\",\"{\\\"Key\\\":\\\"Field7\\\",\\\"Annotation\\\":\\\"\\\",\\\"NestedType\\\":\\\"{\\\\\\\"Key\\\\\\\":\\\\\\\"StructB\\\\\\\",\\\\\\\"Fields\\\\\\\":[\\\\\\\"{\\\\\\\\\\\\\\\"Key\\\\\\\\\\\\\\\":\\\\\\\\\\\\\\\"Field1\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"Annotation\\\\\\\\\\\\\\\":\\\\\\\\\\\\\\\"\\\\\\\\\\\\\\\"}\\\\\\\"]}\\\"}\"]}" } diff --git a/parser/sample_formatter.go b/parser/sample_formatter.go new file mode 100644 index 0000000..a1288e7 --- /dev/null +++ b/parser/sample_formatter.go @@ -0,0 +1,41 @@ +package beeParser + +import "encoding/json" + +type AnnotationJSONFormatter struct { + Annotation Annotator +} + +func (f *AnnotationJSONFormatter) Format(field *StructField) string { + if field.Comment == "" && field.Doc == "" { + return "" + } + kvs := f.Annotation.Annotate(field.Doc + field.Comment) + res, _ := json.Marshal(kvs) + return string(res) +} + +func NewAnnotationJSONFormatter() *AnnotationJSONFormatter { + return &AnnotationJSONFormatter{Annotation: &Annotation{}} +} + +type AnnotationYAMLFormatter struct { + Annotation Annotator +} + +func (f *AnnotationYAMLFormatter) Format(field *StructField) string { + if field.Comment == "" && field.Doc == "" { + return "" + } + kvs := f.Annotation.Annotate(field.Doc + field.Comment) + res, _ := json.Marshal(kvs) + return string(res) +} + +func NewAnnotationYAMLFormatter() *AnnotationYAMLFormatter { + return &AnnotationYAMLFormatter{Annotation: &Annotation{}} +} + +type AnnotationTextFromatter struct { + Annotation Annotator +}