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