| package storer |
| |
| import ( |
| "errors" |
| "io" |
| |
| "gopkg.in/src-d/go-git.v4/plumbing" |
| ) |
| |
| const MaxResolveRecursion = 1024 |
| |
| // ErrMaxResolveRecursion is returned by ResolveReference is MaxResolveRecursion |
| // is exceeded |
| var ErrMaxResolveRecursion = errors.New("max. recursion level reached") |
| |
| // ReferenceStorer is a generic storage of references. |
| type ReferenceStorer interface { |
| SetReference(*plumbing.Reference) error |
| // CheckAndSetReference sets the reference `new`, but if `old` is |
| // not `nil`, it first checks that the current stored value for |
| // `old.Name()` matches the given reference value in `old`. If |
| // not, it returns an error and doesn't update `new`. |
| CheckAndSetReference(new, old *plumbing.Reference) error |
| Reference(plumbing.ReferenceName) (*plumbing.Reference, error) |
| IterReferences() (ReferenceIter, error) |
| RemoveReference(plumbing.ReferenceName) error |
| CountLooseRefs() (int, error) |
| PackRefs() error |
| } |
| |
| // ReferenceIter is a generic closable interface for iterating over references. |
| type ReferenceIter interface { |
| Next() (*plumbing.Reference, error) |
| ForEach(func(*plumbing.Reference) error) error |
| Close() |
| } |
| |
| type referenceFilteredIter struct { |
| ff func(r *plumbing.Reference) bool |
| iter ReferenceIter |
| } |
| |
| // NewReferenceFilteredIter returns a reference iterator for the given reference |
| // Iterator. This iterator will iterate only references that accomplish the |
| // provided function. |
| func NewReferenceFilteredIter( |
| ff func(r *plumbing.Reference) bool, iter ReferenceIter) ReferenceIter { |
| return &referenceFilteredIter{ff, iter} |
| } |
| |
| // Next returns the next reference from the iterator. If the iterator has reached |
| // the end it will return io.EOF as an error. |
| func (iter *referenceFilteredIter) Next() (*plumbing.Reference, error) { |
| for { |
| r, err := iter.iter.Next() |
| if err != nil { |
| return nil, err |
| } |
| |
| if iter.ff(r) { |
| return r, nil |
| } |
| |
| continue |
| } |
| } |
| |
| // ForEach call the cb function for each reference contained on this iter until |
| // an error happens or the end of the iter is reached. If ErrStop is sent |
| // the iteration is stopped but no error is returned. The iterator is closed. |
| func (iter *referenceFilteredIter) ForEach(cb func(*plumbing.Reference) error) error { |
| defer iter.Close() |
| for { |
| r, err := iter.Next() |
| if err == io.EOF { |
| break |
| } |
| if err != nil { |
| return err |
| } |
| |
| if err := cb(r); err != nil { |
| if err == ErrStop { |
| break |
| } |
| |
| return err |
| } |
| } |
| |
| return nil |
| } |
| |
| // Close releases any resources used by the iterator. |
| func (iter *referenceFilteredIter) Close() { |
| iter.iter.Close() |
| } |
| |
| // ReferenceSliceIter implements ReferenceIter. It iterates over a series of |
| // references stored in a slice and yields each one in turn when Next() is |
| // called. |
| // |
| // The ReferenceSliceIter must be closed with a call to Close() when it is no |
| // longer needed. |
| type ReferenceSliceIter struct { |
| series []*plumbing.Reference |
| pos int |
| } |
| |
| // NewReferenceSliceIter returns a reference iterator for the given slice of |
| // objects. |
| func NewReferenceSliceIter(series []*plumbing.Reference) ReferenceIter { |
| return &ReferenceSliceIter{ |
| series: series, |
| } |
| } |
| |
| // Next returns the next reference from the iterator. If the iterator has |
| // reached the end it will return io.EOF as an error. |
| func (iter *ReferenceSliceIter) Next() (*plumbing.Reference, 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 reference 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 *ReferenceSliceIter) ForEach(cb func(*plumbing.Reference) error) error { |
| return forEachReferenceIter(iter, cb) |
| } |
| |
| type bareReferenceIterator interface { |
| Next() (*plumbing.Reference, error) |
| Close() |
| } |
| |
| func forEachReferenceIter(iter bareReferenceIterator, cb func(*plumbing.Reference) 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 |
| } |
| } |
| } |
| |
| // Close releases any resources used by the iterator. |
| func (iter *ReferenceSliceIter) Close() { |
| iter.pos = len(iter.series) |
| } |
| |
| // MultiReferenceIter implements ReferenceIter. It iterates over several |
| // ReferenceIter, |
| // |
| // The MultiReferenceIter must be closed with a call to Close() when it is no |
| // longer needed. |
| type MultiReferenceIter struct { |
| iters []ReferenceIter |
| } |
| |
| // NewMultiReferenceIter returns an reference iterator for the given slice of |
| // EncodedObjectIters. |
| func NewMultiReferenceIter(iters []ReferenceIter) ReferenceIter { |
| return &MultiReferenceIter{iters: iters} |
| } |
| |
| // Next returns the next reference from the iterator, if one iterator reach |
| // io.EOF is removed and the next one is used. |
| func (iter *MultiReferenceIter) Next() (*plumbing.Reference, 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 reference 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 *MultiReferenceIter) ForEach(cb func(*plumbing.Reference) error) error { |
| return forEachReferenceIter(iter, cb) |
| } |
| |
| // Close releases any resources used by the iterator. |
| func (iter *MultiReferenceIter) Close() { |
| for _, i := range iter.iters { |
| i.Close() |
| } |
| } |
| |
| // ResolveReference resolves a SymbolicReference to a HashReference. |
| func ResolveReference(s ReferenceStorer, n plumbing.ReferenceName) (*plumbing.Reference, error) { |
| r, err := s.Reference(n) |
| if err != nil || r == nil { |
| return r, err |
| } |
| return resolveReference(s, r, 0) |
| } |
| |
| func resolveReference(s ReferenceStorer, r *plumbing.Reference, recursion int) (*plumbing.Reference, error) { |
| if r.Type() != plumbing.SymbolicReference { |
| return r, nil |
| } |
| |
| if recursion > MaxResolveRecursion { |
| return nil, ErrMaxResolveRecursion |
| } |
| |
| t, err := s.Reference(r.Target()) |
| if err != nil { |
| return nil, err |
| } |
| |
| recursion++ |
| return resolveReference(s, t, recursion) |
| } |