// Package core implement the core interfaces and structs used by go-git
package core
import (
"errors"
"io"
)
var (
ErrObjectNotFound = errors.New("object not found")
// ErrInvalidType is returned when an invalid object type is provided.
ErrInvalidType = errors.New("invalid object type")
)
// TODO: Consider adding a Hash function to the ObjectReader and ObjectWriter
// interfaces that returns the hash calculated for the reader or writer.
// ObjectReader is a generic representation of an object reader.
//
// ObjectReader implements io.ReadCloser. Close should be called when finished
// with it.
type ObjectReader io.ReadCloser
// ObjectWriter is a generic representation of an object writer.
//
// ObjectWriter implements io.WriterCloser. Close should be called when finished
// with it.
type ObjectWriter io.WriteCloser
// Object is a generic representation of any git object
type Object interface {
Hash() Hash
Type() ObjectType
SetType(ObjectType)
Size() int64
SetSize(int64)
Reader() (ObjectReader, error)
Writer() (ObjectWriter, error)
}
// ObjectType internal object type
// Integer values from 0 to 7 map to those exposed by git.
// AnyObject is used to represent any from 0 to 7.
type ObjectType int8
const (
InvalidObject ObjectType = 0
CommitObject ObjectType = 1
TreeObject ObjectType = 2
BlobObject ObjectType = 3
TagObject ObjectType = 4
// 5 reserved for future expansion
OFSDeltaObject ObjectType = 6
REFDeltaObject ObjectType = 7
AnyObject ObjectType = -127
)
func (t ObjectType) String() string {
switch t {
case CommitObject:
return "commit"
case TreeObject:
return "tree"
case BlobObject:
return "blob"
case TagObject:
return "tag"
case OFSDeltaObject:
return "ofs-delta"
case REFDeltaObject:
return "ref-delta"
case AnyObject:
return "any"
default:
return "unknown"
}
}
func (t ObjectType) Bytes() []byte {
return []byte(t.String())
}
// Valid returns true if t is a valid ObjectType.
func (t ObjectType) Valid() bool {
return t >= CommitObject && t <= REFDeltaObject
}
// ParseObjectType parses a string representation of ObjectType. It returns an
// error on parse failure.
func ParseObjectType(value string) (typ ObjectType, err error) {
switch value {
case "commit":
typ = CommitObject
case "tree":
typ = TreeObject
case "blob":
typ = BlobObject
case "tag":
typ = TagObject
case "ofs-delta":
typ = OFSDeltaObject
case "ref-delta":
typ = REFDeltaObject
default:
err = ErrInvalidType
}
return
}
// 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 ObjectStorage
series []Hash
t ObjectType
pos int
}
// NewObjectLookupIter returns an object iterator given an object storage and
// a slice of object hashes.
func NewObjectLookupIter(storage ObjectStorage, t ObjectType, series []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 ErrObjectNotFound as an error. If the
// object is retreieved successfully error will be nil.
func (iter *ObjectLookupIter) Next() (Object, error) {
if iter.pos >= len(iter.series) {
return nil, io.EOF
}
hash := iter.series[iter.pos]
obj, err := iter.storage.Get(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(Object) error) error {
defer iter.Close()
for _, hash := range iter.series {
obj, err := iter.storage.Get(AnyObject, hash)
if err != nil {
return err
}
if err := cb(obj); err != nil {
if err == ErrStop {
return nil
}
return nil
}
}
return nil
}
// 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 []Object
pos int
}
// NewObjectSliceIter returns an object iterator for the given slice of objects.
func NewObjectSliceIter(series []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() (Object, error) {
if iter.pos >= len(iter.series) {
return nil, io.EOF
}
obj := iter.series[iter.pos]
iter.pos++
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(Object) error) error {
defer iter.Close()
for _, o := range iter.series {
if err := cb(o); err != nil {
if err == ErrStop {
return nil
}
return err
}
}
return nil
}
// Close releases any resources used by the iterator.
func (iter *ObjectSliceIter) Close() {
iter.pos = len(iter.series)
}
// 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() (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(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 nil
}
}
}
// Close releases any resources used by the iterator.
func (iter *MultiObjectIter) Close() {
for _, i := range iter.iters {
i.Close()
}
}