ref: dfae3821e05fc2376d57c11f4b5ae18de1750cbd
dir: /refstore/chain/resolve.go/
package chain
import (
"errors"
"fmt"
"codeberg.org/lindenii/furgit/ref"
"codeberg.org/lindenii/furgit/refstore"
)
// Resolve resolves a reference from the first backend that has it.
func (chain *Chain) Resolve(name string) (ref.Ref, error) {
for i, backend := range chain.backends {
if backend == nil {
continue
}
resolved, err := backend.Resolve(name)
if err == nil {
return resolved, nil
}
if errors.Is(err, refstore.ErrReferenceNotFound) {
continue
}
return nil, fmt.Errorf("refstore: backend %d resolve: %w", i, err)
}
return nil, refstore.ErrReferenceNotFound
}
// ResolveFully resolves symbolic references through Resolve until detached.
//
// It intentionally does not call backend ResolveFully. This allows symbolic
// references to cross backends in the chain.
func (chain *Chain) ResolveFully(name string) (ref.Detached, error) {
cur := name
seen := map[string]struct{}{}
for {
if _, ok := seen[cur]; ok {
return ref.Detached{}, fmt.Errorf("refstore: symbolic reference cycle at %q", cur)
}
seen[cur] = struct{}{}
resolved, err := chain.Resolve(cur)
if err != nil {
return ref.Detached{}, err
}
switch resolved := resolved.(type) {
case ref.Detached:
return resolved, nil
case ref.Symbolic:
if resolved.Target == "" {
return ref.Detached{}, fmt.Errorf("refstore: symbolic reference %q has empty target", resolved.Name())
}
cur = resolved.Target
default:
return ref.Detached{}, fmt.Errorf("refstore: unsupported reference type %T", resolved)
}
}
}