...
1 package parser
2
3 import (
4 "go/ast"
5 "go/parser"
6 "go/token"
7 "os"
8 "strings"
9 )
10
11 const (
12 structComment = "easyjson:json"
13 structSkipComment = "easyjson:skip"
14 )
15
16 type Parser struct {
17 PkgPath string
18 PkgName string
19 StructNames []string
20 AllStructs bool
21 }
22
23 type visitor struct {
24 *Parser
25
26 name string
27 }
28
29 func (p *Parser) needType(comments *ast.CommentGroup) (skip, explicit bool) {
30 if comments == nil {
31 return
32 }
33
34 for _, v := range comments.List {
35 comment := v.Text
36
37 if len(comment) > 2 {
38 switch comment[1] {
39 case '/':
40
41 comment = comment[2:]
42 case '*':
43
44 comment = comment[2 : len(comment)-2]
45 }
46 }
47
48 for _, comment := range strings.Split(comment, "\n") {
49 comment = strings.TrimSpace(comment)
50
51 if strings.HasPrefix(comment, structSkipComment) {
52 return true, false
53 }
54 if strings.HasPrefix(comment, structComment) {
55 return false, true
56 }
57 }
58 }
59
60 return
61 }
62
63 func (v *visitor) Visit(n ast.Node) (w ast.Visitor) {
64 switch n := n.(type) {
65 case *ast.Package:
66 return v
67 case *ast.File:
68 v.PkgName = n.Name.String()
69 return v
70
71 case *ast.GenDecl:
72 skip, explicit := v.needType(n.Doc)
73
74 if skip || explicit {
75 for _, nc := range n.Specs {
76 switch nct := nc.(type) {
77 case *ast.TypeSpec:
78 nct.Doc = n.Doc
79 }
80 }
81 }
82
83 return v
84 case *ast.TypeSpec:
85 skip, explicit := v.needType(n.Doc)
86 if skip {
87 return nil
88 }
89 if !explicit && !v.AllStructs {
90 return nil
91 }
92
93 v.name = n.Name.String()
94
95
96 if explicit {
97 v.StructNames = append(v.StructNames, v.name)
98 return nil
99 }
100
101 return v
102 case *ast.StructType:
103 v.StructNames = append(v.StructNames, v.name)
104 return nil
105 }
106 return nil
107 }
108
109 func (p *Parser) Parse(fname string, isDir bool) error {
110 var err error
111 if p.PkgPath, err = getPkgPath(fname, isDir); err != nil {
112 return err
113 }
114
115 fset := token.NewFileSet()
116 if isDir {
117 packages, err := parser.ParseDir(fset, fname, excludeTestFiles, parser.ParseComments)
118 if err != nil {
119 return err
120 }
121
122 for _, pckg := range packages {
123 ast.Walk(&visitor{Parser: p}, pckg)
124 }
125 } else {
126 f, err := parser.ParseFile(fset, fname, nil, parser.ParseComments)
127 if err != nil {
128 return err
129 }
130
131 ast.Walk(&visitor{Parser: p}, f)
132 }
133 return nil
134 }
135
136 func excludeTestFiles(fi os.FileInfo) bool {
137 return !strings.HasSuffix(fi.Name(), "_test.go")
138 }
139
View as plain text