mirror of https://github.com/markbates/pkger.git
Merge pull request #17 from markbates/declan-macmanus
skip ignoreable directories
This commit is contained in:
commit
08fb31c710
|
@ -13,6 +13,7 @@ steps:
|
||||||
mv !(gopath) "$(modulePath)"
|
mv !(gopath) "$(modulePath)"
|
||||||
displayName: "Setup Go Workspace"
|
displayName: "Setup Go Workspace"
|
||||||
- script: |
|
- script: |
|
||||||
|
go get github.com/gobuffalo/buffalo
|
||||||
go get -t -v ./...
|
go get -t -v ./...
|
||||||
go test -race ./...
|
go test -race ./...
|
||||||
workingDirectory: "$(modulePath)"
|
workingDirectory: "$(modulePath)"
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package parser
|
package parser
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"go/token"
|
"go/token"
|
||||||
"sort"
|
"sort"
|
||||||
)
|
)
|
||||||
|
@ -39,11 +40,12 @@ func (decls Decls) Files() ([]*File, error) {
|
||||||
|
|
||||||
files, err := fl.Files(v)
|
files, err := fl.Files(v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, fmt.Errorf("%w: %s", err, d)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, f := range files {
|
for _, f := range files {
|
||||||
m[f.Abs] = f
|
m[f.Abs] = f
|
||||||
|
v[f.Abs] = f.Abs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,11 +2,6 @@ package parser
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"go/ast"
|
|
||||||
"go/parser"
|
|
||||||
"go/token"
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
|
|
||||||
"github.com/markbates/pkger/here"
|
"github.com/markbates/pkger/here"
|
||||||
)
|
)
|
||||||
|
@ -21,36 +16,3 @@ func (f File) String() string {
|
||||||
b, _ := json.MarshalIndent(f, "", " ")
|
b, _ := json.MarshalIndent(f, "", " ")
|
||||||
return string(b)
|
return string(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
type parsedFile struct {
|
|
||||||
File string
|
|
||||||
FileSet *token.FileSet
|
|
||||||
Ast *ast.File
|
|
||||||
}
|
|
||||||
|
|
||||||
// parseFileMode ...
|
|
||||||
func parseFileMode(f string, mode parser.Mode) (parsedFile, error) {
|
|
||||||
pf := parsedFile{
|
|
||||||
File: f,
|
|
||||||
FileSet: token.NewFileSet(),
|
|
||||||
}
|
|
||||||
|
|
||||||
b, err := ioutil.ReadFile(f)
|
|
||||||
if err != nil {
|
|
||||||
return pf, err
|
|
||||||
}
|
|
||||||
src := string(b)
|
|
||||||
|
|
||||||
pff, err := parser.ParseFile(pf.FileSet, f, src, mode)
|
|
||||||
if err != nil && err != io.EOF {
|
|
||||||
return pf, err
|
|
||||||
}
|
|
||||||
pf.Ast = pff
|
|
||||||
|
|
||||||
return pf, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// parseFile ...
|
|
||||||
func parseFile(f string) (parsedFile, error) {
|
|
||||||
return parseFileMode(f, 0)
|
|
||||||
}
|
|
||||||
|
|
|
@ -71,11 +71,7 @@ func (d OpenDecl) Files(virtual map[string]string) ([]*File, error) {
|
||||||
return wd.Files(virtual)
|
return wd.Files(virtual)
|
||||||
}
|
}
|
||||||
var files []*File
|
var files []*File
|
||||||
files = append(files, &File{
|
files = append(files, d.file)
|
||||||
Abs: filepath.Join(her.Module.Dir, pt.Name),
|
|
||||||
Path: pt,
|
|
||||||
Here: her,
|
|
||||||
})
|
|
||||||
|
|
||||||
return files, nil
|
return files, nil
|
||||||
}
|
}
|
||||||
|
|
192
parser/parser.go
192
parser/parser.go
|
@ -4,68 +4,212 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"go/parser"
|
"go/parser"
|
||||||
"go/token"
|
"go/token"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/markbates/pkger/here"
|
"github.com/markbates/pkger/here"
|
||||||
)
|
)
|
||||||
|
|
||||||
// var DefaultIgnoredFolders = []string{".", "_", "vendor", "node_modules", "_fixtures", "testdata"}
|
var defaultIgnoredFolders = []string{".", "_", "vendor", "node_modules", "testdata"}
|
||||||
|
|
||||||
func Parse(her here.Info) (Decls, error) {
|
func Parse(her here.Info) (Decls, error) {
|
||||||
src, err := fromSource(her)
|
p, err := New(her)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return p.Decls()
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseSource(source Source, mode parser.Mode) (*ParsedSource, error) {
|
||||||
|
pf := &ParsedSource{
|
||||||
|
Source: source,
|
||||||
|
FileSet: token.NewFileSet(),
|
||||||
|
}
|
||||||
|
|
||||||
|
b, err := ioutil.ReadFile(source.Abs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
src := string(b)
|
||||||
|
|
||||||
|
pff, err := parser.ParseFile(pf.FileSet, source.Abs, src, mode)
|
||||||
|
if err != nil && err != io.EOF {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
pf.Ast = pff
|
||||||
|
|
||||||
|
return pf, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseFile(abs string, mode parser.Mode) (*ParsedSource, error) {
|
||||||
|
s := Source{
|
||||||
|
Abs: abs,
|
||||||
|
}
|
||||||
|
|
||||||
|
info, err := os.Stat(abs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return src, nil
|
if info.IsDir() {
|
||||||
|
return nil, fmt.Errorf("%s is a directory", abs)
|
||||||
}
|
}
|
||||||
|
|
||||||
func fromSource(her here.Info) (Decls, error) {
|
dir := filepath.Dir(abs)
|
||||||
root := her.Dir
|
|
||||||
fi, err := os.Stat(root)
|
s.Here, err = here.Dir(dir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if !fi.IsDir() {
|
|
||||||
return nil, fmt.Errorf("%q is not a directory", root)
|
s.Path, err = s.Here.Parse(strings.TrimPrefix(abs, dir))
|
||||||
|
|
||||||
|
return ParseSource(s, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseDir(abs string, mode parser.Mode) ([]*ParsedSource, error) {
|
||||||
|
info, err := os.Stat(abs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !info.IsDir() {
|
||||||
|
return nil, fmt.Errorf("%s is not a directory", abs)
|
||||||
|
}
|
||||||
|
dir := filepath.Dir(abs)
|
||||||
|
|
||||||
|
her, err := here.Dir(dir)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
pt, err := her.Parse(strings.TrimPrefix(abs, dir))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
fset := token.NewFileSet()
|
||||||
|
|
||||||
|
pkgs, err := parser.ParseDir(fset, abs, nil, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var srcs []*ParsedSource
|
||||||
|
for _, pkg := range pkgs {
|
||||||
|
for name, pf := range pkg.Files {
|
||||||
|
s := &ParsedSource{
|
||||||
|
Source: Source{
|
||||||
|
Abs: name,
|
||||||
|
Path: pt,
|
||||||
|
Here: her,
|
||||||
|
},
|
||||||
|
FileSet: fset,
|
||||||
|
Ast: pf,
|
||||||
|
}
|
||||||
|
srcs = append(srcs, s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return srcs, nil
|
||||||
|
}
|
||||||
|
func New(her here.Info) (*Parser, error) {
|
||||||
|
return &Parser{
|
||||||
|
Info: her,
|
||||||
|
decls: map[string]Decls{},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type Parser struct {
|
||||||
|
here.Info
|
||||||
|
decls map[string]Decls
|
||||||
|
once sync.Once
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Parser) Decls() (Decls, error) {
|
||||||
|
if err := p.parse(); err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var decls Decls
|
var decls Decls
|
||||||
|
orderedNames := []string{
|
||||||
|
"MkdirAll",
|
||||||
|
"Create",
|
||||||
|
"Stat",
|
||||||
|
"Open",
|
||||||
|
"Dir",
|
||||||
|
"Walk",
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, n := range orderedNames {
|
||||||
|
decls = append(decls, p.decls[n]...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return decls, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Parser) DeclsMap() (map[string]Decls, error) {
|
||||||
|
err := p.Parse()
|
||||||
|
return p.decls, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Parser) Parse() error {
|
||||||
|
(&p.once).Do(func() {
|
||||||
|
p.err = p.parse()
|
||||||
|
})
|
||||||
|
return p.err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Parser) parse() error {
|
||||||
|
p.decls = map[string]Decls{}
|
||||||
|
root := p.Dir
|
||||||
|
|
||||||
|
fi, err := os.Stat(root)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !fi.IsDir() {
|
||||||
|
return fmt.Errorf("%q is not a directory", root)
|
||||||
|
}
|
||||||
|
|
||||||
err = filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
|
err = filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
fset := token.NewFileSet()
|
|
||||||
|
|
||||||
if !info.IsDir() {
|
if !info.IsDir() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
pkgs, err := parser.ParseDir(fset, path, nil, 0)
|
|
||||||
if err != nil {
|
base := filepath.Base(path)
|
||||||
return err
|
for _, x := range defaultIgnoredFolders {
|
||||||
|
if strings.HasPrefix(base, x) {
|
||||||
|
return filepath.SkipDir
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, pkg := range pkgs {
|
srcs, err := ParseDir(path, 0)
|
||||||
for name, pf := range pkg.Files {
|
if err != nil {
|
||||||
f := &file{
|
return fmt.Errorf("%w: %s", err, path)
|
||||||
fset: fset,
|
|
||||||
astFile: pf,
|
|
||||||
filename: name,
|
|
||||||
current: her,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
x, err := f.find()
|
for _, src := range srcs {
|
||||||
|
mm, err := src.DeclsMap()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("%w: %s", err, src.Abs)
|
||||||
}
|
}
|
||||||
decls = append(decls, x...)
|
for k, v := range mm {
|
||||||
|
p.decls[k] = append(p.decls[k], v...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
return decls, err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,19 +1,23 @@
|
||||||
package parser_test
|
package parser
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/markbates/pkger/here"
|
"github.com/markbates/pkger/here"
|
||||||
"github.com/markbates/pkger/parser"
|
|
||||||
"github.com/markbates/pkger/pkging/pkgtest"
|
"github.com/markbates/pkger/pkging/pkgtest"
|
||||||
"github.com/markbates/pkger/pkging/stdos"
|
"github.com/markbates/pkger/pkging/stdos"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_Parser_Ref(t *testing.T) {
|
func Test_Parser_Ref(t *testing.T) {
|
||||||
|
defer func() {
|
||||||
|
c := exec.Command("go", "mod", "tidy", "-v")
|
||||||
|
c.Run()
|
||||||
|
}()
|
||||||
r := require.New(t)
|
r := require.New(t)
|
||||||
|
|
||||||
ref, err := pkgtest.NewRef()
|
ref, err := pkgtest.NewRef()
|
||||||
|
@ -26,16 +30,20 @@ func Test_Parser_Ref(t *testing.T) {
|
||||||
_, err = pkgtest.LoadFiles("/", ref, disk)
|
_, err = pkgtest.LoadFiles("/", ref, disk)
|
||||||
r.NoError(err)
|
r.NoError(err)
|
||||||
|
|
||||||
res, err := parser.Parse(ref.Info)
|
res, err := Parse(ref.Info)
|
||||||
|
|
||||||
r.NoError(err)
|
r.NoError(err)
|
||||||
|
|
||||||
files, err := res.Files()
|
files, err := res.Files()
|
||||||
r.NoError(err)
|
r.NoError(err)
|
||||||
r.Len(files, 22)
|
r.Len(files, 23)
|
||||||
|
|
||||||
for _, f := range files {
|
for _, f := range files {
|
||||||
|
if f.Path.Pkg == ref.Module.Path {
|
||||||
r.True(strings.HasPrefix(f.Abs, ref.Dir), "%q %q", f.Abs, ref.Dir)
|
r.True(strings.HasPrefix(f.Abs, ref.Dir), "%q %q", f.Abs, ref.Dir)
|
||||||
|
} else {
|
||||||
|
r.False(strings.HasPrefix(f.Abs, ref.Dir), "%q %q", f.Abs, ref.Dir)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,11 +59,15 @@ func Test_Parser_Example_HTTP(t *testing.T) {
|
||||||
|
|
||||||
root := filepath.Join(cur.Dir, "examples", "http", "pkger")
|
root := filepath.Join(cur.Dir, "examples", "http", "pkger")
|
||||||
r.NoError(os.Chdir(root))
|
r.NoError(os.Chdir(root))
|
||||||
|
defer func() {
|
||||||
|
c := exec.Command("go", "mod", "tidy", "-v")
|
||||||
|
c.Run()
|
||||||
|
}()
|
||||||
|
|
||||||
her, err := here.Dir(".")
|
her, err := here.Dir(".")
|
||||||
r.NoError(err)
|
r.NoError(err)
|
||||||
|
|
||||||
res, err := parser.Parse(her)
|
res, err := Parse(her)
|
||||||
r.NoError(err)
|
r.NoError(err)
|
||||||
|
|
||||||
files, err := res.Files()
|
files, err := res.Files()
|
||||||
|
|
|
@ -0,0 +1,185 @@
|
||||||
|
package parser
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"go/ast"
|
||||||
|
"go/token"
|
||||||
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/markbates/pkger/here"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Source struct {
|
||||||
|
Abs string // full path on disk to file
|
||||||
|
Path here.Path
|
||||||
|
Here here.Info
|
||||||
|
}
|
||||||
|
|
||||||
|
type ParsedSource struct {
|
||||||
|
Source
|
||||||
|
FileSet *token.FileSet
|
||||||
|
Ast *ast.File
|
||||||
|
decls map[string]Decls
|
||||||
|
once sync.Once
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ParsedSource) Parse() error {
|
||||||
|
(&p.once).Do(func() {
|
||||||
|
p.err = p.parse()
|
||||||
|
})
|
||||||
|
return p.err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ParsedSource) value(node ast.Node) (string, error) {
|
||||||
|
var s string
|
||||||
|
|
||||||
|
switch x := node.(type) {
|
||||||
|
case *ast.BasicLit:
|
||||||
|
s = x.Value
|
||||||
|
case *ast.Ident:
|
||||||
|
s = x.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
return strconv.Unquote(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ParsedSource) parse() error {
|
||||||
|
p.decls = map[string]Decls{}
|
||||||
|
var fn walker = func(node ast.Node) bool {
|
||||||
|
ce, ok := node.(*ast.CallExpr)
|
||||||
|
if !ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
sel, ok := ce.Fun.(*ast.SelectorExpr)
|
||||||
|
if !ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
pkg, ok := sel.X.(*ast.Ident)
|
||||||
|
if !ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if pkg.Name != "pkger" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
var fn func(f File, pos token.Position, value string) Decl
|
||||||
|
|
||||||
|
name := sel.Sel.Name
|
||||||
|
switch name {
|
||||||
|
case "MkdirAll":
|
||||||
|
fn = func(f File, pos token.Position, value string) Decl {
|
||||||
|
return MkdirAllDecl{
|
||||||
|
file: &f,
|
||||||
|
pos: pos,
|
||||||
|
value: value,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case "Create":
|
||||||
|
fn = func(f File, pos token.Position, value string) Decl {
|
||||||
|
return CreateDecl{
|
||||||
|
file: &f,
|
||||||
|
pos: pos,
|
||||||
|
value: value,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case "Stat":
|
||||||
|
fn = func(f File, pos token.Position, value string) Decl {
|
||||||
|
return StatDecl{
|
||||||
|
file: &f,
|
||||||
|
pos: pos,
|
||||||
|
value: value,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case "Open":
|
||||||
|
fn = func(f File, pos token.Position, value string) Decl {
|
||||||
|
return OpenDecl{
|
||||||
|
file: &f,
|
||||||
|
pos: pos,
|
||||||
|
value: value,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case "Dir":
|
||||||
|
fn = func(f File, pos token.Position, value string) Decl {
|
||||||
|
return HTTPDecl{
|
||||||
|
file: &f,
|
||||||
|
pos: pos,
|
||||||
|
value: value,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case "Walk":
|
||||||
|
fn = func(f File, pos token.Position, value string) Decl {
|
||||||
|
return WalkDecl{
|
||||||
|
file: &f,
|
||||||
|
pos: pos,
|
||||||
|
value: value,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(ce.Args) < 1 {
|
||||||
|
p.err = fmt.Errorf("declarations require at least one argument")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
n := ce.Args[0]
|
||||||
|
val, err := p.value(n)
|
||||||
|
if err != nil {
|
||||||
|
p.err = fmt.Errorf("%w: %s", err, n)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
info, err := here.Dir(filepath.Dir(p.Abs))
|
||||||
|
if err != nil {
|
||||||
|
p.err = fmt.Errorf("%w: %s", err, p.Abs)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
pt, err := info.Parse(val)
|
||||||
|
if err != nil {
|
||||||
|
p.err = fmt.Errorf("%w: %s", err, p.Abs)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if pt.Pkg != info.Module.Path {
|
||||||
|
info, err = here.Package(pt.Pkg)
|
||||||
|
if err != nil {
|
||||||
|
p.err = fmt.Errorf("%w: %s", err, p.Abs)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
f := File{
|
||||||
|
Abs: filepath.Join(info.Module.Dir, pt.Name),
|
||||||
|
Here: info,
|
||||||
|
Path: pt,
|
||||||
|
}
|
||||||
|
|
||||||
|
p.decls[name] = append(p.decls[name], fn(f, p.FileSet.Position(n.Pos()), val))
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
ast.Walk(fn, p.Ast)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ParsedSource) DeclsMap() (map[string]Decls, error) {
|
||||||
|
err := p.Parse()
|
||||||
|
return p.decls, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// wrap a function to fulfill ast.Visitor interface
|
||||||
|
type walker func(ast.Node) bool
|
||||||
|
|
||||||
|
func (w walker) Visit(node ast.Node) ast.Visitor {
|
||||||
|
if w(node) {
|
||||||
|
return w
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -2,6 +2,7 @@ package parser
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
"go/token"
|
"go/token"
|
||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
@ -15,8 +16,7 @@ type StatDecl struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d StatDecl) String() string {
|
func (d StatDecl) String() string {
|
||||||
b, _ := json.Marshal(d)
|
return fmt.Sprintf("pkger.Stat(%q)", d.value)
|
||||||
return string(b)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d StatDecl) MarshalJSON() ([]byte, error) {
|
func (d StatDecl) MarshalJSON() ([]byte, error) {
|
||||||
|
|
|
@ -1,374 +0,0 @@
|
||||||
package parser
|
|
||||||
|
|
||||||
import (
|
|
||||||
"go/ast"
|
|
||||||
"go/token"
|
|
||||||
"path/filepath"
|
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"github.com/markbates/pkger/here"
|
|
||||||
)
|
|
||||||
|
|
||||||
type visitor func(node ast.Node) (w ast.Visitor)
|
|
||||||
|
|
||||||
func (v visitor) Visit(node ast.Node) ast.Visitor {
|
|
||||||
return v(node)
|
|
||||||
}
|
|
||||||
|
|
||||||
// inspired by https://gist.github.com/cryptix/d1b129361cea51a59af2
|
|
||||||
type file struct {
|
|
||||||
fset *token.FileSet
|
|
||||||
astFile *ast.File
|
|
||||||
filename string
|
|
||||||
decls Decls
|
|
||||||
current here.Info
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *file) walk(fn func(ast.Node) bool) {
|
|
||||||
ast.Walk(walker(fn), f.astFile)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *file) find() (Decls, error) {
|
|
||||||
// --- virtual calls first ---
|
|
||||||
if err := f.findMkdirAllCalls(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := f.findCreateCalls(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// -- physical calls second ---
|
|
||||||
if err := f.findStatCalls(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := f.findOpenCalls(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := f.findHTTPCalls(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := f.findWalkCalls(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return f.decls, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *file) asValue(node ast.Node) (string, error) {
|
|
||||||
var s string
|
|
||||||
|
|
||||||
switch x := node.(type) {
|
|
||||||
case *ast.BasicLit:
|
|
||||||
s = x.Value
|
|
||||||
case *ast.Ident:
|
|
||||||
s = x.Name
|
|
||||||
}
|
|
||||||
|
|
||||||
return strconv.Unquote(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *file) findMkdirAllCalls() error {
|
|
||||||
var err error
|
|
||||||
f.walk(func(node ast.Node) bool {
|
|
||||||
ce, ok := node.(*ast.CallExpr)
|
|
||||||
if !ok {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
exists := isPkgDot(ce.Fun, "pkger", "MkdirAll")
|
|
||||||
if !(exists) || len(ce.Args) != 2 {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
n := ce.Args[0]
|
|
||||||
|
|
||||||
s, err := f.asValue(n)
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
info, err := here.Dir(filepath.Dir(f.filename))
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
pf := &File{
|
|
||||||
Abs: f.filename,
|
|
||||||
Here: info,
|
|
||||||
Path: here.Path{
|
|
||||||
Pkg: info.Module.Path,
|
|
||||||
Name: s,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
decl := MkdirAllDecl{
|
|
||||||
file: pf,
|
|
||||||
pos: f.fset.Position(n.Pos()),
|
|
||||||
value: s,
|
|
||||||
}
|
|
||||||
|
|
||||||
f.decls = append(f.decls, decl)
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *file) findStatCalls() error {
|
|
||||||
var err error
|
|
||||||
f.walk(func(node ast.Node) bool {
|
|
||||||
ce, ok := node.(*ast.CallExpr)
|
|
||||||
if !ok {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
exists := isPkgDot(ce.Fun, "pkger", "Stat")
|
|
||||||
if !(exists) || len(ce.Args) != 1 {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
n := ce.Args[0]
|
|
||||||
|
|
||||||
s, err := f.asValue(n)
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
info, err := here.Dir(filepath.Dir(f.filename))
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
pf := &File{
|
|
||||||
Abs: f.filename,
|
|
||||||
Here: info,
|
|
||||||
Path: here.Path{
|
|
||||||
Pkg: info.Module.Path,
|
|
||||||
Name: s,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
decl := StatDecl{
|
|
||||||
file: pf,
|
|
||||||
pos: f.fset.Position(n.Pos()),
|
|
||||||
value: s,
|
|
||||||
}
|
|
||||||
|
|
||||||
f.decls = append(f.decls, decl)
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *file) findCreateCalls() error {
|
|
||||||
var err error
|
|
||||||
f.walk(func(node ast.Node) bool {
|
|
||||||
ce, ok := node.(*ast.CallExpr)
|
|
||||||
if !ok {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
exists := isPkgDot(ce.Fun, "pkger", "Create")
|
|
||||||
if !(exists) || len(ce.Args) != 1 {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
n := ce.Args[0]
|
|
||||||
|
|
||||||
s, err := f.asValue(n)
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
info, err := here.Dir(filepath.Dir(f.filename))
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
pf := &File{
|
|
||||||
Abs: f.filename,
|
|
||||||
Here: info,
|
|
||||||
Path: here.Path{
|
|
||||||
Pkg: info.Module.Path,
|
|
||||||
Name: s,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
decl := CreateDecl{
|
|
||||||
file: pf,
|
|
||||||
pos: f.fset.Position(n.Pos()),
|
|
||||||
value: s,
|
|
||||||
}
|
|
||||||
|
|
||||||
f.decls = append(f.decls, decl)
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *file) findOpenCalls() error {
|
|
||||||
var err error
|
|
||||||
f.walk(func(node ast.Node) bool {
|
|
||||||
ce, ok := node.(*ast.CallExpr)
|
|
||||||
if !ok {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
exists := isPkgDot(ce.Fun, "pkger", "Open")
|
|
||||||
if !(exists) || len(ce.Args) != 1 {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
n := ce.Args[0]
|
|
||||||
|
|
||||||
s, err := f.asValue(n)
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
info, err := here.Dir(filepath.Dir(f.filename))
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
pf := &File{
|
|
||||||
Abs: f.filename,
|
|
||||||
Here: info,
|
|
||||||
Path: here.Path{
|
|
||||||
Pkg: info.Module.Path,
|
|
||||||
Name: s,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
decl := OpenDecl{
|
|
||||||
file: pf,
|
|
||||||
pos: f.fset.Position(n.Pos()),
|
|
||||||
value: s,
|
|
||||||
}
|
|
||||||
|
|
||||||
f.decls = append(f.decls, decl)
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *file) findWalkCalls() error {
|
|
||||||
var err error
|
|
||||||
f.walk(func(node ast.Node) bool {
|
|
||||||
ce, ok := node.(*ast.CallExpr)
|
|
||||||
if !ok {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
exists := isPkgDot(ce.Fun, "pkger", "Walk")
|
|
||||||
if !(exists) || len(ce.Args) != 2 {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
n := ce.Args[0]
|
|
||||||
|
|
||||||
s, err := f.asValue(n)
|
|
||||||
if err != nil {
|
|
||||||
err = err
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
dir := filepath.Dir(f.filename)
|
|
||||||
info, err := here.Dir(dir)
|
|
||||||
if err != nil {
|
|
||||||
err = err
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
pf := &File{
|
|
||||||
Abs: f.filename,
|
|
||||||
Here: info,
|
|
||||||
Path: here.Path{
|
|
||||||
Pkg: info.Module.Path,
|
|
||||||
Name: s,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
decl := WalkDecl{
|
|
||||||
file: pf,
|
|
||||||
pos: f.fset.Position(n.Pos()),
|
|
||||||
value: s,
|
|
||||||
}
|
|
||||||
|
|
||||||
f.decls = append(f.decls, decl)
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *file) findHTTPCalls() error {
|
|
||||||
var err error
|
|
||||||
f.walk(func(node ast.Node) bool {
|
|
||||||
ce, ok := node.(*ast.CallExpr)
|
|
||||||
if !ok {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
exists := isPkgDot(ce.Fun, "pkger", "Dir")
|
|
||||||
if !(exists) || len(ce.Args) != 1 {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
n := ce.Args[0]
|
|
||||||
|
|
||||||
s, err := f.asValue(n)
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
info, err := here.Dir(filepath.Dir(f.filename))
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
pf := &File{
|
|
||||||
Abs: f.filename,
|
|
||||||
Here: info,
|
|
||||||
Path: here.Path{
|
|
||||||
Pkg: info.Module.Path,
|
|
||||||
Name: s,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
pos := f.fset.Position(n.Pos())
|
|
||||||
decl := HTTPDecl{
|
|
||||||
file: pf,
|
|
||||||
pos: pos,
|
|
||||||
value: s,
|
|
||||||
}
|
|
||||||
|
|
||||||
f.decls = append(f.decls, decl)
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// helpers
|
|
||||||
// =======
|
|
||||||
func isPkgDot(expr ast.Expr, pkg, name string) bool {
|
|
||||||
sel, ok := expr.(*ast.SelectorExpr)
|
|
||||||
return ok && isIdent(sel.X, pkg) && isIdent(sel.Sel, name)
|
|
||||||
}
|
|
||||||
|
|
||||||
func isIdent(expr ast.Expr, ident string) bool {
|
|
||||||
id, ok := expr.(*ast.Ident)
|
|
||||||
return ok && id.Name == ident
|
|
||||||
}
|
|
||||||
|
|
||||||
// wrap a function to fulfill ast.Visitor interface
|
|
||||||
type walker func(ast.Node) bool
|
|
||||||
|
|
||||||
func (w walker) Visit(node ast.Node) ast.Visitor {
|
|
||||||
if w(node) {
|
|
||||||
return w
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -1,10 +1,8 @@
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
@ -12,7 +10,6 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
|
||||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
|
gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func Serve() {
|
func Serve() {
|
||||||
|
pkger.Stat("github.com/gobuffalo/buffalo:/logo.svg")
|
||||||
dir := http.FileServer(pkger.Dir("/public"))
|
dir := http.FileServer(pkger.Dir("/public"))
|
||||||
http.ListenAndServe(":3000", dir)
|
http.ListenAndServe(":3000", dir)
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,16 +29,20 @@ func Test_Stuff(t *testing.T) {
|
||||||
decls, err := parser.Parse(ref.Info)
|
decls, err := parser.Parse(ref.Info)
|
||||||
r.NoError(err)
|
r.NoError(err)
|
||||||
|
|
||||||
r.Len(decls, 9)
|
r.Len(decls, 10)
|
||||||
|
|
||||||
files, err := decls.Files()
|
files, err := decls.Files()
|
||||||
r.NoError(err)
|
r.NoError(err)
|
||||||
|
|
||||||
for _, f := range files {
|
for _, f := range files {
|
||||||
|
if f.Path.Pkg == ref.Module.Path {
|
||||||
r.Equal("app", f.Path.Pkg)
|
r.Equal("app", f.Path.Pkg)
|
||||||
|
} else {
|
||||||
|
r.NotEqual("app", f.Path.Pkg)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
r.Len(files, 22)
|
r.Len(files, 23)
|
||||||
|
|
||||||
bb := &bytes.Buffer{}
|
bb := &bytes.Buffer{}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue