shithub: furgit

Download patch

ref: 84aac13e64962806e5101abc461be50198ac5642
parent: 02efb65bef2e1645809ac16676f218618ea2378b
author: Runxi Yu <runxiyu@umich.edu>
date: Sun Mar 29 11:10:21 EDT 2026

object/fetch: Use proper errors

--- a/object/fetch/exact_blob.go
+++ b/object/fetch/exact_blob.go
@@ -1,11 +1,11 @@
 package fetch
 
 import (
-	"fmt"
-
+	giterrors "codeberg.org/lindenii/furgit/errors"
 	"codeberg.org/lindenii/furgit/object/blob"
 	objectid "codeberg.org/lindenii/furgit/object/id"
 	"codeberg.org/lindenii/furgit/object/stored"
+	objecttype "codeberg.org/lindenii/furgit/object/type"
 )
 
 // ExactBlob reads, parses, and wraps the blob at id.
@@ -19,7 +19,7 @@
 
 	blob, ok := parsed.(*blob.Blob)
 	if !ok {
-		return nil, fmt.Errorf("object/fetch: expected blob object %s, got %v", id, parsed.ObjectType())
+		return nil, &giterrors.ObjectTypeError{OID: id, Got: parsed.ObjectType(), Want: objecttype.TypeBlob}
 	}
 
 	return stored.New(id, blob), nil
--- a/object/fetch/exact_blob_reader.go
+++ b/object/fetch/exact_blob_reader.go
@@ -12,5 +12,5 @@
 //
 // Labels: Life-Parent, Close-Caller.
 func (r *Fetcher) ExactBlobReader(id objectid.ObjectID) (io.ReadCloser, int64, error) {
-	return r.exactReader(id, objecttype.TypeBlob, "blob")
+	return r.exactReader(id, objecttype.TypeBlob)
 }
--- a/object/fetch/exact_commit.go
+++ b/object/fetch/exact_commit.go
@@ -1,11 +1,11 @@
 package fetch
 
 import (
-	"fmt"
-
+	giterrors "codeberg.org/lindenii/furgit/errors"
 	"codeberg.org/lindenii/furgit/object/commit"
 	objectid "codeberg.org/lindenii/furgit/object/id"
 	"codeberg.org/lindenii/furgit/object/stored"
+	objecttype "codeberg.org/lindenii/furgit/object/type"
 )
 
 // ExactCommit reads, parses, and wraps the commit at id.
@@ -19,7 +19,7 @@
 
 	commit, ok := parsed.(*commit.Commit)
 	if !ok {
-		return nil, fmt.Errorf("object/fetch: expected commit object %s, got %v", id, parsed.ObjectType())
+		return nil, &giterrors.ObjectTypeError{OID: id, Got: parsed.ObjectType(), Want: objecttype.TypeCommit}
 	}
 
 	return stored.New(id, commit), nil
--- a/object/fetch/exact_commit_reader.go
+++ b/object/fetch/exact_commit_reader.go
@@ -14,5 +14,5 @@
 //
 // Labels: Life-Parent, Close-Caller.
 func (r *Fetcher) ExactCommitReader(id objectid.ObjectID) (io.ReadCloser, int64, error) {
-	return r.exactReader(id, objecttype.TypeCommit, "commit")
+	return r.exactReader(id, objecttype.TypeCommit)
 }
--- a/object/fetch/exact_reader.go
+++ b/object/fetch/exact_reader.go
@@ -1,9 +1,9 @@
 package fetch
 
 import (
-	"fmt"
 	"io"
 
+	giterrors "codeberg.org/lindenii/furgit/errors"
 	objectid "codeberg.org/lindenii/furgit/object/id"
 	objecttype "codeberg.org/lindenii/furgit/object/type"
 )
@@ -10,16 +10,16 @@
 
 // exactReader reads one object's content stream and verifies that its header
 // type matches wantType.
