From 9d3d35b3d7ef3dedf037c930af5b0e78b857efc0 Mon Sep 17 00:00:00 2001 From: clawbot Date: Sat, 5 Sep 2026 03:29:37 +0000 Subject: [PATCH] add FileExists and DirExists for checking what is at a path FileExists reports whether a path holds a regular file and DirExists whether it holds a directory, both answering false for anything they cannot look at. Comes with doc comments and table-driven tests. (closes #15) Model: opus-5 --- exists.go | 31 +++++++++++++++++++++++++++ exists_test.go | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+) create mode 100644 exists.go create mode 100644 exists_test.go diff --git a/exists.go b/exists.go new file mode 100644 index 0000000..485d68f --- /dev/null +++ b/exists.go @@ -0,0 +1,31 @@ +package util + +import "os" + +// FileExists reports whether there is a regular file at path. A directory, a +// device node or a socket gives false, and so does anything the process is not +// allowed to look at, since from the caller's point of view there is no usable +// file there either way. +// +// Symbolic links are followed, so a link pointing at a regular file gives true +// and a link pointing at nothing gives false. +func FileExists(path string) bool { + info, err := os.Stat(path) + if err != nil { + return false + } + + return info.Mode().IsRegular() +} + +// DirExists reports whether there is a directory at path. As with FileExists, +// anything that cannot be looked at gives false, and symbolic links are +// followed. +func DirExists(path string) bool { + info, err := os.Stat(path) + if err != nil { + return false + } + + return info.IsDir() +} diff --git a/exists_test.go b/exists_test.go new file mode 100644 index 0000000..9d78262 --- /dev/null +++ b/exists_test.go @@ -0,0 +1,57 @@ +package util + +import ( + "os" + "path/filepath" + "testing" +) + +func TestFileExistsAndDirExists(t *testing.T) { + root := t.TempDir() + + file := filepath.Join(root, "a-file") + if err := os.WriteFile(file, []byte("contents"), 0644); err != nil { + t.Fatalf("could not write the test file: %v", err) + } + + directory := filepath.Join(root, "a-directory") + if err := os.Mkdir(directory, 0755); err != nil { + t.Fatalf("could not make the test directory: %v", err) + } + + linkToFile := filepath.Join(root, "link-to-file") + if err := os.Symlink(file, linkToFile); err != nil { + t.Fatalf("could not make the test link: %v", err) + } + + brokenLink := filepath.Join(root, "broken-link") + if err := os.Symlink(filepath.Join(root, "nothing-here"), brokenLink); err != nil { + t.Fatalf("could not make the broken test link: %v", err) + } + + tests := []struct { + name string + path string + expectedFile bool + expectedIsDir bool + }{ + {"a regular file", file, true, false}, + {"a directory", directory, false, true}, + {"a link to a file", linkToFile, true, false}, + {"a link to nothing", brokenLink, false, false}, + {"a path that is not there", filepath.Join(root, "missing"), false, false}, + {"a path whose parent is not there", filepath.Join(root, "missing", "deeper"), false, false}, + {"the empty path", "", false, false}, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + if got := FileExists(test.path); got != test.expectedFile { + t.Errorf("FileExists: expected %v got %v", test.expectedFile, got) + } + if got := DirExists(test.path); got != test.expectedIsDir { + t.Errorf("DirExists: expected %v got %v", test.expectedIsDir, got) + } + }) + } +} -- 2.49.1