package object import ( "io" "github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/plumbing/storer" "github.com/go-git/go-git/v5/utils/ioutil" ) // Blob is used to store arbitrary data - it is generally a file. type Blob struct { // Hash of the blob. Hash plumbing.Hash // Size of the (uncompressed) blob. Size int64 obj plumbing.EncodedObject } // GetBlob gets a blob from an object storer and decodes it. func GetBlob(s storer.EncodedObjectStorer, h plumbing.Hash) (*Blob, error) { o, err := s.EncodedObject(plumbing.BlobObject, h) if err != nil { return nil, err } return DecodeBlob(o) } // DecodeObject decodes an encoded object into a *Blob. func DecodeBlob(o plumbing.EncodedObject) (*Blob, error) { b := &Blob{} if err := b.Decode(o); err != nil { return nil, err } return b, nil } // ID returns the object ID of the blob. The returned value will always match // the current value of Blob.Hash. // // ID is present to fulfill the Object interface. func (b *Blob) ID() plumbing.Hash { return b.Hash } // Type returns the type of object. It always returns plumbing.BlobObject. // // Type is present to fulfill the Object interface. func (b *Blob) Type() plumbing.ObjectType { return plumbing.BlobObject } // Decode transforms a plumbing.EncodedObject into a Blob struct. func (b *Blob) Decode(o plumbing.EncodedObject) error { if o.Type() != plumbing.BlobObject { return ErrUnsupportedObject } b.Hash = o.Hash() b.Size = o.Size() b.obj = o return nil } // Encode transforms a Blob into a plumbing.EncodedObject. func (b *Blob) Encode(o plumbing.EncodedObject) (err error) { o.SetType(plumbing.BlobObject) w, err := o.Writer() if err != nil { return err } defer ioutil.CheckClose(w, &err) r, err := b.Reader() if err != nil { return err } defer ioutil.CheckClose(r, &err) _, err = io.Copy(w, r) return err } // Reader returns a reader allow the access to the content of the blob func (b *Blob) Reader() (io.ReadCloser, error) { return b.obj.Reader() } // BlobIter provides an iterator for a set of blobs. type BlobIter struct { storer.EncodedObjectIter s storer.EncodedObjectStorer } // NewBlobIter takes a storer.EncodedObjectStorer and a // storer.EncodedObjectIter and returns a *BlobIter that iterates over all // blobs contained in the storer.EncodedObjectIter. // // Any non-blob object returned by the storer.EncodedObjectIter is skipped. func NewBlobIter(s storer.EncodedObjectStorer, iter storer.EncodedObjectIter) *BlobIter { return &BlobIter{iter, s} } // Next moves the iterator to the next blob and returns a pointer to it. If // there are no more blobs, it returns io.EOF. func (iter *BlobIter) Next() (*Blob, error) { for { obj, err := iter.EncodedObjectIter.Next() if err != nil { return nil, err } if obj.Type() != plumbing.BlobObject { continue } return DecodeBlob(obj) } } // ForEach call the cb function for each blob contained on this iter until // an error happens or the end of the iter is reached. If ErrStop is sent // the iteration is stop but no error is returned. The iterator is closed. func (iter *BlobIter) ForEach(cb func(*Blob) error) error { return iter.EncodedObjectIter.ForEach(func(obj plumbing.EncodedObject) error { if obj.Type() != plumbing.BlobObject { return nil } b, err := DecodeBlob(obj) if err != nil { return err } return cb(b) }) }