176 lines
3.9 KiB
Go
176 lines
3.9 KiB
Go
package filepathfilter
|
|
|
|
import (
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/git-lfs/wildmatch"
|
|
"github.com/rubyist/tracerx"
|
|
)
|
|
|
|
type Pattern interface {
|
|
Match(filename string) bool
|
|
// String returns a string representation (see: regular expressions) of
|
|
// the underlying pattern used to match filenames against this Pattern.
|
|
String() string
|
|
}
|
|
|
|
type Filter struct {
|
|
include []Pattern
|
|
exclude []Pattern
|
|
}
|
|
|
|
func NewFromPatterns(include, exclude []Pattern) *Filter {
|
|
return &Filter{include: include, exclude: exclude}
|
|
}
|
|
|
|
func New(include, exclude []string) *Filter {
|
|
return NewFromPatterns(
|
|
convertToWildmatch(include),
|
|
convertToWildmatch(exclude))
|
|
}
|
|
|
|
// Include returns the result of calling String() on each Pattern in the
|
|
// include set of this *Filter.
|
|
func (f *Filter) Include() []string { return wildmatchToString(f.include...) }
|
|
|
|
// Exclude returns the result of calling String() on each Pattern in the
|
|
// exclude set of this *Filter.
|
|
func (f *Filter) Exclude() []string { return wildmatchToString(f.exclude...) }
|
|
|
|
// wildmatchToString maps the given set of Pattern's to a string slice by
|
|
// calling String() on each pattern.
|
|
func wildmatchToString(ps ...Pattern) []string {
|
|
s := make([]string, 0, len(ps))
|
|
for _, p := range ps {
|
|
s = append(s, p.String())
|
|
}
|
|
|
|
return s
|
|
}
|
|
|
|
func (f *Filter) Allows(filename string) bool {
|
|
if f == nil {
|
|
return true
|
|
}
|
|
|
|
var included bool
|
|
for _, inc := range f.include {
|
|
if included = inc.Match(filename); included {
|
|
break
|
|
}
|
|
}
|
|
|
|
tracerx.Printf("filepathfilter: rejecting %q via %v", filename, f.include)
|
|
if !included && len(f.include) > 0 {
|
|
return false
|
|
}
|
|
|
|
for _, ex := range f.exclude {
|
|
if ex.Match(filename) {
|
|
tracerx.Printf("filepathfilter: rejecting %q via %q", filename, ex.String())
|
|
return false
|
|
}
|
|
}
|
|
|
|
tracerx.Printf("filepathfilter: accepting %q", filename)
|
|
return true
|
|
}
|
|
|
|
type wm struct {
|
|
w *wildmatch.Wildmatch
|
|
p string
|
|
dirs bool
|
|
}
|
|
|
|
func (w *wm) Match(filename string) bool {
|
|
return w.w.Match(w.chomp(filename))
|
|
}
|
|
|
|
func (w *wm) chomp(filename string) string {
|
|
return strings.TrimSuffix(filename, string(filepath.Separator))
|
|
}
|
|
|
|
func (w *wm) String() string {
|
|
return w.p
|
|
}
|
|
|
|
const (
|
|
sep byte = '/'
|
|
)
|
|
|
|
func NewPattern(p string) Pattern {
|
|
pp := p
|
|
|
|
// Special case: the below patterns match anything according to existing
|
|
// behavior.
|
|
switch pp {
|
|
case `*`, `.`, `./`, `.\`:
|
|
pp = join("**", "*")
|
|
}
|
|
|
|
dirs := strings.Contains(pp, string(sep))
|
|
rooted := strings.HasPrefix(pp, string(sep))
|
|
wild := strings.Contains(pp, "*")
|
|
|
|
if !dirs && !wild {
|
|
// Special case: if pp is a literal string (optionally including
|
|
// a character class), rewrite it is a substring match.
|
|
pp = join("**", pp, "**")
|
|
} else {
|
|
if dirs && !rooted {
|
|
// Special case: if there are any directory separators,
|
|
// rewrite "pp" as a substring match.
|
|
if !wild {
|
|
pp = join("**", pp, "**")
|
|
}
|
|
} else {
|
|
if rooted {
|
|
// Special case: if there are not any directory
|
|
// separators, rewrite "pp" as a substring
|
|
// match.
|
|
pp = join(pp, "**")
|
|
} else {
|
|
// Special case: if there are not any directory
|
|
// separators, rewrite "pp" as a substring
|
|
// match.
|
|
pp = join("**", pp)
|
|
}
|
|
}
|
|
}
|
|
tracerx.Printf("filepathfilter: rewrite %q as %q", p, pp)
|
|
|
|
return &wm{
|
|
p: p,
|
|
w: wildmatch.NewWildmatch(
|
|
pp,
|
|
wildmatch.SystemCase,
|
|
),
|
|
dirs: dirs,
|
|
}
|
|
}
|
|
|
|
// join joins path elements together via the separator "sep" and produces valid
|
|
// paths without multiple separators (unless multiple separators were included
|
|
// in the original paths []string).
|
|
func join(paths ...string) string {
|
|
var joined string
|
|
|
|
for i, path := range paths {
|
|
joined = joined + path
|
|
if i != len(paths)-1 && !strings.HasSuffix(path, string(sep)) {
|
|
joined = joined + string(sep)
|
|
}
|
|
}
|
|
|
|
return joined
|
|
}
|
|
|
|
func convertToWildmatch(rawpatterns []string) []Pattern {
|
|
patterns := make([]Pattern, len(rawpatterns))
|
|
for i, raw := range rawpatterns {
|
|
patterns[i] = NewPattern(raw)
|
|
}
|
|
return patterns
|
|
}
|