diff --git a/go.mod b/go.mod
index e4418a5..f117de2 100644
--- a/go.mod
+++ b/go.mod
@@ -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
diff --git a/go.sum b/go.sum
index ce91369..4b88faa 100644
--- a/go.sum
+++ b/go.sum
@@ -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=
diff --git a/parser/annotator.go b/parser/annotator.go
index 022c3d6..f2fcc9d 100644
--- a/parser/annotator.go
+++ b/parser/annotator.go
@@ -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
+}
diff --git a/parser/annotator_test.go b/parser/annotator_test.go
index d3d9ea5..17862bd 100644
--- a/parser/annotator_test.go
+++ b/parser/annotator_test.go
@@ -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)
diff --git a/parser/formatter.go b/parser/formatter.go
index f50926d..fcfb621 100644
--- a/parser/formatter.go
+++ b/parser/formatter.go
@@ -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)
}
diff --git a/parser/formatter_test.go b/parser/formatter_test.go
index 11b6312..6bdea9b 100644
--- a/parser/formatter_test.go
+++ b/parser/formatter_test.go
@@ -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:
+ //test
+ //
+ //https://github.com/beego/bee https://github.com/beego
+ //
+ //1
+ //false
}
diff --git a/parser/parser.go b/parser/parser.go
index 8559e6b..055ea83 100644
--- a/parser/parser.go
+++ b/parser/parser.go
@@ -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
diff --git a/parser/sample_formatter.go b/parser/sample_formatter.go
deleted file mode 100644
index a1288e7..0000000
--- a/parser/sample_formatter.go
+++ /dev/null
@@ -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
-}