-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathfile.go
137 lines (110 loc) · 3.67 KB
/
file.go
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
package utility
import (
"os"
"path/filepath"
"strings"
"github.com/pkg/errors"
)
const (
WalkThroughError = "walking through file tree"
)
// FileExists provides a clearer interface for checking if a file
// exists.
func FileExists(path string) bool {
if path == "" {
return false
}
_, err := os.Stat(path)
return !os.IsNotExist(err)
}
// WriteRawFile writes a sequence of byes to a new file created at the
// specified path.
func WriteRawFile(path string, data []byte) error {
file, err := os.Create(path)
if err != nil {
return errors.Wrapf(err, "creating file '%s'", path)
}
_, err = file.Write(data)
if err != nil {
return errors.Wrapf(err, "writing data to file '%s'", path)
}
if err = file.Close(); err != nil {
return errors.Wrapf(err, "closing file '%s' after successfully writing data", path)
}
return nil
}
// WriteFile provides a clearer interface for writing string data to a
// file.
func WriteFile(path string, data string) error {
return errors.WithStack(WriteRawFile(path, []byte(data)))
}
// FileMatcher represents a type that can match against files and file
// information to determine if it should be included.
type FileMatcher interface {
Match(file string, info os.FileInfo) bool
}
// AlwaysMatch provides a FileMatcher implementation that will always match a
// file or directory.
type AlwaysMatch struct{}
// Match always returns true.
func (m AlwaysMatch) Match(string, os.FileInfo) bool { return true }
// FileFileAlwaysMatch provides a FileMatcher implementation that will always
// match a file, but not a directory.
type FileAlwaysMatch struct{}
func (m FileAlwaysMatch) Match(_ string, info os.FileInfo) bool {
return !info.IsDir()
}
// NeverMatch provides a FileMatcher implementation that will never match a
// file or directory.
type NeverMatch struct{}
// Match always returns false.
func (m NeverMatch) Match(string, os.FileInfo) bool { return false }
// FileListBuilder provides options to find files within a directory.
type FileListBuilder struct {
// Include determines which files should be included. This has lower
// precedence than the Exclude filter.
Include FileMatcher
// Exclude determines which files should be excluded from the file list.
// This has higher precedence than the Include filter.
Exclude FileMatcher
// WorkingDir is the base working directory from which to start searching
// for files.
WorkingDir string
files []string
}
// NewFileListBuilder returns a default FileListBuilder that will match all
// files (but not directory names) within the given directory dir.
func NewFileListBuilder(dir string) *FileListBuilder {
return &FileListBuilder{
Include: FileAlwaysMatch{},
Exclude: NeverMatch{},
WorkingDir: dir,
}
}
// Build finds all files that pass the include and exclude filters within the
// working directory. It does not follow symlinks. All files returned are
// relative to the working directory.
func (b *FileListBuilder) Build() ([]string, error) {
if b.Include == nil {
return nil, errors.New("cannot build file list without an include filter")
}
if err := filepath.Walk(b.WorkingDir, b.visitPath); err != nil {
return nil, errors.Wrap(err, WalkThroughError)
}
return b.files, nil
}
func (b *FileListBuilder) visitPath(path string, info os.FileInfo, err error) error {
if err != nil {
return errors.Wrapf(err, "path '%s'", path)
}
if b.Exclude != nil && b.Exclude.Match(path, info) {
return nil
}
if b.Include == nil || !b.Include.Match(path, info) {
return nil
}
// All accumulated paths are relative to the working directory.
toAdd := strings.TrimPrefix(strings.TrimPrefix(path, b.WorkingDir), string(os.PathSeparator))
b.files = append(b.files, toAdd)
return nil
}