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
}
}
}