fix annotator and test formatter

This commit is contained in:
LinXiaoYi 2021-06-25 23:15:46 +08:00
parent a829cb9846
commit 2422bf63e6
8 changed files with 272 additions and 85 deletions

2
go.mod
View File

@ -7,8 +7,10 @@ require (
github.com/davecgh/go-spew v1.1.1
github.com/flosch/pongo2 v0.0.0-20200529170236-5abacdfa4915
github.com/fsnotify/fsnotify v1.4.9
github.com/ghodss/yaml v1.0.0
github.com/go-delve/delve v1.5.0
github.com/go-sql-driver/mysql v1.5.0
github.com/go-yaml/yaml v2.1.0+incompatible
github.com/gorilla/websocket v1.4.2
github.com/lib/pq v1.7.0
github.com/pelletier/go-toml v1.8.1

3
go.sum
View File

@ -78,6 +78,7 @@ github.com/flosch/pongo2 v0.0.0-20200529170236-5abacdfa4915/go.mod h1:fB4mx6dzqF
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/glendc/gopher-json v0.0.0-20170414221815-dc4743023d0c/go.mod h1:Gja1A+xZ9BoviGJNA2E9vFkPjjsl+CoJxSXiQM1UXtw=
github.com/go-check/check v0.0.0-20180628173108-788fd7840127 h1:0gkP6mzaMqkmpcJYCFOLkIBwI7xFExG03bbkOkCvUPI=
@ -94,6 +95,8 @@ github.com/go-redis/redis/v7 v7.4.0/go.mod h1:JDNMw23GTyLNC4GZu9njt15ctBQVn7xjRf
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-yaml/yaml v2.1.0+incompatible h1:RYi2hDdss1u4YE7GwixGzWwVo47T8UQwnTLB6vQiq+o=
github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=

View File

@ -1,6 +1,8 @@
package beeParser
import (
"fmt"
"strconv"
"strings"
)
@ -9,6 +11,8 @@ type Annotator interface {
}
type Annotation struct {
Key, Description string
Default interface{}
}
func isWhitespace(ch byte) bool { return ch == ' ' || ch == '\t' || ch == '\r' }
@ -30,16 +34,27 @@ func handleTailWhitespace(s string) string {
}
//handle value to remove head and tail space.
func handleWhitespaceValues(values []string) []string {
res := make([]string, 0)
func handleWhitespaceValues(values []string) []interface{} {
res := make([]interface{}, 0)
for _, v := range values {
v = handleHeadWhitespace(v)
v = handleTailWhitespace(v)
res = append(res, v)
res = append(res, transferType(v))
}
return res
}
//try to transfer string to original type
func transferType(str string) interface{} {
if res, err := strconv.Atoi(str); err == nil {
return res
}
if res, err := strconv.ParseBool(str); err == nil {
return res
}
return str
}
//parse annotation to generate array with key and values
//start with "@" as a key-value pair,key and values are separated by a space,wrap to distinguish values.
func (a *Annotation) Annotate(annotation string) map[string]interface{} {
@ -51,7 +66,26 @@ func (a *Annotation) Annotate(annotation string) map[string]interface{} {
kvs := strings.Split(line, " ")
key := kvs[0]
values := strings.Split(strings.TrimSpace(line[len(kvs[0]):]), "\n")
results[key] = handleWhitespaceValues(values)
if len(values) == 1 {
results[key] = handleWhitespaceValues(values)[0]
} else {
results[key] = handleWhitespaceValues(values)
}
}
return results
}
func NewAnnotation(annotation string) *Annotation {
a := &Annotation{}
kvs := a.Annotate(annotation)
if v, ok := kvs["Key"]; ok {
a.Key = fmt.Sprintf("%v", v)
}
if v, ok := kvs["Description"]; ok {
a.Description = fmt.Sprintf("%v", v)
}
if v, ok := kvs["Default"]; ok {
a.Default = v
}
return a
}

View File

@ -32,14 +32,14 @@ func TestMain(m *testing.M) {
func TestAnnotate(t *testing.T) {
expect1 := map[string]interface{}{
"Name": []string{"Field1"},
"Type": []string{"string"},
"Path": []string{"https://github.com/beego/bee", "https://github.com/beego"},
"Name": "Field1",
"Type": "string",
"Path": []interface{}{"https://github.com/beego/bee", "https://github.com/beego"},
}
expect2 := map[string]interface{}{
"Number": []string{"2"},
"Projects": []string{"https://github.com/beego/bee", "", "https://github.com/beego"},
"Number": 2,
"Projects": []interface{}{"https://github.com/beego/bee", "", "https://github.com/beego"},
}
actual := BeeAnnotator.Annotate(Annotation1)
@ -55,13 +55,17 @@ func TestHandleWhitespaceValues(t *testing.T) {
"",
" bee ",
" bee beego ",
" 1 ",
" false ",
}
expect := []string{
expect := []interface{}{
"beego",
"",
"bee",
"bee beego",
1,
false,
}
actual := handleWhitespaceValues(src)

View File

@ -1,20 +1,97 @@
package beeParser
import "encoding/json"
import (
"encoding/json"
"encoding/xml"
type AnnotationFormatter struct {
Annotation Annotator
"gopkg.in/yaml.v2"
)
type JsonFormatter struct {
}
func (f *AnnotationFormatter) Format(field *StructField) string {
if field.Comment == "" && field.Doc == "" {
return ""
func (f *JsonFormatter) FieldFormatFunc(field *StructField) ([]byte, error) {
annotation := NewAnnotation(field.Doc + field.Comment)
res := map[string]interface{}{}
if field.NestedType != nil {
res[annotation.Key] = field.NestedType
} else {
res[annotation.Key] = annotation.Default
}
kvs := f.Annotation.Annotate(field.Doc + field.Comment)
res, _ := json.Marshal(kvs)
return string(res)
return json.Marshal(res)
}
func NewAnnotationFormatter() *AnnotationFormatter {
return &AnnotationFormatter{Annotation: &Annotation{}}
func (f *JsonFormatter) StructFormatFunc(node *StructNode) ([]byte, error) {
return json.Marshal(node.Fields)
}
func (f *JsonFormatter) Marshal(node *StructNode) ([]byte, error) {
return json.MarshalIndent(node, "", " ")
}
type YamlFormatter struct {
}
func (f *YamlFormatter) FieldFormatFunc(field *StructField) ([]byte, error) {
annotation := NewAnnotation(field.Doc + field.Comment)
res := map[string]interface{}{}
if field.NestedType != nil {
res[annotation.Key] = field.NestedType
} else {
res[annotation.Key] = annotation.Default
}
return yaml.Marshal(res)
}
func (f *YamlFormatter) StructFormatFunc(node *StructNode) ([]byte, error) {
return yaml.Marshal(node.Fields)
}
func (f *YamlFormatter) Marshal(node *StructNode) ([]byte, error) {
return yaml.Marshal(node)
}
type XmlFormatter struct {
}
func (f *XmlFormatter) FieldFormatFunc(field *StructField) ([]byte, error) {
annotation := NewAnnotation(field.Doc + field.Comment)
if field.NestedType != nil {
type xmlStruct struct {
XMLName xml.Name
Default interface{} `xml:",innerxml"`
Description string `xml:",comment"`
}
b, _ := field.NestedType.FormatFunc(field.NestedType)
return xml.Marshal(&xmlStruct{
XMLName: xml.Name{Local: annotation.Key},
Description: annotation.Description,
Default: b,
})
} else {
type xmlStruct struct {
XMLName xml.Name
Default interface{} `xml:",chardata"`
Description string `xml:",comment"`
}
return xml.Marshal(&xmlStruct{
XMLName: xml.Name{Local: annotation.Key},
Description: annotation.Description,
Default: annotation.Default,
})
}
}
func (f *XmlFormatter) StructFormatFunc(node *StructNode) ([]byte, error) {
res := make([]byte, 0)
for _, f := range node.Fields {
b, _ := f.FormatFunc(f)
res = append(res, b...)
res = append(res, '\n')
}
return res, nil
}
func (f *XmlFormatter) Marshal(node *StructNode) ([]byte, error) {
return node.FormatFunc(node)
}

View File

@ -1,33 +1,126 @@
package beeParser
import (
"testing"
"github.com/stretchr/testify/assert"
"fmt"
"log"
)
func TestFormat(t *testing.T) {
except := `{
"Name": [
"Field1"
],
"Path":[
"https://github.com/beego/bee",
"https://github.com/beego"
],
"test":[
"test comment"
]
}`
const src = `
package p
field := &StructField{
Comment: "@test test comment",
Doc: `@Name Field1
@Path https://github.com/beego/bee
https://github.com/beego`,
type StructA struct {
// @Key Field1
// @Default test
// @Description ddddddd
Field1 string
// @Key Field2
Field2 struct{
// @Key a
// @Default https://github.com/beego/bee
// https://github.com/beego
a string
// @Key b
// @Default https://github.com/beego/bee https://github.com/beego
b string
}
// @Key Field3
// @Default 1
Field3 int
// @Key Field4
// @Default false
Field4 bool
}
`
func ExampleJsonFormatter() {
sp, err := NewStructParser("src.go", src, "StructA", &JsonFormatter{})
if err != nil {
log.Fatal(err)
}
actual := NewAnnotationFormatter().Format(field)
b, err := sp.Marshal()
if err != nil {
log.Fatal(err)
}
assert.JSONEq(t, except, actual)
fmt.Println(string(b))
// Output:
//[
// {
// "Field1": "test"
// },
// {
// "Field2": [
// {
// "a": [
// "https://github.com/beego/bee",
// "https://github.com/beego"
// ]
// },
// {
// "b": "https://github.com/beego/bee https://github.com/beego"
// }
// ]
// },
// {
// "Field3": 1
// },
// {
// "Field4": false
// }
//]
}
func ExampleYamlFormatter() {
sp, err := NewStructParser("src.go", src, "StructA", &YamlFormatter{})
if err != nil {
log.Fatal(err)
}
b, err := sp.Marshal()
if err != nil {
log.Fatal(err)
}
fmt.Println(string(b))
// Output:
//|
// - |
// Field1: test
// - |
// Field2: |
// - |
// a:
// - https://github.com/beego/bee
// - https://github.com/beego
// - |
// b: https://github.com/beego/bee https://github.com/beego
// - |
// Field3: 1
// - |
// Field4: false
}
func ExampleXmlFormatter() {
sp, err := NewStructParser("src.go", src, "StructA", &XmlFormatter{})
if err != nil {
log.Fatal(err)
}
b, err := sp.Marshal()
if err != nil {
log.Fatal(err)
}
fmt.Println(string(b))
// Output:
//<Field1>test<!--ddddddd--></Field1>
//<Field2><a></a>
//<b>https://github.com/beego/bee https://github.com/beego</b>
//</Field2>
//<Field3>1</Field3>
//<Field4>false</Field4>
}

View File

@ -34,6 +34,13 @@ func (sf *StructField) MarshalText() ([]byte, error) {
return sf.FormatFunc(sf)
}
func (sf *StructField) MarshalJSON() ([]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
@ -49,6 +56,14 @@ func (sn *StructNode) MarshalText() ([]byte, error) {
return sn.FormatFunc(sn)
}
func (sn *StructNode) MarshalJSON() ([]byte, error) {
if sn.FormatFunc == nil {
return nil, errors.New("format func is missing")
}
return sn.FormatFunc(sn)
}
// StructParser parses structs in given file or string
type StructParser struct {
MainStruct *StructNode

View File

@ -1,41 +0,0 @@
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
}