diff options
| author | 2026-06-07 12:36:30 +0000 | |
|---|---|---|
| committer | 2026-06-07 12:36:30 +0000 | |
| commit | 8a1c175d2c903ac27eecd5ba07c59e59005f589a (patch) | |
| tree | cff453f4549bdd60e7bb8e5c72ccf53032ed9689 | |
| parent | REFACTOR: blob, tag, and tree are done! (diff) | |
| signature | No signature | |
object/tree: Add Find
| -rw-r--r-- | object/tree/insert.go | 19 | ||||
| -rw-r--r-- | object/tree/lookup.go | 25 | ||||
| -rw-r--r-- | object/tree/lookup_test.go | 41 |
3 files changed, 67 insertions, 18 deletions
diff --git a/object/tree/insert.go b/object/tree/insert.go index b5227938..5e519069 100644 --- a/object/tree/insert.go +++ b/object/tree/insert.go @@ -26,7 +26,7 @@ func (tree *Tree) Insert(entry Entry) error { return fmt.Errorf("%w: entry %q has invalid mode", ErrInvalidTree, entry.Name) } - if _, found := tree.find(entry.Name); found { + if _, found := tree.Find(entry.Name); found { return fmt.Errorf("%w: entry %q already exists", ErrInvalidTree, entry.Name) } @@ -41,23 +41,6 @@ func (tree *Tree) Insert(entry Entry) error { return nil } -// find returns the index of the entry with the given name, if present. -// -// A name conflicts whether stored as a blob-like or as a subtree entry, -// so both orderings are searched. -func (tree *Tree) find(name string) (int, bool) { - for _, searchIsTree := range [...]bool{true, false} { - index, ok := slices.BinarySearchFunc(tree.entries, name, func(existing Entry, target string) int { - return nameCompare(existing.Name, existing.Mode == mode.Directory, target, searchIsTree) - }) - if ok && tree.entries[index].Name == name { - return index, true - } - } - - return 0, false -} - // validateName checks that name is a structurally valid tree entry name. func validateName(name string) error { if name == "" { diff --git a/object/tree/lookup.go b/object/tree/lookup.go new file mode 100644 index 00000000..34a01748 --- /dev/null +++ b/object/tree/lookup.go @@ -0,0 +1,25 @@ +package tree + +import ( + "slices" + + "lindenii.org/go/furgit/object/tree/mode" +) + +// Find returns the entry with the given name, if present. +// +// A name matches whether stored as a blob-like or as a subtree entry, +// so both orderings are searched. +// The returned entry is a copy; mutating it does not affect the tree. +func (tree *Tree) Find(name string) (Entry, bool) { + for _, searchIsTree := range [...]bool{true, false} { + index, ok := slices.BinarySearchFunc(tree.entries, name, func(existing Entry, target string) int { + return nameCompare(existing.Name, existing.Mode == mode.Directory, target, searchIsTree) + }) + if ok && tree.entries[index].Name == name { + return tree.entries[index], true + } + } + + return Entry{}, false +} diff --git a/object/tree/lookup_test.go b/object/tree/lookup_test.go new file mode 100644 index 00000000..22d73615 --- /dev/null +++ b/object/tree/lookup_test.go @@ -0,0 +1,41 @@ +package tree_test + +import ( + "testing" + + "lindenii.org/go/furgit/internal/testgit" + "lindenii.org/go/furgit/object/id" +) + +func TestFind(t *testing.T) { + t.Parallel() + + for _, objectFormat := range id.SupportedObjectFormats() { + t.Run(objectFormat.String(), func(t *testing.T) { + t.Parallel() + + repo, err := testgit.NewRepo(t, testgit.RepoOptions{ObjectFormat: objectFormat}) + if err != nil { + t.Fatalf("NewRepo: %v", err) + } + + entries := mixedEntries(t, repo) + tr := buildTree(t, entries) + + for _, want := range entries { + got, ok := tr.Find(want.Name) + if !ok { + t.Fatalf("Find(%q) not found", want.Name) + } + + if got.Mode != want.Mode || got.Name != want.Name || got.ID != want.ID { + t.Fatalf("Find(%q) = %+v, want %+v", want.Name, got, want) + } + } + + if _, ok := tr.Find("does-not-exist"); ok { + t.Fatalf("Find(does-not-exist) = true, want false") + } + }) + } +} |
