From f2ba84d5fa228caed171af7850ee6776cc252076 Mon Sep 17 00:00:00 2001 From: LTLA Date: Thu, 17 Oct 2024 20:56:19 -0700 Subject: [PATCH] Support manual skipping of subdirectories during indexing. This provides an easy mechanism for excluding subdirectories from the search. The intended use is to allow users to ignore older versions of a resource by just creating a .SewerRatignore file in those versions' subdirectories. --- README.md | 1 + list.go | 8 ++++++- list_test.go | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 69 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index ccb89e9..6a94543 100644 --- a/README.md +++ b/README.md @@ -77,6 +77,7 @@ Subdirectories with names starting with `.` are skipped during the recursive wal This is generally a sensible choice as these directories usually do not contain any interesting (scientific) information. If any such subdirectory is relevant, a user can force SewerRat to include it in the index by passing its path directly as `path`. This is because leading dots are allowed in the components of the supplied `path`, just not in its subdirectories. +Conversely, a user can force SewerRat to skip a particular subdirectory by placing a (possibly empty) `.SewerRatignore` file inside it. Symbolic links in the specified directory are treated differently depending on their target. If the directory contains symbolic links to files, the contents of the target files can be indexed as long as the link has one of the `base` names. diff --git a/list.go b/list.go index 1b66a43..c0a6771 100644 --- a/list.go +++ b/list.go @@ -6,6 +6,7 @@ import ( "os" "path/filepath" "strings" + "errors" ) func listFiles(dir string, recursive bool) ([]string, error) { @@ -63,8 +64,13 @@ func listMetadata(dir string, base_names []string) (map[string]fs.FileInfo, []st base := filepath.Base(path) if strings.HasPrefix(base, ".") { return fs.SkipDir - } else { + } + + _, err := os.Lstat(filepath.Join(path, ".SewerRatignore")) + if err != nil && errors.Is(err, fs.ErrNotExist) { return nil + } else { + return fs.SkipDir } } diff --git a/list_test.go b/list_test.go index bb13b1b..80ca70b 100644 --- a/list_test.go +++ b/list_test.go @@ -282,3 +282,64 @@ func TestListMetadataDot(t *testing.T) { } }) } + +func TestListMetadataIgnored(t *testing.T) { + dir, err := os.MkdirTemp("", "") + if (err != nil) { + t.Fatalf("failed to create a temporary directory; %v", err) + } + + path := filepath.Join(dir, "A.json") + err = os.WriteFile(path, []byte(""), 0644) + if err != nil { + t.Fatalf("failed to create a mock file; %v", err) + } + + // Throwing in a hidden directory. + subdir := filepath.Join(dir, "super") + err = os.Mkdir(subdir, 0755) + if err != nil { + t.Fatalf("failed to create a temporary subdirectory; %v", err) + } + + subpath1 := filepath.Join(subdir, "A.json") + err = os.WriteFile(subpath1, []byte(""), 0644) + if err != nil { + t.Fatalf("failed to create a mock file; %v", err) + } + + t.Run("control", func(t *testing.T) { + found, fails, err := listMetadata(dir, []string{ "A.json" }) + if err != nil { + t.Fatal(err) + } + if len(fails) > 0 { + t.Fatal("unexpected failures") + } + if len(found) != 2 { + t.Fatal("expected exactly two files") + } + }) + + err = os.WriteFile(filepath.Join(subdir, ".SewerRatignore"), []byte{}, 0644) + if err != nil { + t.Fatalf("failed to create the ignore file; %v", err) + } + + t.Run("ignored", func(t *testing.T) { + found, fails, err := listMetadata(dir, []string{ "A.json" }) + if err != nil { + t.Fatal(err) + } + if len(fails) > 0 { + t.Fatal("unexpected failures") + } + if len(found) != 1 { + t.Fatal("expected exactly one file") + } + _, ok := found[filepath.Join(dir, "A.json")] + if !ok { + t.Fatal("missing file") + } + }) +}