Skip to content

Commit

Permalink
Optimized shouldRestart() (#24)
Browse files Browse the repository at this point in the history
  • Loading branch information
vanodevium authored May 28, 2022
1 parent 5348a15 commit ffddac7
Show file tree
Hide file tree
Showing 2 changed files with 120 additions and 27 deletions.
63 changes: 36 additions & 27 deletions gow.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,9 +95,9 @@ var (
FLAG_SEP = FLAG_SET.String("S", "", "")
SEP []byte

EXTENSIONS = &flagStrings{validateExtension, []string{"go", "mod"}}
IGNORED_PATHS = &flagStrings{validatePath, nil}
WATCH = &flagStrings{validatePath, DEFAULT_WATCH}
EXTENSIONS = &flagStrings{validateExtension, decorateExtension, []string{"go", "mod"}}
IGNORED_PATHS = &flagStrings{validatePath, decorateIgnore, nil}
WATCH = &flagStrings{validatePath, nil, DEFAULT_WATCH}
DEFAULT_WATCH = []string{`.`}

log = l.New(os.Stderr, "[gow] ", 0)
Expand Down Expand Up @@ -128,6 +128,9 @@ func main() {

SEP = []byte(unescapedLine(*FLAG_SEP))

EXTENSIONS.Prepare()
IGNORED_PATHS.Prepare()

// Everything needed for cleanup must be registered here.
var termios *unix.Termios
var cmd *exec.Cmd
Expand Down Expand Up @@ -492,46 +495,25 @@ func allowByIgnoredPaths(absPath string) (bool, error) {
return true, nil
}

cwd, err := os.Getwd()
if err != nil {
return false, fmt.Errorf(`failed to get working directory: %w`, err)
}

for _, ignored := range IGNORED_PATHS.values {
ignoredAbsPath := filepath.Join(cwd, ignored)
if hasBasePath(absPath, ignoredAbsPath) {
if hasBasePath(absPath, ignored) {
return false, nil
}
}

return true, nil
}

var pathSepStr = string(filepath.Separator)

// Assumes both paths are relative or both are absolute. Doesn't care to support
// scheme-qualified paths such as network paths.
func hasBasePath(longerPath string, basePath string) bool {
longer := strings.Split(filepath.Clean(longerPath), pathSepStr)
base := strings.Split(filepath.Clean(basePath), pathSepStr)

if len(base) > len(longer) {
return false
}

for i := 0; i < len(base); i++ {
if base[i] != longer[i] {
return false
}
}

return true
return strings.HasPrefix(longerPath, basePath)
}

func allowByExtensions(path string) bool {
ext := filepath.Ext(path)
// Note: `filepath.Ext` includes a dot prefix, which we have to slice off.
return ext != "" && stringsInclude(EXTENSIONS.values, ext[1:])
return ext != "" && stringsInclude(EXTENSIONS.values, ext)
}

func stringsInclude(list []string, val string) bool {
Expand All @@ -545,6 +527,7 @@ func stringsInclude(list []string, val string) bool {

type flagStrings struct {
validate func(string) error
decorate func(string) string
values []string
}

Expand All @@ -569,6 +552,19 @@ func (self *flagStrings) Set(input string) error {
return nil
}

func (self *flagStrings) Prepare() {

var values []string
for _, val := range self.values {
if self.decorate != nil {
val = self.decorate(val)
}
values = append(values, val)
}

self.values = values
}

func validateExtension(val string) error {
if wordRegexp.MatchString(val) {
return nil
Expand All @@ -589,6 +585,19 @@ func validatePath(val string) error {
)
}

func decorateExtension(val string) string {
return fmt.Sprintf(".%s", val)
}

func decorateIgnore(val string) string {
if !filepath.IsAbs(val) {
cwd, _ := os.Getwd()
val = filepath.Join(cwd, val)
}

return filepath.Clean(val)
}

var pathRegexp = regexp.MustCompile(`^[\w. /\\-]+$`)

func clearTerminal() {
Expand Down
84 changes: 84 additions & 0 deletions gow_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package main

import (
"github.com/rjeczalik/notify"
"os"
"path/filepath"
"testing"
)

type TestFsEvent struct {
path string
}

func (e *TestFsEvent) Event() notify.Event {
return 0
}

func (e *TestFsEvent) Path() string {
return e.path
}

func (e *TestFsEvent) Sys() interface{} {
return nil
}

func BenchmarkShouldRestart(b *testing.B) {
EXTENSIONS = &flagStrings{validateExtension, decorateExtension, []string{"ext1", "ext2", "ext3"}}
IGNORED_PATHS = &flagStrings{validatePath, decorateIgnore, []string{"./ignore1", "ignore2", "ignore3"}}

EXTENSIONS.Prepare()
IGNORED_PATHS.Prepare()

cwd, _ := os.Getwd()
event := &TestFsEvent{path: filepath.Join(cwd, "ignore3/file.ext3")}

for i := 0; i < b.N; i++ {
s, _ := shouldRestart(event)
if s {
b.Fatal("shouldRestart() is broken")
}
}
}

func TestShouldRestart(t *testing.T) {

type shouldRestartCase struct {
path string
ignore []string
extensions []string
expected bool
}

cases := []shouldRestartCase{
{path: "file.go", extensions: []string{"mod", "go"}, ignore: []string{}, expected: true},
{path: "to/file.go", extensions: []string{"mod", "go"}, ignore: []string{}, expected: true},
{path: "to/file", extensions: []string{"mod", "go"}, ignore: []string{}, expected: false},
{path: "to/file.txt", extensions: []string{"mod", "go"}, ignore: []string{}, expected: false},
{path: "to/file.go.txt", extensions: []string{"mod", "go"}, ignore: []string{}, expected: false},
{path: "to/file.go", extensions: []string{"mod", "go"}, ignore: []string{"to"}, expected: false},
{path: "to/file.go", extensions: []string{"mod", "go"}, ignore: []string{"yo", "to"}, expected: false},
{path: "to/file.go", extensions: []string{"mod", "go"}, ignore: []string{"yo", "./to/"}, expected: false},
{path: "to/file.go", extensions: []string{"mod", "go"}, ignore: []string{"file"}, expected: true},
{path: "to/file.go", extensions: []string{"mod", "go"}, ignore: []string{}, expected: true},
{path: ".hidden/file.go", extensions: []string{"mod", "go"}, ignore: []string{}, expected: true},
{path: ".hidden/ignore/file.go", extensions: []string{"mod", "go"}, ignore: []string{".hidden/ignore"}, expected: false},
{path: ".hidden/no/file.go", extensions: []string{"mod", "go"}, ignore: []string{".hidden/ignore"}, expected: true},
}

cwd, _ := os.Getwd()

for _, testCase := range cases {
EXTENSIONS = &flagStrings{validateExtension, decorateExtension, testCase.extensions}
IGNORED_PATHS = &flagStrings{validatePath, decorateIgnore, testCase.ignore}

EXTENSIONS.Prepare()
IGNORED_PATHS.Prepare()

should, _ := shouldRestart(&TestFsEvent{path: filepath.Join(cwd, testCase.path)})

if testCase.expected != should {
t.Error(testCase.expected, should, testCase)
}
}
}

0 comments on commit ffddac7

Please sign in to comment.