2016-11-21 18:38:39 +00:00
|
|
|
package filepathfilter
|
|
|
|
|
2016-11-21 21:34:45 +00:00
|
|
|
import (
|
|
|
|
"strings"
|
2018-02-08 00:58:44 +00:00
|
|
|
|
2021-08-23 15:52:52 +00:00
|
|
|
"github.com/git-lfs/wildmatch/v2"
|
2018-02-15 23:52:32 +00:00
|
|
|
"github.com/rubyist/tracerx"
|
2016-11-21 21:34:45 +00:00
|
|
|
)
|
|
|
|
|
2016-11-21 23:44:41 +00:00
|
|
|
type Pattern interface {
|
2016-11-21 21:34:45 +00:00
|
|
|
Match(filename string) bool
|
2017-06-20 23:28:34 +00:00
|
|
|
// String returns a string representation (see: regular expressions) of
|
|
|
|
// the underlying pattern used to match filenames against this Pattern.
|
|
|
|
String() string
|
2016-11-21 21:34:45 +00:00
|
|
|
}
|
2016-11-21 18:38:39 +00:00
|
|
|
|
|
|
|
type Filter struct {
|
2020-06-25 18:59:46 +00:00
|
|
|
include []Pattern
|
|
|
|
exclude []Pattern
|
|
|
|
defaultValue bool
|
2016-11-21 23:44:41 +00:00
|
|
|
}
|
|
|
|
|
2021-08-17 13:29:55 +00:00
|
|
|
type PatternType bool
|
|
|
|
|
|
|
|
const (
|
|
|
|
GitIgnore = PatternType(false)
|
|
|
|
GitAttributes = PatternType(true)
|
|
|
|
)
|
|
|
|
|
|
|
|
func (p PatternType) String() string {
|
|
|
|
if p == GitIgnore {
|
|
|
|
return "gitignore"
|
|
|
|
}
|
|
|
|
return "gitattributes"
|
|
|
|
}
|
|
|
|
|
2020-06-25 18:59:46 +00:00
|
|
|
type options struct {
|
|
|
|
defaultValue bool
|
2016-11-21 18:38:39 +00:00
|
|
|
}
|
|
|
|
|
2020-06-25 18:59:46 +00:00
|
|
|
type option func(*options)
|
|
|
|
|
|
|
|
// DefaultValue is an option representing the default value of a filepathfilter
|
|
|
|
// if no patterns match. If this option is not provided, the default is true.
|
|
|
|
func DefaultValue(val bool) option {
|
|
|
|
return func(args *options) {
|
|
|
|
args.defaultValue = val
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewFromPatterns(include, exclude []Pattern, setters ...option) *Filter {
|
|
|
|
args := &options{defaultValue: true}
|
|
|
|
for _, setter := range setters {
|
|
|
|
setter(args)
|
|
|
|
}
|
|
|
|
return &Filter{include: include, exclude: exclude, defaultValue: args.defaultValue}
|
|
|
|
}
|
|
|
|
|
2021-08-17 13:29:55 +00:00
|
|
|
func New(include, exclude []string, ptype PatternType, setters ...option) *Filter {
|
2018-02-08 00:58:44 +00:00
|
|
|
return NewFromPatterns(
|
2021-08-17 13:29:55 +00:00
|
|
|
convertToWildmatch(include, ptype),
|
|
|
|
convertToWildmatch(exclude, ptype), setters...)
|
2016-11-21 18:38:39 +00:00
|
|
|
}
|
|
|
|
|
2017-06-23 18:05:46 +00:00
|
|
|
// Include returns the result of calling String() on each Pattern in the
|
|
|
|
// include set of this *Filter.
|
2018-02-08 00:58:44 +00:00
|
|
|
func (f *Filter) Include() []string { return wildmatchToString(f.include...) }
|
2017-06-23 18:05:46 +00:00
|
|
|
|
|
|
|
// Exclude returns the result of calling String() on each Pattern in the
|
|
|
|
// exclude set of this *Filter.
|
2018-02-08 00:58:44 +00:00
|
|
|
func (f *Filter) Exclude() []string { return wildmatchToString(f.exclude...) }
|
2017-06-23 18:05:46 +00:00
|
|
|
|
2018-02-08 00:58:44 +00:00
|
|
|
// wildmatchToString maps the given set of Pattern's to a string slice by
|
2017-06-23 18:05:46 +00:00
|
|
|
// calling String() on each pattern.
|
2018-02-08 00:58:44 +00:00
|
|
|
func wildmatchToString(ps ...Pattern) []string {
|
2017-06-23 18:05:46 +00:00
|
|
|
s := make([]string, 0, len(ps))
|
|
|
|
for _, p := range ps {
|
|
|
|
s = append(s, p.String())
|
|
|
|
}
|
|
|
|
|
|
|
|
return s
|
|
|
|
}
|
|
|
|
|
2016-11-21 21:34:45 +00:00
|
|
|
func (f *Filter) Allows(filename string) bool {
|
2018-01-30 01:46:44 +00:00
|
|
|
if f == nil {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2018-02-26 23:58:13 +00:00
|
|
|
var included bool
|
2018-02-08 00:58:44 +00:00
|
|
|
for _, inc := range f.include {
|
2018-02-26 23:58:13 +00:00
|
|
|
if included = inc.Match(filename); included {
|
2018-02-08 00:58:44 +00:00
|
|
|
break
|
|
|
|
}
|
2018-01-30 01:46:44 +00:00
|
|
|
}
|
|
|
|
|
2018-02-26 23:58:13 +00:00
|
|
|
if !included && len(f.include) > 0 {
|
2018-11-02 16:36:48 +00:00
|
|
|
tracerx.Printf("filepathfilter: rejecting %q via %v", filename, f.include)
|
2018-02-08 00:58:44 +00:00
|
|
|
return false
|
2018-01-30 01:46:44 +00:00
|
|
|
}
|
|
|
|
|
2020-06-25 18:59:46 +00:00
|
|
|
// Beyond this point, the only values we can logically return are false
|
|
|
|
// or the default value. If the default is false, then there's no point
|
|
|
|
// traversing the exclude patterns because the return value will always
|
|
|
|
// be false; we'd do extra work for no functional benefit.
|
|
|
|
if !included && !f.defaultValue {
|
|
|
|
tracerx.Printf("filepathfilter: rejecting %q", filename)
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2018-02-08 00:58:44 +00:00
|
|
|
for _, ex := range f.exclude {
|
|
|
|
if ex.Match(filename) {
|
2018-02-15 23:52:32 +00:00
|
|
|
tracerx.Printf("filepathfilter: rejecting %q via %q", filename, ex.String())
|
2018-02-08 00:58:44 +00:00
|
|
|
return false
|
2018-01-30 01:46:44 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-25 18:59:46 +00:00
|
|
|
// No patterns matched and our default value is true.
|
2018-02-15 23:52:32 +00:00
|
|
|
tracerx.Printf("filepathfilter: accepting %q", filename)
|
2018-01-30 01:46:44 +00:00
|
|
|
return true
|
2017-06-20 23:34:44 +00:00
|
|
|
}
|
|
|
|
|
2018-02-08 00:58:44 +00:00
|
|
|
type wm struct {
|
2021-08-17 13:29:55 +00:00
|
|
|
w *wildmatch.Wildmatch
|
|
|
|
p string
|
2017-06-20 23:25:19 +00:00
|
|
|
}
|
|
|
|
|
2018-02-08 00:58:44 +00:00
|
|
|
func (w *wm) Match(filename string) bool {
|
2021-08-17 13:29:55 +00:00
|
|
|
return w.w.Match(filename)
|
2017-08-08 02:46:23 +00:00
|
|
|
}
|
|
|
|
|
2018-02-08 00:58:44 +00:00
|
|
|
func (w *wm) String() string {
|
2018-02-08 02:36:12 +00:00
|
|
|
return w.p
|
2017-06-20 23:25:45 +00:00
|
|
|
}
|
|
|
|
|
2018-02-14 23:45:07 +00:00
|
|
|
const (
|
|
|
|
sep byte = '/'
|
|
|
|
)
|
|
|
|
|
2021-08-17 13:29:55 +00:00
|
|
|
func NewPattern(p string, ptype PatternType) Pattern {
|
|
|
|
tracerx.Printf("filepathfilter: creating pattern %q of type %v", p, ptype)
|
|
|
|
|
|
|
|
switch ptype {
|
|
|
|
case GitIgnore:
|
|
|
|
return &wm{
|
|
|
|
p: p,
|
|
|
|
w: wildmatch.NewWildmatch(
|
|
|
|
p,
|
|
|
|
wildmatch.SystemCase,
|
|
|
|
wildmatch.Contents,
|
|
|
|
),
|
2020-11-10 22:52:52 +00:00
|
|
|
}
|
2021-08-17 13:29:55 +00:00
|
|
|
case GitAttributes:
|
|
|
|
return &wm{
|
|
|
|
p: p,
|
|
|
|
w: wildmatch.NewWildmatch(
|
|
|
|
p,
|
|
|
|
wildmatch.SystemCase,
|
|
|
|
wildmatch.Basename,
|
|
|
|
wildmatch.GitAttributes,
|
|
|
|
),
|
2018-02-08 01:46:27 +00:00
|
|
|
}
|
2021-08-17 13:29:55 +00:00
|
|
|
default:
|
|
|
|
panic("unreachable")
|
2017-08-08 02:46:23 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-14 23:45:07 +00:00
|
|
|
// 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
|
|
|
|
}
|
|
|
|
|
2021-08-17 13:29:55 +00:00
|
|
|
func convertToWildmatch(rawpatterns []string, ptype PatternType) []Pattern {
|
2018-02-08 00:58:44 +00:00
|
|
|
patterns := make([]Pattern, len(rawpatterns))
|
|
|
|
for i, raw := range rawpatterns {
|
2021-08-17 13:29:55 +00:00
|
|
|
patterns[i] = NewPattern(raw, ptype)
|
2017-11-17 16:29:43 +00:00
|
|
|
}
|
2018-02-08 00:58:44 +00:00
|
|
|
return patterns
|
2016-11-21 23:34:35 +00:00
|
|
|
}
|