04abbd8436
Following on from the changes in PR #4781, we can make additional message strings translatable using the tr.Tr.Get() method. Because this method performs printf(3)-style format string parsing and interpolation, we can simplify some of the surrounding calls, e.g., from fmt.Errorf() to errors.New(), and from fmt.Fprintf() to fmt.Fprintln(). This ensures that if either the translated text or any interpolated arguments happen to contain character sequences that would be interpreted as Go format specifiers (e.g., "%s" or "%d"), these will not result in warnings such as "%!s(MISSING)" in the output text. Note also that we try to remove newlines from the message strings were possible and change the surrounding calls to append them instead, e.g., with fmt.Fprintln().
191 lines
4.4 KiB
Go
191 lines
4.4 KiB
Go
package filepathfilter
|
|
|
|
import (
|
|
"strings"
|
|
|
|
"github.com/git-lfs/git-lfs/v3/tr"
|
|
"github.com/git-lfs/wildmatch/v2"
|
|
"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
|
|
defaultValue bool
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
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...)
|
|
}
|
|
|
|
// 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 {
|
|
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(tr.Tr.Get("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
|
|
}
|