Skip to content

Commit

Permalink
Diversify filters to extend API usage.
Browse files Browse the repository at this point in the history
Signed-off-by: Vaibhav <[email protected]>
  • Loading branch information
vrongmeal committed Apr 21, 2020
1 parent bdb90f6 commit 25d9447
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 53 deletions.
10 changes: 9 additions & 1 deletion cmd/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -238,8 +238,16 @@ func runEngine(conf *leaf.Config) error {
ExitOnError: false,
})

fc, err := leaf.NewFCFromPatterns(
conf.Filters,
leaf.StandardFilterMatcher,
leaf.StandardFilterHandler)
if err != nil {
log.Fatalf("error creating filters: %v", err)
}

watcher, err := leaf.NewWatcher(
conf.Root, conf.Exclude, conf.Filters)
conf.Root, conf.Exclude, fc)
if err != nil {
log.Fatalf("error creating watcher: %v", err)
}
Expand Down
115 changes: 83 additions & 32 deletions filters.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import (

// Filter can be used to Filter out watch results.
type Filter struct {
include bool
pattern string
Include bool // whether to include pattern
Pattern string
}

// NewFilter creates a filter from the pattern string. The
Expand All @@ -27,16 +27,16 @@ func NewFilter(pattern string) (Filter, error) {

toInclude := cleanedPattern[0]
if toInclude == '+' {
f.include = true
f.Include = true
} else if toInclude == '-' {
f.include = false
f.Include = false
} else {
return f, fmt.Errorf(
"should have first character as '+' or '-'")
}

onlyPath := strings.Trim(cleanedPattern[1:], " ")
f.pattern, err = filepath.Abs(onlyPath)
f.Pattern, err = filepath.Abs(onlyPath)
if err != nil {
return f, fmt.Errorf(
"error making path absolute: %v", err)
Expand All @@ -47,43 +47,64 @@ func NewFilter(pattern string) (Filter, error) {

// A FilterCollection contains a bunch of includes and excludes.
type FilterCollection struct {
Includes []Filter
Excludes []Filter
Includes []string
Excludes []string

match FilterMatchFunc
handle FilterHandleFunc
}

// NewFilterCollection creates a filter collection from a bunch
// of filter patterns.
func NewFilterCollection(patterns []string) (*FilterCollection, error) {
func NewFilterCollection(filters []Filter, mf FilterMatchFunc, hf FilterHandleFunc) *FilterCollection {
collection := &FilterCollection{
Includes: []Filter{},
Excludes: []Filter{},
Includes: []string{},
Excludes: []string{},
match: mf,
handle: hf,
}

if len(patterns) == 0 {
return collection, nil
if len(filters) == 0 {
return collection
}

for _, pattern := range patterns {
f, err := NewFilter(pattern)
if err != nil {
return nil, fmt.Errorf(
"error in '%s' pattern: %v", pattern, err)
for _, f := range filters {
if f.Include {
collection.Includes = append(collection.Includes, f.Pattern)
} else {
collection.Excludes = append(collection.Excludes, f.Pattern)
}
}

if f.include {
collection.Includes = append(collection.Includes, f)
} else {
collection.Excludes = append(collection.Excludes, f)
return collection
}

// NewFCFromPatterns creates a filter collection from a list of
// string format filters, like `+ /path/to/some/dir`.
func NewFCFromPatterns(patterns []string, mf FilterMatchFunc, hf FilterHandleFunc) (*FilterCollection, error) {
filters := []Filter{}

for _, p := range patterns {
f, err := NewFilter(p)
if err != nil {
return nil, err
}

filters = append(filters, f)
}

return collection, nil
return NewFilterCollection(filters, mf, hf), nil
}

// match matches the pattern with the path and returns true
// if the path either starts with (in absolute terms) or
// matches like the path regex.
func match(pattern, path string) bool {
// FilterMatchFunc compares the pattern with the path of
// the file changed and returns true if the path resembles
// the given pattern.
type FilterMatchFunc func(pattern, path string) bool

// StandardFilterMatcher matches the pattern with the path
// and returns true if the path either starts with
// (in absolute terms) or matches like the path regex.
func StandardFilterMatcher(pattern, path string) bool {
matched, err := filepath.Match(pattern, path)
if err != nil {
return false
Expand Down Expand Up @@ -111,11 +132,11 @@ func match(pattern, path string) bool {

// HasInclude tells if the collection matches the path with
// one of its includes.
func (c *FilterCollection) HasInclude(path string) bool {
func (fc *FilterCollection) HasInclude(path string) bool {
cleanedPath := filepath.Clean(path)

for _, f := range c.Includes {
if match(f.pattern, cleanedPath) {
for _, pattern := range fc.Includes {
if fc.match(pattern, cleanedPath) {
return true
}
}
Expand All @@ -125,14 +146,44 @@ func (c *FilterCollection) HasInclude(path string) bool {

// HasExclude tells if the collection matches the path with
// one of its excludes.
func (c *FilterCollection) HasExclude(path string) bool {
func (fc *FilterCollection) HasExclude(path string) bool {
cleanedPath := filepath.Clean(path)

for _, f := range c.Excludes {
if match(f.pattern, cleanedPath) {
for _, pattern := range fc.Excludes {
if fc.match(pattern, cleanedPath) {
return true
}
}

return false
}

// ShouldHandlePath returns the result of the path handler
// for the filter collection.
func (fc *FilterCollection) ShouldHandlePath(path string) bool {
handlerFunc := fc.handle
return handlerFunc(fc, path)
}

// FilterHandleFunc is a function that checks if for the filter
// collection, should the path be handled or not, i.e., should
// the notifier tick for change in path or not.
type FilterHandleFunc func(fc *FilterCollection, path string) bool

// StandardFilterHandler returns true if the path should be included
// and returns false if path should not be included in result.
func StandardFilterHandler(fc *FilterCollection, path string) bool {
handle := false

// If there are no includes, path should be handled unless
// it is in the excludes.
if len(fc.Includes) == 0 || fc.HasInclude(path) {
handle = true
}

if fc.HasExclude(path) {
handle = false
}

return handle
}
28 changes: 8 additions & 20 deletions watcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,16 +31,20 @@ type Watcher struct {
}

// NewWatcher returns a watcher from the given options.
func NewWatcher(root string, exclude, filters []string) (*Watcher, error) {
w := &Watcher{}
func NewWatcher(root string, exclude []string, fc *FilterCollection) (*Watcher, error) {
w := &Watcher{
fc: fc,
res: make(chan WatchResult),
}

isdir, err := isDir(root)
if err != nil {
return nil, err
}

if !isdir {
return nil, fmt.Errorf("path '%s' is not a directory", root)
return nil, fmt.Errorf(
"path '%s' is not a directory", root)
}

w.root = filepath.Clean(root)
Expand All @@ -65,11 +69,6 @@ func NewWatcher(root string, exclude, filters []string) (*Watcher, error) {
w.exclude = append(w.exclude, absPath)
}

w.fc, err = NewFilterCollection(filters)
if err != nil {
return nil, err
}

w.notifier, err = fsnotify.NewWatcher()
if err != nil {
return nil, err
Expand All @@ -93,8 +92,6 @@ func NewWatcher(root string, exclude, filters []string) (*Watcher, error) {
}
}

w.res = make(chan WatchResult)

return w, nil
}

Expand All @@ -114,16 +111,7 @@ func (w *Watcher) startWatcher(ctx context.Context) {
case event := <-w.notifier.Events:
if event.Op == fsnotify.Write {
file := event.Name
handle := false
if len(w.fc.Includes) == 0 || w.fc.HasInclude(file) {
handle = true
}

if w.fc.HasExclude(file) {
handle = false
}

if handle {
if w.fc.ShouldHandlePath(file) {
w.res <- WatchResult{File: file}
}
}
Expand Down

0 comments on commit 25d9447

Please sign in to comment.