aboutsummaryrefslogtreecommitdiff
path: root/object/fetch
diff options
context:
space:
mode:
authorGravatar Runxi Yu2026-06-08 07:03:38 +0000
committerGravatar Runxi Yu2026-06-08 07:03:38 +0000
commitca96826ecc39c463f43039fc78d362fde97ce20c (patch)
tree35df2332e6cc53594f84ac3fc2b4eca7f5e81881 /object/fetch
parentobject/fetch: Begin by adding package doc (diff)
object/fetch: Finish blob
Diffstat (limited to 'object/fetch')
-rw-r--r--object/fetch/blob.go97
1 files changed, 97 insertions, 0 deletions
diff --git a/object/fetch/blob.go b/object/fetch/blob.go
new file mode 100644
index 00000000..ee5bae1c
--- /dev/null
+++ b/object/fetch/blob.go
@@ -0,0 +1,97 @@
+package fetch
+
+import (
+ "io"
+
+ "lindenii.org/go/furgit/errs"
+ "lindenii.org/go/furgit/object/blob"
+ oid "lindenii.org/go/furgit/object/id"
+ "lindenii.org/go/furgit/object/stored"
+ "lindenii.org/go/furgit/object/tag"
+ "lindenii.org/go/furgit/object/typ"
+)
+
+// ExactBlob reads, parses, and wraps the blob at id.
+//
+// Labels: Life-Parent.
+func (r *Fetcher) ExactBlob(id oid.ObjectID) (*stored.Stored[*blob.Blob], error) {
+ parsed, err := r.parseObject(id)
+ if err != nil {
+ return nil, err
+ }
+
+ blob, ok := parsed.(*blob.Blob)
+ if !ok {
+ return nil, &errs.ObjectTypeError{OID: id, Got: parsed.ObjectType(), Want: typ.TypeBlob}
+ }
+
+ return stored.New(id, blob), nil
+}
+
+// ExactBlobReader returns a reader for the content of the blob at id,
+// together with its content size in bytes.
+//
+// Labels: Life-Parent, Close-Caller.
+func (r *Fetcher) ExactBlobReader(id oid.ObjectID) (io.ReadCloser, int64, error) {
+ return r.exactReader(id, typ.TypeBlob)
+}
+
+// PeelToBlob peels tags until it reaches a blob.
+//
+// Labels: Life-Parent.
+func (r *Fetcher) PeelToBlob(id oid.ObjectID) (*stored.Stored[*blob.Blob], error) {
+ for {
+ obj, err := r.ExactObject(id)
+ if err != nil {
+ return nil, err
+ }
+
+ switch parsed := obj.Object().(type) {
+ case *blob.Blob:
+ return stored.New(id, parsed), nil
+ case *tag.Tag:
+ id = parsed.TargetID
+ default:
+ return nil, &errs.ObjectTypeError{OID: id, Got: parsed.ObjectType(), Want: typ.TypeBlob}
+ }
+ }
+}
+
+// PeelToBlobID peels tags until it reaches a blob object ID.
+func (r *Fetcher) PeelToBlobID(id oid.ObjectID) (oid.ObjectID, error) {
+ for {
+ ty, _, err := r.Header(id)
+ if err != nil {
+ return oid.ObjectID{}, err
+ }
+
+ switch ty {
+ case typ.TypeBlob:
+ return id, nil
+ case typ.TypeTag:
+ tag, err := r.ExactTag(id)
+ if err != nil {
+ return oid.ObjectID{}, err
+ }
+
+ id = tag.Object().TargetID
+ case typ.TypeUnknown, typ.TypeCommit, typ.TypeTree:
+ return oid.ObjectID{}, &errs.ObjectTypeError{OID: id, Got: ty, Want: typ.TypeBlob}
+ default:
+ return oid.ObjectID{}, &errs.ObjectTypeError{OID: id, Got: ty, Want: typ.TypeBlob}
+ }
+ }
+}
+
+// PeelToBlobReader returns a reader for the content of the peeled blob at id,
+// together with its content size in bytes.
+//
+// Labels: Life-Parent, Close-Caller.
+func (r *Fetcher) PeelToBlobReader(id oid.ObjectID) (io.ReadCloser, int64, error) {
+ blobID, err := r.PeelToBlobID(id)
+ if err != nil {
+ return nil, 0, err
+ }
+
+ return r.ExactBlobReader(blobID)
+}