-func (r *Fetcher) exactReader(id objectid.ObjectID, wantType objecttype.Type, wantName string) (io.ReadCloser, int64, error) {
+func (r *Fetcher) exactReader(id objectid.ObjectID, wantType objecttype.Type) (io.ReadCloser, int64, error) {
 	gotType, size, rc, err := r.store.ReadReaderContent(id)
 	if err != nil {
-		return nil, 0, err
+		return nil, 0, wrapObjectReadError(id, err)
 	}
 
 	if gotType != wantType {
 		_ = rc.Close()
 
-		return nil, 0, fmt.Errorf("object/fetch: expected %s object %s, got %v", wantName, id, gotType)
+		return nil, 0, &giterrors.ObjectTypeError{OID: id, Got: gotType, Want: wantType}
 	}
 
 	return rc, size, nil
--- a/object/fetch/exact_tag.go
+++ b/object/fetch/exact_tag.go
@@ -1,11 +1,11 @@
 package fetch
 
 import (
-	"fmt"
-
+	giterrors "codeberg.org/lindenii/furgit/errors"
 	objectid "codeberg.org/lindenii/furgit/object/id"
 	"codeberg.org/lindenii/furgit/object/stored"
 	"codeberg.org/lindenii/furgit/object/tag"
+	objecttype "codeberg.org/lindenii/furgit/object/type"
 )
 
 // ExactTag reads, parses, and wraps the tag at id.
@@ -19,7 +19,7 @@
 
 	tag, ok := parsed.(*tag.Tag)
 	if !ok {
-		return nil, fmt.Errorf("object/fetch: expected tag object %s, got %v", id, parsed.ObjectType())
+		return nil, &giterrors.ObjectTypeError{OID: id, Got: parsed.ObjectType(), Want: objecttype.TypeTag}
 	}
 
 	return stored.New(id, tag), nil
--- a/object/fetch/exact_tag_reader.go
+++ b/object/fetch/exact_tag_reader.go
@@ -14,5 +14,5 @@
 //
 // Labels: Life-Parent, Close-Caller.
 func (r *Fetcher) ExactTagReader(id objectid.ObjectID) (io.ReadCloser, int64, error) {
-	return r.exactReader(id, objecttype.TypeTag, "tag")
+	return r.exactReader(id, objecttype.TypeTag)
 }
--- a/object/fetch/exact_tree.go
+++ b/object/fetch/exact_tree.go
@@ -1,11 +1,11 @@
 package fetch
 
 import (
-	"fmt"
-
+	giterrors "codeberg.org/lindenii/furgit/errors"
 	objectid "codeberg.org/lindenii/furgit/object/id"
 	"codeberg.org/lindenii/furgit/object/stored"
 	"codeberg.org/lindenii/furgit/object/tree"
+	objecttype "codeberg.org/lindenii/furgit/object/type"
 )
 
 // ExactTree reads, parses, and wraps the tree at id.
@@ -19,7 +19,7 @@
 
 	tree, ok := parsed.(*tree.Tree)
 	if !ok {
-		return nil, fmt.Errorf("object/fetch: expected tree object %s, got %v", id, parsed.ObjectType())
+		return nil, &giterrors.ObjectTypeError{OID: id, Got: parsed.ObjectType(), Want: objecttype.TypeTree}
 	}
 
 	return stored.New(id, tree), nil
--- a/object/fetch/exact_tree_reader.go
+++ b/object/fetch/exact_tree_reader.go
@@ -14,5 +14,5 @@
 //
 // Labels: Life-Parent, Close-Caller.
 func (r *Fetcher) ExactTreeReader(id objectid.ObjectID) (io.ReadCloser, int64, error) {
-	return r.exactReader(id, objecttype.TypeTree, "tree")
+	return r.exactReader(id, objecttype.TypeTree)
 }
--- /dev/null
+++ b/object/fetch/object_errors.go
@@ -1,0 +1,19 @@
+package fetch
+
+import (
+	stderrors "errors"
+
+	giterrors "codeberg.org/lindenii/furgit/errors"
+	objectid "codeberg.org/lindenii/furgit/object/id"
+	objectstore "codeberg.org/lindenii/furgit/object/store"
+)
+
+// wrapObjectReadError maps raw object-store lookup failures to fetcher-level
+// object lookup errors.
+func wrapObjectReadError(id objectid.ObjectID, err error) error {
+	if stderrors.Is(err, objectstore.ErrObjectNotFound) {
+		return &giterrors.ObjectMissingError{OID: id}
+	}
+
+	return err
+}
--- a/object/fetch/object_parse.go
+++ b/object/fetch/object_parse.go
@@ -10,7 +10,7 @@
 func (r *Fetcher) parseObject(id objectid.ObjectID) (object.Object, error) {
 	ty, content, err := r.store.ReadBytesContent(id)
 	if err != nil {
-		return nil, err
+		return nil, wrapObjectReadError(id, err)
 	}
 
 	parsed, err := object.ParseWithoutHeader(ty, content, id.Algorithm())
--- a/object/fetch/peel_to_blob.go
+++ b/object/fetch/peel_to_blob.go
@@ -1,12 +1,12 @@
 package fetch
 
 import (
-	"fmt"
-
+	giterrors "codeberg.org/lindenii/furgit/errors"
 	"codeberg.org/lindenii/furgit/object/blob"
 	objectid "codeberg.org/lindenii/furgit/object/id"
 	"codeberg.org/lindenii/furgit/object/stored"
 	"codeberg.org/lindenii/furgit/object/tag"
+	objecttype "codeberg.org/lindenii/furgit/object/type"
 )
 
 // PeelToBlob peels tags until it reaches a blob.
@@ -25,7 +25,7 @@
 		case *tag.Tag:
 			id = parsed.Target
 		default:
-			return nil, fmt.Errorf("object/fetch: expected blob-ish object %s, got %v", id, parsed.ObjectType())
+			return nil, &giterrors.ObjectTypeError{OID: id, Got: parsed.ObjectType(), Want: objecttype.TypeBlob}
 		}
 	}
 }
