package filter import ( "strings" "github.com/gobwas/glob" ) type Filter interface { Match(string) bool } // Compile takes a list of string filters and returns a Filter interface // for matching a given string against the filter list. The filter list // supports glob matching too, ie: // // f, _ := Compile([]string{"cpu", "mem", "net*"}) // f.Match("cpu") // true // f.Match("network") // true // f.Match("memory") // false // func Compile(filters []string) (Filter, error) { // 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 } 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 }