git-lfs/filepathfilter/filepathfilter.go

190 lines
4.3 KiB
Go
Raw Normal View History

2016-11-21 18:38:39 +00:00
package filepathfilter
import (
"strings"
"github.com/git-lfs/wildmatch/v2"
"github.com/rubyist/tracerx"
)
2016-11-21 23:44:41 +00:00
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
}
2016-11-21 18:38:39 +00:00
type Filter struct {
include []Pattern
exclude []Pattern
defaultValue bool
2016-11-21 23:44:41 +00:00
}
type PatternType bool
const (
GitIgnore = PatternType(false)
GitAttributes = PatternType(true)
)
func (p PatternType) String() string {
if p == GitIgnore {
return "gitignore"
}
return "gitattributes"
}
type options struct {
defaultValue bool
2016-11-21 18:38:39 +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}
}
func New(include, exclude []string, ptype PatternType, setters ...option) *Filter {
return NewFromPatterns(
convertToWildmatch(include, ptype),
convertToWildmatch(exclude, ptype), setters...)
2016-11-21 18:38:39 +00:00
}
// 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
}
}
if !included && len(f.include) > 0 {
tracerx.Printf("filepathfilter: rejecting %q via %v", filename, f.include)
return false
}
// 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
}
for _, ex := range f.exclude {
if ex.Match(filename) {
tracerx.Printf("filepathfilter: rejecting %q via %q", filename, ex.String())
return false
}
}
// No patterns matched and our default value is true.
tracerx.Printf("filepathfilter: accepting %q", filename)
return true
}
type wm struct {
w *wildmatch.Wildmatch
p string
}
func (w *wm) Match(filename string) bool {
return w.w.Match(filename)
}
func (w *wm) String() string {
2018-02-08 02:36:12 +00:00
return w.p
}
const (
sep byte = '/'
)
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,
),
}
case GitAttributes:
return &wm{
p: p,
w: wildmatch.NewWildmatch(
p,
wildmatch.SystemCase,
wildmatch.Basename,
wildmatch.GitAttributes,
),
}
default:
panic("unreachable")
}
}
// 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, ptype PatternType) []Pattern {
patterns := make([]Pattern, len(rawpatterns))
for i, raw := range rawpatterns {
patterns[i] = NewPattern(raw, ptype)
}
return patterns
2016-11-21 23:34:35 +00:00
}