1package object 2 3import ( 4 "io" 5 6 "github.com/go-git/go-git/v5/plumbing" 7 "github.com/go-git/go-git/v5/plumbing/storer" 8 "github.com/go-git/go-git/v5/utils/ioutil" 9) 10 11// Blob is used to store arbitrary data - it is generally a file. 12type Blob struct { 13 // Hash of the blob. 14 Hash plumbing.Hash 15 // Size of the (uncompressed) blob. 16 Size int64 17 18 obj plumbing.EncodedObject 19} 20 21// GetBlob gets a blob from an object storer and decodes it. 22func GetBlob(s storer.EncodedObjectStorer, h plumbing.Hash) (*Blob, error) { 23 o, err := s.EncodedObject(plumbing.BlobObject, h) 24 if err != nil { 25 return nil, err 26 } 27 28 return DecodeBlob(o) 29} 30 31// DecodeObject decodes an encoded object into a *Blob. 32func DecodeBlob(o plumbing.EncodedObject) (*Blob, error) { 33 b := &Blob{} 34 if err := b.Decode(o); err != nil { 35 return nil, err 36 } 37 38 return b, nil 39} 40 41// ID returns the object ID of the blob. The returned value will always match 42// the current value of Blob.Hash. 43// 44// ID is present to fulfill the Object interface. 45func (b *Blob) ID() plumbing.Hash { 46 return b.Hash 47} 48 49// Type returns the type of object. It always returns plumbing.BlobObject. 50// 51// Type is present to fulfill the Object interface. 52func (b *Blob) Type() plumbing.ObjectType { 53 return plumbing.BlobObject 54} 55 56// Decode transforms a plumbing.EncodedObject into a Blob struct. 57func (b *Blob) Decode(o plumbing.EncodedObject) error { 58 if o.Type() != plumbing.BlobObject { 59 return ErrUnsupportedObject 60 } 61 62 b.Hash = o.Hash() 63 b.Size = o.Size() 64 b.obj = o 65 66 return nil 67} 68 69// Encode transforms a Blob into a plumbing.EncodedObject. 70func (b *Blob) Encode(o plumbing.EncodedObject) (err error) { 71 o.SetType(plumbing.BlobObject) 72 73 w, err := o.Writer() 74 if err != nil { 75 return err 76 } 77 78 defer ioutil.CheckClose(w, &err) 79 80 r, err := b.Reader() 81 if err != nil { 82 return err 83 } 84 85 defer ioutil.CheckClose(r, &err) 86 87 _, err = io.Copy(w, r) 88 return err 89} 90 91// Reader returns a reader allow the access to the content of the blob 92func (b *Blob) Reader() (io.ReadCloser, error) { 93 return b.obj.Reader() 94} 95 96// BlobIter provides an iterator for a set of blobs. 97type BlobIter struct { 98 storer.EncodedObjectIter 99 s storer.EncodedObjectStorer 100} 101 102// NewBlobIter takes a storer.EncodedObjectStorer and a 103// storer.EncodedObjectIter and returns a *BlobIter that iterates over all 104// blobs contained in the storer.EncodedObjectIter. 105// 106// Any non-blob object returned by the storer.EncodedObjectIter is skipped. 107func NewBlobIter(s storer.EncodedObjectStorer, iter storer.EncodedObjectIter) *BlobIter { 108 return &BlobIter{iter, s} 109} 110 111// Next moves the iterator to the next blob and returns a pointer to it. If 112// there are no more blobs, it returns io.EOF. 113func (iter *BlobIter) Next() (*Blob, error) { 114 for { 115 obj, err := iter.EncodedObjectIter.Next() 116 if err != nil { 117 return nil, err 118 } 119 120 if obj.Type() != plumbing.BlobObject { 121 continue 122 } 123 124 return DecodeBlob(obj) 125 } 126} 127 128// ForEach call the cb function for each blob contained on this iter until 129// an error happens or the end of the iter is reached. If ErrStop is sent 130// the iteration is stop but no error is returned. The iterator is closed. 131func (iter *BlobIter) ForEach(cb func(*Blob) error) error { 132 return iter.EncodedObjectIter.ForEach(func(obj plumbing.EncodedObject) error { 133 if obj.Type() != plumbing.BlobObject { 134 return nil 135 } 136 137 b, err := DecodeBlob(obj) 138 if err != nil { 139 return err 140 } 141 142 return cb(b) 143 }) 144} 145