diff options
Diffstat (limited to 'plumbing/storer/object.go')
-rw-r--r-- | plumbing/storer/object.go | 241 |
1 files changed, 241 insertions, 0 deletions
diff --git a/plumbing/storer/object.go b/plumbing/storer/object.go new file mode 100644 index 0000000..c7841b6 --- /dev/null +++ b/plumbing/storer/object.go @@ -0,0 +1,241 @@ +package storer + +import ( + "errors" + "io" + + "gopkg.in/src-d/go-git.v4/plumbing" +) + +var ( + //ErrStop is used to stop a ForEach function in an Iter + ErrStop = errors.New("stop iter") +) + +// ObjectStorer generic storage of objects +type ObjectStorer interface { + // NewObject returns a new plumbing.Object, the real type of the object can + // be a custom implementation or the defaul one, plumbing.MemoryObject + NewObject() plumbing.Object + // SetObject save an object into the storage, the object shuld be create + // with the NewObject, method, and file if the type is not supported. + SetObject(plumbing.Object) (plumbing.Hash, error) + // Object get an object by hash with the given plumbing.ObjectType. + // Implementors should return (nil, plumbing.ErrObjectNotFound) if an object + // doesn't exist with both the given hash and object type. + // + // Valid plumbing.ObjectType values are CommitObject, BlobObject, TagObject, + // TreeObject and AnyObject. If plumbing.AnyObject is given, the object must + // be looked up regardless of its type. + Object(plumbing.ObjectType, plumbing.Hash) (plumbing.Object, error) + // IterObjects returns a custom ObjectIter over all the object on the + // storage. + // + // Valid plumbing.ObjectType values are CommitObject, BlobObject, TagObject, + IterObjects(plumbing.ObjectType) (ObjectIter, error) +} + +// Transactioner is a optional method for ObjectStorer, it enable transaction +// base write and read operations in the storage +type Transactioner interface { + // Begin starts a transaction. + Begin() Transaction +} + +// PackfileWriter is a optional method for ObjectStorer, it enable direct write +// of packfile to the storage +type PackfileWriter interface { + // PackfileWriter retuns a writer for writing a packfile to the storage + // + // If the Storer not implements PackfileWriter the objects should be written + // using the Set method. + PackfileWriter() (io.WriteCloser, error) +} + +// ObjectIter is a generic closable interface for iterating over objects. +type ObjectIter interface { + Next() (plumbing.Object, error) + ForEach(func(plumbing.Object) error) error + Close() +} + +// Transaction is an in-progress storage transaction. A transaction must end +// with a call to Commit or Rollback. +type Transaction interface { + SetObject(plumbing.Object) (plumbing.Hash, error) + Object(plumbing.ObjectType, plumbing.Hash) (plumbing.Object, error) + Commit() error + Rollback() error +} + +// ObjectLookupIter implements ObjectIter. It iterates over a series of object +// hashes and yields their associated objects by retrieving each one from +// object storage. The retrievals are lazy and only occur when the iterator +// moves forward with a call to Next(). +// +// The ObjectLookupIter must be closed with a call to Close() when it is no +// longer needed. +type ObjectLookupIter struct { + storage ObjectStorer + series []plumbing.Hash + t plumbing.ObjectType + pos int +} + +// NewObjectLookupIter returns an object iterator given an object storage and +// a slice of object hashes. +func NewObjectLookupIter( + storage ObjectStorer, t plumbing.ObjectType, series []plumbing.Hash) *ObjectLookupIter { + return &ObjectLookupIter{ + storage: storage, + series: series, + t: t, + } +} + +// Next returns the next object from the iterator. If the iterator has reached +// the end it will return io.EOF as an error. If the object can't be found in +// the object storage, it will return plumbing.ErrObjectNotFound as an error. +// If the object is retreieved successfully error will be nil. +func (iter *ObjectLookupIter) Next() (plumbing.Object, error) { + if iter.pos >= len(iter.series) { + return nil, io.EOF + } + + hash := iter.series[iter.pos] + obj, err := iter.storage.Object(iter.t, hash) + if err == nil { + iter.pos++ + } + + return obj, err +} + +// ForEach call the cb function for each object contained on this iter until +// an error happends 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 *ObjectLookupIter) ForEach(cb func(plumbing.Object) error) error { + return ForEachIterator(iter, cb) +} + +// Close releases any resources used by the iterator. +func (iter *ObjectLookupIter) Close() { + iter.pos = len(iter.series) +} + +// ObjectSliceIter implements ObjectIter. It iterates over a series of objects +// stored in a slice and yields each one in turn when Next() is called. +// +// The ObjectSliceIter must be closed with a call to Close() when it is no +// longer needed. +type ObjectSliceIter struct { + series []plumbing.Object + pos int +} + +// NewObjectSliceIter returns an object iterator for the given slice of objects. +func NewObjectSliceIter(series []plumbing.Object) *ObjectSliceIter { + return &ObjectSliceIter{ + series: series, + } +} + +// Next returns the next object from the iterator. If the iterator has reached +// the end it will return io.EOF as an error. If the object is retreieved +// successfully error will be nil. +func (iter *ObjectSliceIter) Next() (plumbing.Object, error) { + if len(iter.series) == 0 { + return nil, io.EOF + } + + obj := iter.series[0] + iter.series = iter.series[1:] + + return obj, nil +} + +// ForEach call the cb function for each object contained on this iter until +// an error happends 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 *ObjectSliceIter) ForEach(cb func(plumbing.Object) error) error { + return ForEachIterator(iter, cb) +} + +// Close releases any resources used by the iterator. +func (iter *ObjectSliceIter) Close() { + iter.series = []plumbing.Object{} +} + +// MultiObjectIter implements ObjectIter. It iterates over several ObjectIter, +// +// The MultiObjectIter must be closed with a call to Close() when it is no +// longer needed. +type MultiObjectIter struct { + iters []ObjectIter + pos int +} + +// NewMultiObjectIter returns an object iterator for the given slice of objects. +func NewMultiObjectIter(iters []ObjectIter) ObjectIter { + return &MultiObjectIter{iters: iters} +} + +// Next returns the next object from the iterator, if one iterator reach io.EOF +// is removed and the next one is used. +func (iter *MultiObjectIter) Next() (plumbing.Object, error) { + if len(iter.iters) == 0 { + return nil, io.EOF + } + + obj, err := iter.iters[0].Next() + if err == io.EOF { + iter.iters[0].Close() + iter.iters = iter.iters[1:] + return iter.Next() + } + + return obj, err +} + +// ForEach call the cb function for each object contained on this iter until +// an error happends 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 *MultiObjectIter) ForEach(cb func(plumbing.Object) error) error { + return ForEachIterator(iter, cb) +} + +// Close releases any resources used by the iterator. +func (iter *MultiObjectIter) Close() { + for _, i := range iter.iters { + i.Close() + } +} + +type bareIterator interface { + Next() (plumbing.Object, error) + Close() +} + +// ForEachIterator is a helper function to build iterators without need to +// rewrite the same ForEach function each time. +func ForEachIterator(iter bareIterator, cb func(plumbing.Object) error) error { + defer iter.Close() + for { + obj, err := iter.Next() + if err != nil { + if err == io.EOF { + return nil + } + + return err + } + + if err := cb(obj); err != nil { + if err == ErrStop { + return nil + } + + return err + } + } +} |