--- a/object/fetch/peel_to_blob_id.go
+++ b/object/fetch/peel_to_blob_id.go
@@ -1,8 +1,7 @@
 package fetch
 
 import (
-	"fmt"
-
+	giterrors "codeberg.org/lindenii/furgit/errors"
 	objectid "codeberg.org/lindenii/furgit/object/id"
 	objecttype "codeberg.org/lindenii/furgit/object/type"
 )
@@ -12,7 +11,7 @@
 	for {
 		ty, _, err := r.store.ReadHeader(id)
 		if err != nil {
-			return objectid.ObjectID{}, err
+			return objectid.ObjectID{}, wrapObjectReadError(id, err)
 		}
 
 		switch ty {
@@ -31,9 +30,9 @@
 			objecttype.TypeFuture,
 			objecttype.TypeOfsDelta,
 			objecttype.TypeRefDelta:
-			return objectid.ObjectID{}, fmt.Errorf("object/fetch: expected blob-ish object %s, got %v", id, ty)
+			return objectid.ObjectID{}, &giterrors.ObjectTypeError{OID: id, Got: ty, Want: objecttype.TypeBlob}
 		default:
-			return objectid.ObjectID{}, fmt.Errorf("object/fetch: expected blob-ish object %s, got %v", id, ty)
+			return objectid.ObjectID{}, &giterrors.ObjectTypeError{OID: id, Got: ty, Want: objecttype.TypeBlob}
 		}
 	}
 }
--- a/object/fetch/peel_to_commit.go
+++ b/object/fetch/peel_to_commit.go
@@ -1,12 +1,12 @@
 package fetch
 
 import (
-	"fmt"
-
+	giterrors "codeberg.org/lindenii/furgit/errors"
 	"codeberg.org/lindenii/furgit/object/commit"
 	objectid "codeberg.org/lindenii/furgit/object/id"
 	"codeberg.org/lindenii/furgit/object/stored"
 	"codeberg.org/lindenii/furgit/object/tag"
+	objecttype "codeberg.org/lindenii/furgit/object/type"
 )
 
 // PeelToCommit peels tags until it reaches a commit.
@@ -25,7 +25,7 @@
 		case *tag.Tag:
 			id = parsed.Target
 		default:
-			return nil, fmt.Errorf("object/fetch: expected commit-ish object %s, got %v", id, parsed.ObjectType())
+			return nil, &giterrors.ObjectTypeError{OID: id, Got: parsed.ObjectType(), Want: objecttype.TypeCommit}
 		}
 	}
 }
