From dfb617c1f903754efcba98f6036483832171aab9 Mon Sep 17 00:00:00 2001
From: Vyacheslav Pryimak <vyacheslavpryimak@gmail.com>
Date: Sat, 25 Nov 2023 19:10:03 +0200
Subject: [PATCH] Find unformatted files in dir (Part 1) (#141)

---
 reviser/dir.go         | 51 +++++++++++++++++++++++++----
 reviser/dir_test.go    | 73 +++++++++++++++++++++++++++++++++++++++++-
 reviser/file_option.go |  4 ++-
 3 files changed, 119 insertions(+), 9 deletions(-)

diff --git a/reviser/dir.go b/reviser/dir.go
index 8653fa3..a967492 100644
--- a/reviser/dir.go
+++ b/reviser/dir.go
@@ -12,6 +12,8 @@ import (
 	"golang.org/x/exp/slices"
 )
 
+type walkCallbackFunc = func(hasChanged bool, path string, content []byte) error
+
 const (
 	goExtension   = ".go"
 	recursivePath = "./..."
@@ -75,7 +77,19 @@ func (d *SourceDir) Fix(options ...SourceFileOption) error {
 	if !ok {
 		return ErrPathIsNotDir
 	}
-	err := filepath.WalkDir(d.dir, d.walk(options...))
+	err := filepath.WalkDir(d.dir, d.walk(
+		func(hasChanged bool, path string, content []byte) error {
+			if !hasChanged {
+				return nil
+			}
+			if err := os.WriteFile(path, content, 0o644); err != nil {
+				log.Fatalf("failed to write fixed result to file(%s): %+v\n", path, err)
+				return err
+			}
+			return nil
+		},
+		options...,
+	))
 	if err != nil {
 		return fmt.Errorf("failed to walk dif: %w", err)
 	}
@@ -83,7 +97,34 @@ func (d *SourceDir) Fix(options ...SourceFileOption) error {
 	return nil
 }
 
-func (d *SourceDir) walk(options ...SourceFileOption) fs.WalkDirFunc {
+// Find collection of bad formatted paths
+func (d *SourceDir) Find(options ...SourceFileOption) ([]string, error) {
+	var (
+		ok                     bool
+		badFormattedCollection []string
+	)
+	d.dir, ok = IsDir(d.dir)
+	if !ok {
+		return nil, ErrPathIsNotDir
+	}
+	err := filepath.WalkDir(d.dir, d.walk(
+		func(hasChanged bool, path string, content []byte) error {
+			if !hasChanged {
+				return nil
+			}
+			badFormattedCollection = append(badFormattedCollection, path)
+			return nil
+		},
+		options...,
+	))
+	if err != nil {
+		return nil, fmt.Errorf("failed to walk dif: %w", err)
+	}
+
+	return badFormattedCollection, nil
+}
+
+func (d *SourceDir) walk(callback walkCallbackFunc, options ...SourceFileOption) fs.WalkDirFunc {
 	return func(path string, dirEntry fs.DirEntry, err error) error {
 		if !d.isRecursive && dirEntry.IsDir() && filepath.Base(d.dir) != dirEntry.Name() {
 			return filepath.SkipDir
@@ -96,11 +137,7 @@ func (d *SourceDir) walk(options ...SourceFileOption) fs.WalkDirFunc {
 			if err != nil {
 				return fmt.Errorf("failed to fix: %w", err)
 			}
-			if hasChange {
-				if err := os.WriteFile(path, content, 0o644); err != nil {
-					log.Fatalf("failed to write fixed result to file(%s): %+v\n", path, err)
-				}
-			}
+			return callback(hasChange, path, content)
 		}
 		return nil
 	}
diff --git a/reviser/dir_test.go b/reviser/dir_test.go
index 1356309..a2d265a 100644
--- a/reviser/dir_test.go
+++ b/reviser/dir_test.go
@@ -96,7 +96,6 @@ func main() {
 	}
 	for _, test := range tests {
 		t.Run(test.name, func(tt *testing.T) {
-
 			exec(tt, func(ttt *testing.T) error {
 				// executing SourceDir.Fix
 				err := NewSourceDir(test.args.project, test.args.path, true, test.args.excludes).Fix()
@@ -163,3 +162,75 @@ func TestSourceDir_IsExcluded(t *testing.T) {
 		})
 	}
 }
+
+func TestSourceDir_Find(t *testing.T) {
+	testFile := "testdata/dir/dir1/file1.go"
+
+	originContent := `package dir1
+import (
+	"strings"
+
+	"fmt"
+)
+func main() {
+	fmt.Println(strings.ToLower("Hello World!"))
+}
+`
+	exec := func(tt *testing.T, fn func(*testing.T) error) {
+		// create test file
+		err := os.MkdirAll(filepath.Dir(testFile), os.ModePerm)
+		assert.NoError(tt, err)
+		err = os.WriteFile(testFile, []byte(originContent), os.ModePerm)
+		assert.NoError(tt, err)
+
+		// exec test func
+		err = fn(tt)
+		assert.NoError(tt, err)
+
+		// remove test file
+		err = os.Remove(testFile)
+		assert.NoError(tt, err)
+	}
+	var sortedContent string
+	exec(t, func(tt *testing.T) error {
+		sortedData, changed, err := NewSourceFile("testdata", testFile).Fix()
+		assert.NoError(tt, err)
+		sortedContent = string(sortedData)
+		assert.Equal(tt, true, changed)
+		assert.NotEqual(tt, originContent, sortedContent)
+		return nil
+	})
+
+	type args struct {
+		project  string
+		path     string
+		excludes string
+	}
+	tests := []struct {
+		name string
+		args args
+		want []string
+	}{
+		{
+			name: "found-unformatted",
+			args: args{project: "testdata", path: "testdata/dir", excludes: "dir1" + sep + "test.go"},
+			want: []string{"testdata/dir/dir1/file1.go"},
+		},
+	}
+	for _, test := range tests {
+		t.Run(test.name, func(tt *testing.T) {
+			exec(tt, func(ttt *testing.T) error {
+				files, err := NewSourceDir(test.args.project, test.args.path, true, test.args.excludes).Find()
+				assert.NoError(tt, err)
+				rootPath, err := os.Getwd()
+				assert.NoError(tt, err)
+				var want []string
+				for _, w := range test.want {
+					want = append(want, rootPath+"/"+w)
+				}
+				assert.Equal(tt, want, files)
+				return nil
+			})
+		})
+	}
+}
diff --git a/reviser/file_option.go b/reviser/file_option.go
index 7480d2e..b5eff6d 100644
--- a/reviser/file_option.go
+++ b/reviser/file_option.go
@@ -1,6 +1,8 @@
 package reviser
 
-import "strings"
+import (
+	"strings"
+)
 
 // SourceFileOption is an int alias for options
 type SourceFileOption func(f *SourceFile) error