e40ab0145f
Signed-off-by: Casey Lee <cplee@nektos.com>
164 lines
3.3 KiB
Go
164 lines
3.3 KiB
Go
package glob
|
|
|
|
import (
|
|
"fmt"
|
|
"path/filepath"
|
|
"regexp"
|
|
"strings"
|
|
|
|
"github.com/mgutz/str"
|
|
)
|
|
|
|
// WatchCriterion is the criteria needed to test if a file
|
|
// matches a pattern.
|
|
type WatchCriterion struct {
|
|
// Root is the root directory to start watching.
|
|
Root string
|
|
// Includes are the regexp for including files
|
|
IncludesRegexp []*regexp.Regexp
|
|
// Excludes are the regexp for excluding files
|
|
ExcludesRegexp []*regexp.Regexp
|
|
Includes []string
|
|
Excludes []string
|
|
}
|
|
|
|
func newWatchCriterion(r string) *WatchCriterion {
|
|
return &WatchCriterion{
|
|
Root: r,
|
|
IncludesRegexp: []*regexp.Regexp{},
|
|
ExcludesRegexp: []*regexp.Regexp{},
|
|
Includes: []string{},
|
|
Excludes: []string{},
|
|
}
|
|
}
|
|
|
|
// WatchCriteria is the set of criterion to watch one or more glob patterns.
|
|
type WatchCriteria struct {
|
|
Items []*WatchCriterion
|
|
}
|
|
|
|
func newWatchCriteria() *WatchCriteria {
|
|
return &WatchCriteria{
|
|
Items: []*WatchCriterion{},
|
|
}
|
|
}
|
|
|
|
func (cr *WatchCriteria) findParent(root string) *WatchCriterion {
|
|
for _, item := range cr.Items {
|
|
if item.Root == root || strings.Contains(item.Root, root) {
|
|
return item
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (cr *WatchCriteria) add(glob string) error {
|
|
var err error
|
|
|
|
if glob == "" || glob == "!" {
|
|
return nil
|
|
}
|
|
|
|
isExclude := strings.HasPrefix(glob, "!")
|
|
if isExclude {
|
|
glob = glob[1:]
|
|
}
|
|
|
|
// determine if the root of pattern already exists
|
|
root := PatternRoot(glob)
|
|
root, err = filepath.Abs(root)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
root = filepath.ToSlash(root)
|
|
cri := cr.findParent(root)
|
|
if cri == nil {
|
|
cri = newWatchCriterion(root)
|
|
cr.Items = append(cr.Items, cri)
|
|
}
|
|
|
|
glob, err = filepath.Abs(glob)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// add glob to {in,ex}cludes
|
|
if isExclude {
|
|
if str.SliceIndexOf(cri.Excludes, glob) < 0 {
|
|
re := Globexp(glob)
|
|
cri.ExcludesRegexp = append(cri.ExcludesRegexp, re)
|
|
cri.Excludes = append(cri.Excludes, glob)
|
|
}
|
|
} else {
|
|
if str.SliceIndexOf(cri.Includes, glob) < 0 {
|
|
re := Globexp(glob)
|
|
cri.IncludesRegexp = append(cri.IncludesRegexp, re)
|
|
cri.Includes = append(cri.Includes, glob)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Roots returns the root paths of all criteria.
|
|
func (cr *WatchCriteria) Roots() []string {
|
|
if cr.Items == nil || len(cr.Items) == 0 {
|
|
return nil
|
|
}
|
|
|
|
roots := make([]string, len(cr.Items))
|
|
for i, it := range cr.Items {
|
|
roots[i] = it.Root
|
|
}
|
|
return roots
|
|
}
|
|
|
|
// Matches determines if pth is matched by internal criteria.
|
|
func (cr *WatchCriteria) Matches(pth string) bool {
|
|
match := false
|
|
pth = filepath.ToSlash(pth)
|
|
for _, it := range cr.Items {
|
|
// if sub path
|
|
if strings.HasPrefix(pth, it.Root) {
|
|
// check if matches an include pattern
|
|
for _, re := range it.IncludesRegexp {
|
|
if re.MatchString(pth) {
|
|
match = true
|
|
break
|
|
}
|
|
}
|
|
// when found, check if it is excluded
|
|
if match {
|
|
for _, re := range it.ExcludesRegexp {
|
|
if re.MatchString(pth) {
|
|
match = false
|
|
break
|
|
}
|
|
}
|
|
if match {
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// EffectiveCriteria is the minimum set of criteria to watch the
|
|
// items in patterns
|
|
func EffectiveCriteria(globs ...string) (*WatchCriteria, error) {
|
|
if len(globs) == 0 {
|
|
return nil, nil
|
|
}
|
|
result := newWatchCriteria()
|
|
for _, glob := range globs {
|
|
err := result.add(glob)
|
|
if err != nil {
|
|
fmt.Println(err.Error())
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
return result, nil
|
|
}
|