2016-06-02 17:47:15 +00:00
|
|
|
package filter
|
|
|
|
|
|
|
|
import (
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/gobwas/glob"
|
|
|
|
)
|
|
|
|
|
|
|
|
type Filter interface {
|
|
|
|
Match(string) bool
|
|
|
|
}
|
|
|
|
|
2016-09-05 15:16:37 +00:00
|
|
|
// Compile takes a list of string filters and returns a Filter interface
|
2016-06-02 17:47:15 +00:00
|
|
|
// for matching a given string against the filter list. The filter list
|
|
|
|
// supports glob matching too, ie:
|
|
|
|
//
|
2016-09-05 15:16:37 +00:00
|
|
|
// f, _ := Compile([]string{"cpu", "mem", "net*"})
|
2016-06-02 17:47:15 +00:00
|
|
|
// f.Match("cpu") // true
|
|
|
|
// f.Match("network") // true
|
|
|
|
// f.Match("memory") // false
|
|
|
|
//
|
2016-09-05 15:16:37 +00:00
|
|
|
func Compile(filters []string) (Filter, error) {
|
2016-06-02 17:47:15 +00:00
|
|
|
// return if there is nothing to compile
|
|
|
|
if len(filters) == 0 {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// check if we can compile a non-glob filter
|
|
|
|
noGlob := true
|
|
|
|
for _, filter := range filters {
|
|
|
|
if hasMeta(filter) {
|
|
|
|
noGlob = false
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
switch {
|
|
|
|
case noGlob:
|
|
|
|
// return non-globbing filter if not needed.
|
|
|
|
return compileFilterNoGlob(filters), nil
|
|
|
|
case len(filters) == 1:
|
|
|
|
return glob.Compile(filters[0])
|
|
|
|
default:
|
|
|
|
return glob.Compile("{" + strings.Join(filters, ",") + "}")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// hasMeta reports whether path contains any magic glob characters.
|
|
|
|
func hasMeta(s string) bool {
|
|
|
|
return strings.IndexAny(s, "*?[") >= 0
|
|
|
|
}
|
|
|
|
|
|
|
|
type filter struct {
|
|
|
|
m map[string]struct{}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (f *filter) Match(s string) bool {
|
|
|
|
_, ok := f.m[s]
|
|
|
|
return ok
|
|
|
|
}
|
|
|
|
|
|
|
|
type filtersingle struct {
|
|
|
|
s string
|
|
|
|
}
|
|
|
|
|
|
|
|
func (f *filtersingle) Match(s string) bool {
|
|
|
|
return f.s == s
|
|
|
|
}
|
|
|
|
|
|
|
|
func compileFilterNoGlob(filters []string) Filter {
|
|
|
|
if len(filters) == 1 {
|
|
|
|
return &filtersingle{s: filters[0]}
|
|
|
|
}
|
|
|
|
out := filter{m: make(map[string]struct{})}
|
|
|
|
for _, filter := range filters {
|
|
|
|
out.m[filter] = struct{}{}
|
|
|
|
}
|
|
|
|
return &out
|
|
|
|
}
|
2017-10-12 22:50:09 +00:00
|
|
|
|
|
|
|
type IncludeExcludeFilter struct {
|
|
|
|
include Filter
|
|
|
|
exclude Filter
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewIncludeExcludeFilter(
|
|
|
|
include []string,
|
|
|
|
exclude []string,
|
|
|
|
) (Filter, error) {
|
|
|
|
in, err := Compile(include)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
ex, err := Compile(exclude)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return &IncludeExcludeFilter{in, ex}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (f *IncludeExcludeFilter) Match(s string) bool {
|
|
|
|
if f.include != nil {
|
|
|
|
if !f.include.Match(s) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if f.exclude != nil {
|
|
|
|
if f.exclude.Match(s) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|