github.com/bingoohuang/pkger@v0.0.0-20210127185155-a71b9df4c4c7/parser/parser.go (about)

     1  package parser
     2  
     3  import (
     4  	"fmt"
     5  	"go/parser"
     6  	"go/token"
     7  	"io"
     8  	"io/ioutil"
     9  	"os"
    10  	"path/filepath"
    11  	"strings"
    12  	"sync"
    13  
    14  	"github.com/bingoohuang/pkger/here"
    15  )
    16  
    17  var defaultIgnoredFolders = []string{".", "_", "vendor", "node_modules", "testdata"}
    18  
    19  func New(her here.Info) (*Parser, error) {
    20  	return &Parser{
    21  		Info:  her,
    22  		decls: map[string]Decls{},
    23  	}, nil
    24  }
    25  
    26  type Parser struct {
    27  	here.Info
    28  	decls    map[string]Decls
    29  	once     sync.Once
    30  	includes []string
    31  	err      error
    32  }
    33  
    34  func Parse(her here.Info, includes ...string) (Decls, error) {
    35  	p, err := New(her)
    36  	if err != nil {
    37  		return nil, err
    38  	}
    39  	p.includes = includes
    40  
    41  	return p.Decls()
    42  }
    43  
    44  func (p *Parser) ParseSource(source Source, mode parser.Mode) (*ParsedSource, error) {
    45  	pf := &ParsedSource{
    46  		Source:  source,
    47  		FileSet: token.NewFileSet(),
    48  	}
    49  
    50  	b, err := ioutil.ReadFile(source.Abs)
    51  	if err != nil {
    52  		return nil, err
    53  	}
    54  	src := string(b)
    55  
    56  	pff, err := parser.ParseFile(pf.FileSet, source.Abs, src, mode)
    57  	if err != nil && err != io.EOF {
    58  		return nil, err
    59  	}
    60  	pf.Ast = pff
    61  
    62  	return pf, nil
    63  }
    64  
    65  func (p *Parser) ParseFile(abs string, mode parser.Mode) (*ParsedSource, error) {
    66  	s := Source{
    67  		Abs: abs,
    68  	}
    69  
    70  	info, err := os.Stat(abs)
    71  	if err != nil {
    72  		return nil, err
    73  	}
    74  
    75  	if info.IsDir() {
    76  		return nil, fmt.Errorf("%s is a directory", abs)
    77  	}
    78  
    79  	dir := filepath.Dir(abs)
    80  
    81  	s.Here, err = here.Dir(dir)
    82  	if err != nil {
    83  		return nil, err
    84  	}
    85  
    86  	s.Path, err = s.Here.Parse(strings.TrimPrefix(abs, dir))
    87  
    88  	return p.ParseSource(s, 0)
    89  }
    90  
    91  func (p *Parser) ParseDir(abs string, mode parser.Mode) ([]*ParsedSource, error) {
    92  	info, err := os.Stat(abs)
    93  	if err != nil {
    94  		return nil, err
    95  	}
    96  
    97  	if !info.IsDir() {
    98  		return nil, fmt.Errorf("%s is not a directory", abs)
    99  	}
   100  
   101  	her, err := here.Dir(abs)
   102  	if err != nil {
   103  		return nil, fmt.Errorf("%s: here.Dir failed %s", err, abs)
   104  	}
   105  
   106  	pt, err := her.Parse(strings.TrimPrefix(abs, filepath.Dir(abs)))
   107  	if err != nil {
   108  		return nil, fmt.Errorf("%s: here.Parse failed %s", err, abs)
   109  	}
   110  
   111  	filter := func(f os.FileInfo) bool {
   112  		return !f.IsDir()
   113  	}
   114  
   115  	fset := token.NewFileSet()
   116  	pkgs, err := parser.ParseDir(fset, abs, filter, 0)
   117  	if err != nil {
   118  		return nil, fmt.Errorf("%s: ParseDir failed %s", err, abs)
   119  	}
   120  
   121  	var srcs []*ParsedSource
   122  	for _, pkg := range pkgs {
   123  		for name, pf := range pkg.Files {
   124  			s := &ParsedSource{
   125  				Source: Source{
   126  					Abs:  name,
   127  					Path: pt,
   128  					Here: her,
   129  				},
   130  				FileSet: fset,
   131  				Ast:     pf,
   132  			}
   133  			srcs = append(srcs, s)
   134  		}
   135  	}
   136  
   137  	return srcs, nil
   138  }
   139  
   140  func (p *Parser) Decls() (Decls, error) {
   141  	if err := p.parse(); err != nil {
   142  		return nil, err
   143  	}
   144  
   145  	var decls Decls
   146  	orderedNames := []string{
   147  		"MkdirAll",
   148  		"Create",
   149  		"Include",
   150  		"Stat",
   151  		"Open",
   152  		"Dir",
   153  		"Walk",
   154  		"Read",
   155  		"ReadStr",
   156  		"MustRead",
   157  		"MustReadStr",
   158  	}
   159  
   160  	for _, n := range orderedNames {
   161  		decls = append(decls, p.decls[n]...)
   162  	}
   163  
   164  	return decls, nil
   165  }
   166  
   167  func (p *Parser) DeclsMap() (map[string]Decls, error) {
   168  	err := p.Parse()
   169  	return p.decls, err
   170  }
   171  
   172  func (p *Parser) Parse() error {
   173  	(&p.once).Do(func() {
   174  		p.err = p.parse()
   175  	})
   176  	return p.err
   177  }
   178  
   179  func (p *Parser) parse() error {
   180  	p.decls = map[string]Decls{}
   181  
   182  	root := p.Dir
   183  
   184  	if err := p.parseIncludes(); err != nil {
   185  		return err
   186  	}
   187  
   188  	fi, err := os.Stat(root)
   189  	if err != nil {
   190  		return err
   191  	}
   192  
   193  	if !fi.IsDir() {
   194  		return fmt.Errorf("%q is not a directory", root)
   195  	}
   196  
   197  	err = filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
   198  		if err != nil {
   199  			return err
   200  		}
   201  
   202  		if !info.IsDir() {
   203  			return nil
   204  		}
   205  
   206  		base := filepath.Base(path)
   207  		for _, x := range defaultIgnoredFolders {
   208  			if strings.HasPrefix(base, x) {
   209  				return filepath.SkipDir
   210  			}
   211  		}
   212  
   213  		srcs, err := p.ParseDir(path, 0)
   214  		if err != nil {
   215  			return fmt.Errorf("%s: %s", err, path)
   216  		}
   217  
   218  		for _, src := range srcs {
   219  			mm, err := src.DeclsMap()
   220  			if err != nil {
   221  				return fmt.Errorf("%s: %s", err, src.Abs)
   222  			}
   223  			for k, v := range mm {
   224  				p.decls[k] = append(p.decls[k], v...)
   225  			}
   226  		}
   227  
   228  		return nil
   229  	})
   230  
   231  	return err
   232  }
   233  
   234  func (p *Parser) parseIncludes() error {
   235  	for _, i := range p.includes {
   236  		ds, err := NewInclude(p.Info, i)
   237  		if err != nil {
   238  			return err
   239  		}
   240  
   241  		for _, d := range ds {
   242  			k := strings.TrimPrefix(d.typ, "pkger.")
   243  			p.decls[k] = append(p.decls[k], d)
   244  		}
   245  	}
   246  	return nil
   247  }