golangci-lint/pkg/fsutils/path_resolver.go
2018-05-08 12:28:36 +03:00

189 lines
3.8 KiB
Go

package fsutils
import (
"fmt"
"os"
"path/filepath"
"sort"
"strings"
)
type PathResolver struct {
excludeDirs map[string]bool
allowedFileExtensions map[string]bool
includeTests bool
}
type pathResolveState struct {
files map[string]bool
dirs map[string]bool
}
func (s *pathResolveState) addFile(path string) {
s.files[filepath.Clean(path)] = true
}
func (s *pathResolveState) addDir(path string) {
s.dirs[filepath.Clean(path)] = true
}
type PathResolveResult struct {
files []string
dirs []string
}
func (prr PathResolveResult) Files() []string {
return prr.files
}
func (prr PathResolveResult) Dirs() []string {
return prr.dirs
}
func (s pathResolveState) toResult() *PathResolveResult {
res := &PathResolveResult{
files: []string{},
dirs: []string{},
}
for f := range s.files {
res.files = append(res.files, f)
}
for d := range s.dirs {
res.dirs = append(res.dirs, d)
}
sort.Strings(res.files)
sort.Strings(res.dirs)
return res
}
func NewPathResolver(excludeDirs, allowedFileExtensions []string, includeTests bool) *PathResolver {
excludeDirsMap := map[string]bool{}
for _, dir := range excludeDirs {
excludeDirsMap[dir] = true
}
allowedFileExtensionsMap := map[string]bool{}
for _, fe := range allowedFileExtensions {
allowedFileExtensionsMap[fe] = true
}
return &PathResolver{
excludeDirs: excludeDirsMap,
allowedFileExtensions: allowedFileExtensionsMap,
includeTests: includeTests,
}
}
func (pr PathResolver) isIgnoredDir(dir string) bool {
dirName := filepath.Base(filepath.Clean(dir)) // ignore dirs on any depth level
// https://github.com/golang/dep/issues/298
// https://github.com/tools/godep/issues/140
if strings.HasPrefix(dirName, ".") && dirName != "." {
return true
}
if strings.HasPrefix(dirName, "_") {
return true
}
return pr.excludeDirs[dirName]
}
func (pr PathResolver) isAllowedFile(path string) bool {
if !pr.includeTests && strings.HasSuffix(path, "_test.go") {
return false
}
return pr.allowedFileExtensions[filepath.Ext(path)]
}
func (pr PathResolver) resolveRecursively(root string, state *pathResolveState) error {
walkErr := filepath.Walk(root, func(p string, i os.FileInfo, err error) error {
if err != nil {
return err
}
if i.IsDir() {
if pr.isIgnoredDir(p) {
return filepath.SkipDir
}
state.addDir(p)
return nil
}
if pr.isAllowedFile(p) {
state.addFile(p)
}
return nil
})
if walkErr != nil {
return fmt.Errorf("can't walk dir %s: %s", root, walkErr)
}
return nil
}
func (pr PathResolver) resolveDir(root string, state *pathResolveState) error {
walkErr := filepath.Walk(root, func(p string, i os.FileInfo, err error) error {
if err != nil {
return err
}
if i.IsDir() {
if filepath.Clean(p) != filepath.Clean(root) {
return filepath.SkipDir
}
state.addDir(p)
return nil
}
if pr.isAllowedFile(p) {
state.addFile(p)
}
return nil
})
if walkErr != nil {
return fmt.Errorf("can't walk dir %s: %s", root, walkErr)
}
return nil
}
func (pr PathResolver) Resolve(paths ...string) (*PathResolveResult, error) {
if len(paths) == 0 {
return nil, fmt.Errorf("no paths are set")
}
state := &pathResolveState{
files: map[string]bool{},
dirs: map[string]bool{},
}
for _, path := range paths {
if strings.HasSuffix(path, "/...") {
if err := pr.resolveRecursively(filepath.Dir(path), state); err != nil {
return nil, fmt.Errorf("can't recursively resolve %s: %s", path, err)
}
continue
}
fi, err := os.Stat(path)
if err != nil {
return nil, fmt.Errorf("can't find path %s: %s", path, err)
}
if fi.IsDir() {
if err := pr.resolveDir(path, state); err != nil {
return nil, fmt.Errorf("can't resolve dir %s: %s", path, err)
}
continue
}
state.addFile(path)
}
return state.toResult(), nil
}