--- a/object/fetch/peel_to_commit_id.go
+++ b/object/fetch/peel_to_commit_id.go
@@ -1,8 +1,7 @@
 package fetch
 
 import (
-	"fmt"
-
+	giterrors "codeberg.org/lindenii/furgit/errors"
 	objectid "codeberg.org/lindenii/furgit/object/id"
 	objecttype "codeberg.org/lindenii/furgit/object/type"
 )
@@ -12,7 +11,7 @@
 	for {
 		ty, _, err := r.store.ReadHeader(id)
 		if err != nil {
-			return objectid.ObjectID{}, err
+			return objectid.ObjectID{}, wrapObjectReadError(id, err)
 		}
 
 		switch ty {
@@ -31,9 +30,9 @@
 			objecttype.TypeFuture,
 			objecttype.TypeOfsDelta,
 			objecttype.TypeRefDelta:
-			return objectid.ObjectID{}, fmt.Errorf("object/fetch: expected commit-ish object %s, got %v", id, ty)
+			return objectid.ObjectID{}, &giterrors.ObjectTypeError{OID: id, Got: ty, Want: objecttype.TypeCommit}
 		default:
-			return objectid.ObjectID{}, fmt.Errorf("object/fetch: expected commit-ish object %s, got %v", id, ty)
+			return objectid.ObjectID{}, &giterrors.ObjectTypeError{OID: id, Got: ty, Want: objecttype.TypeCommit}
 		}
 	}
 }
--- a/object/fetch/peel_to_tree.go
+++ b/object/fetch/peel_to_tree.go
@@ -1,13 +1,13 @@
 package fetch
 
 import (
-	"fmt"
-
+	giterrors "codeberg.org/lindenii/furgit/errors"
 	"codeberg.org/lindenii/furgit/object/commit"
 	objectid "codeberg.org/lindenii/furgit/object/id"
 	"codeberg.org/lindenii/furgit/object/stored"
 	"codeberg.org/lindenii/furgit/object/tag"
 	"codeberg.org/lindenii/furgit/object/tree"
+	objecttype "codeberg.org/lindenii/furgit/object/type"
 )
 
 // PeelToTree peels tags until it reaches a tree or commit. If it reaches a
@@ -29,7 +29,7 @@
 		case *tag.Tag:
 			id = parsed.Target
 		default:
-			return nil, fmt.Errorf("object/fetch: expected tree-ish object %s, got %v", id, parsed.ObjectType())
+			return nil, &giterrors.ObjectTypeError{OID: id, Got: parsed.ObjectType(), Want: objecttype.TypeTree}
 		}
 	}
 }
--- a/object/fetch/peel_to_tree_id.go
+++ b/object/fetch/peel_to_tree_id.go
@@ -1,8 +1,7 @@
 package fetch
 
 import (
-	"fmt"
-
+	giterrors "codeberg.org/lindenii/furgit/errors"
 	objectid "codeberg.org/lindenii/furgit/object/id"
 	objecttype "codeberg.org/lindenii/furgit/object/type"
 )
@@ -13,7 +12,7 @@
 	for {
 		ty, _, err := r.store.ReadHeader(id)
 		if err != nil {
-			return objectid.ObjectID{}, err
+			return objectid.ObjectID{}, wrapObjectReadError(id, err)
 		}
 
 		switch ty {
@@ -38,9 +37,9 @@
 			objecttype.TypeFuture,
 			objecttype.TypeOfsDelta,
 			objecttype.TypeRefDelta:
-			return objectid.ObjectID{}, fmt.Errorf("object/fetch: expected tree-ish object %s, got %v", id, ty)
+			return objectid.ObjectID{}, &giterrors.ObjectTypeError{OID: id, Got: ty, Want: objecttype.TypeTree}
 		default:
-			return objectid.ObjectID{}, fmt.Errorf("object/fetch: expected tree-ish object %s, got %v", id, ty)
+			return objectid.ObjectID{}, &giterrors.ObjectTypeError{OID: id, Got: ty, Want: objecttype.TypeTree}
 		}
 	}
 }
--