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