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") }