aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Runxi Yu2026-06-07 12:36:30 +0000
committerGravatar Runxi Yu2026-06-07 12:36:30 +0000
commit8a1c175d2c903ac27eecd5ba07c59e59005f589a (patch)
treecff453f4549bdd60e7bb8e5c72ccf53032ed9689
parentREFACTOR: blob, tag, and tree are done! (diff)
signatureNo signature
object/tree: Add Find
-rw-r--r--object/tree/insert.go19
-rw-r--r--object/tree/lookup.go25
-rw-r--r--object/tree/lookup_test.go41
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")
+ }
+ })
+ }
+}