From 736091aa54938b32fcb77e2ee6d6a4e1cf1d533a Mon Sep 17 00:00:00 2001 From: Taylor Monacelli Date: Tue, 30 Jul 2024 13:13:11 -0700 Subject: [PATCH] Check file file can't be processed --- cmd/watchDir.go | 20 ++++++++- file/type.go | 59 +++++++++++++++++++++++++ file/type_test.go | 104 +++++++++++++++++++++++++++++++++++++++++++++ watcher/watcher.go | 42 +++++++++++------- 4 files changed, 208 insertions(+), 17 deletions(-) create mode 100644 file/type.go create mode 100644 file/type_test.go diff --git a/cmd/watchDir.go b/cmd/watchDir.go index a9704ba..f0f440c 100644 --- a/cmd/watchDir.go +++ b/cmd/watchDir.go @@ -1,6 +1,8 @@ package cmd import ( + "os" + "github.com/gkwa/littlewill/watcher" "github.com/spf13/cobra" ) @@ -21,7 +23,23 @@ Cobra is a CLI library for Go that empowers applications. This application is a tool to generate the needed files to quickly create a Cobra application.`, Run: func(cmd *cobra.Command, args []string) { - watcher.RunWatcher(cmd, args, patterns, filterType, linkTransforms) + if len(args) == 0 { + err := cmd.Usage() + if err != nil { + cmd.PrintErrf("Error: %v\n", err) + } + cmd.PrintErrln("Error: directory path is required") + os.Exit(1) + } + + dir := args[0] + watcher.RunWatcher( + cmd.Context(), + dir, + patterns, + filterType, + linkTransforms, + ) }, } diff --git a/file/type.go b/file/type.go new file mode 100644 index 0000000..b513392 --- /dev/null +++ b/file/type.go @@ -0,0 +1,59 @@ +package file + +import ( + "fmt" + "os" + "path/filepath" + "strings" +) + +type File struct { + Path string +} + +func (f File) IsSymlink() (bool, error) { + fileInfo, err := os.Lstat(f.Path) + if err != nil { + return false, err + } + return fileInfo.Mode()&os.ModeSymlink != 0, nil +} + +func (f File) FileType() string { + // Get file info without following symlinks + info, err := os.Lstat(f.Path) + if err != nil { + return "Error: Unable to get file info" + } + + // Check if it's a symlink + if info.Mode()&os.ModeSymlink != 0 { + // If it's a symlink, we can optionally get the target + target, err := os.Readlink(f.Path) + if err != nil { + return "Symlink (unable to read target)" + } + return fmt.Sprintf("Symlink to %s", target) + } + + // If it's not a symlink, proceed with file type detection + if info.IsDir() { + return "Directory" + } + + ext := strings.ToLower(filepath.Ext(f.Path)) + switch ext { + case ".txt": + return "Text File" + case ".go": + return "Go Source File" + case ".jpg", ".jpeg": + return "JPEG Image" + case ".png": + return "PNG Image" + case ".pdf": + return "PDF Document" + default: + return "Unknown File Type" + } +} diff --git a/file/type_test.go b/file/type_test.go new file mode 100644 index 0000000..fdae030 --- /dev/null +++ b/file/type_test.go @@ -0,0 +1,104 @@ +package file + +import ( + "os" + "path/filepath" + "strings" + "testing" +) + +func TestFileType(t *testing.T) { + // Create a temporary directory for our test files + tmpDir, err := os.MkdirTemp("", "filetest") + if err != nil { + t.Fatalf("Failed to create temp dir: %v", err) + } + defer os.RemoveAll(tmpDir) + + // Test cases + tests := []struct { + name string + setup func() (string, error) + expected string + }{ + { + name: "Text File", + setup: func() (string, error) { + path := filepath.Join(tmpDir, "test.txt") + err := os.WriteFile(path, []byte("test"), 0o644) + return path, err + }, + expected: "Text File", + }, + { + name: "Go Source File", + setup: func() (string, error) { + path := filepath.Join(tmpDir, "test.go") + err := os.WriteFile(path, []byte("package main"), 0o644) + return path, err + }, + expected: "Go Source File", + }, + { + name: "JPEG Image", + setup: func() (string, error) { + path := filepath.Join(tmpDir, "test.jpg") + err := os.WriteFile(path, []byte("fake jpg"), 0o644) + return path, err + }, + expected: "JPEG Image", + }, + { + name: "Directory", + setup: func() (string, error) { + path := filepath.Join(tmpDir, "testdir") + err := os.Mkdir(path, 0o755) + return path, err + }, + expected: "Directory", + }, + { + name: "Symlink", + setup: func() (string, error) { + target := filepath.Join(tmpDir, "target.txt") + err := os.WriteFile(target, []byte("target"), 0o644) + if err != nil { + return "", err + } + link := filepath.Join(tmpDir, "symlink") + err = os.Symlink(target, link) + return link, err + }, + expected: "Symlink to ", // We'll check if it starts with this + }, + { + name: "Unknown File Type", + setup: func() (string, error) { + path := filepath.Join(tmpDir, "test.xyz") + err := os.WriteFile(path, []byte("unknown"), 0o644) + return path, err + }, + expected: "Unknown File Type", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + path, err := tt.setup() + if err != nil { + t.Fatalf("Setup failed: %v", err) + } + + file := File{Path: path} + result := file.FileType() + + if tt.name == "Symlink" { + if !strings.HasPrefix(result, tt.expected) { + t.Errorf("FileType() = %v, want prefix %v", result, tt.expected) + } + } else if result != tt.expected { + t.Errorf("FileType() = %v, want %v", result, tt.expected) + } + }) + } +} diff --git a/watcher/watcher.go b/watcher/watcher.go index b26bf90..45a146c 100644 --- a/watcher/watcher.go +++ b/watcher/watcher.go @@ -10,42 +10,52 @@ import ( "github.com/fsnotify/fsnotify" "github.com/gkwa/littlewill/core" + "github.com/gkwa/littlewill/file" "github.com/go-logr/logr" - "github.com/spf13/cobra" ) var ignoredEvents = []fsnotify.Op{ fsnotify.Chmod, fsnotify.Remove, + fsnotify.Rename, } type EventHandler func(event fsnotify.Event, path string) -func RunWatcher(cmd *cobra.Command, args []string, patterns []string, filterType string, linkTransforms []func(io.Reader, io.Writer) error) { - logger := logr.FromContextOrDiscard(cmd.Context()) - - if len(args) == 0 { - err := cmd.Usage() - if err != nil { - cmd.PrintErrf("Error: %v\n", err) - } - cmd.PrintErrln("Error: directory path is required") - os.Exit(1) - } +func RunWatcher( + ctx context.Context, + dirToWatch string, + patterns []string, + filterType string, + linkTransforms []func(io.Reader, io.Writer) error, +) { + logger := logr.FromContextOrDiscard(ctx) - dirToWatch := args[0] - ctx := cmd.Context() handler := func(event fsnotify.Event, path string) { time.Sleep(100 * time.Millisecond) fmt.Printf("Event: %s, File: %s\n", event.Op, path) - err := core.ProcessFile(logger, path, linkTransforms...) + + f := file.File{Path: path} + + isSymlink, err := f.IsSymlink() + if err != nil { + logger.Error(err, "Failed to check if path is symlink", "path", path) + } + logger.V(1).Info("file type check", "path", path, "type", f.FileType()) + + if isSymlink { + logger.V(1).Info("skipping symlink", "path", path) + return + } + + err = core.ProcessFile(logger, path, linkTransforms...) if err != nil { logger.Error(err, "Failed to process file", "path", path) } } err := Run(ctx, dirToWatch, patterns, filterType, handler) if err != nil { - cmd.PrintErrf("Error: %v\n", err) + fmt.Fprintf(os.Stderr, "error: %v\n", err) os.Exit(1) } }