github.com/qri-io/qri@v0.10.1-0.20220104210721-c771715036cb/dsref/resolve.go (about)

     1  package dsref
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  )
     7  
     8  var (
     9  	// ErrRefNotFound must be returned by a ref resolver that cannot resolve a
    10  	// given reference
    11  	ErrRefNotFound = errors.New("reference not found")
    12  	// ErrPathRequired should be returned by functions that require a reference
    13  	// have a path value, but got none.
    14  	// ErrPathRequired should *not* be returned by implentations of the
    15  	// ResolveRef interface
    16  	ErrPathRequired = errors.New("reference path value is required")
    17  )
    18  
    19  // Resolver finds the identifier and HEAD path for a dataset reference
    20  type Resolver interface {
    21  	// ResolveRef uses ref as an outParam, setting ref.ID and ref.Path on success
    22  	// some implementations of name resolution may make network calls
    23  	// the returned "source" value should be either an empty string, indicating
    24  	// the source was resolved locally, or the multiaddress of the network
    25  	// address that performed the resolution
    26  	ResolveRef(ctx context.Context, ref *Ref) (source string, err error)
    27  }
    28  
    29  // ParallelResolver composes multiple resolvers into one resolver that runs
    30  // in parallel when called, using the first resolver that doesn't return
    31  // ErrRefNotFound
    32  func ParallelResolver(resolvers ...Resolver) Resolver {
    33  	return parallelResolver(resolvers)
    34  }
    35  
    36  type parallelResolver []Resolver
    37  
    38  func (rs parallelResolver) ResolveRef(ctx context.Context, ref *Ref) (string, error) {
    39  	responses := make(chan struct {
    40  		Ref    Ref
    41  		Source string
    42  	})
    43  	errs := make(chan error)
    44  
    45  	run := func(ctx context.Context, r Resolver) {
    46  		if r == nil {
    47  			errs <- ErrRefNotFound
    48  			return
    49  		}
    50  
    51  		cpy := ref.Copy()
    52  		source, err := r.ResolveRef(ctx, &cpy)
    53  		if err != nil {
    54  			errs <- err
    55  			return
    56  		}
    57  
    58  		responses <- struct {
    59  			Ref    Ref
    60  			Source string
    61  		}{cpy, source}
    62  	}
    63  
    64  	attempts := len(rs)
    65  	for _, r := range rs {
    66  		go run(ctx, r)
    67  	}
    68  
    69  	for {
    70  		select {
    71  		case res := <-responses:
    72  			*ref = res.Ref
    73  			return res.Source, nil
    74  		case err := <-errs:
    75  			attempts--
    76  			if !errors.Is(err, ErrRefNotFound) {
    77  				return "", err
    78  			} else if attempts == 0 {
    79  				return "", ErrRefNotFound
    80  			}
    81  		case <-ctx.Done():
    82  			return "", ctx.Err()
    83  		}
    84  	}
    85  }
    86  
    87  // SequentialResolver composes multiple resolvers into one that runs each
    88  // resolver in sequence, using the first resolver that doesn't return
    89  // ErrorNotFound
    90  func SequentialResolver(resolvers ...Resolver) Resolver {
    91  	return sequentialResolver(resolvers)
    92  }
    93  
    94  type sequentialResolver []Resolver
    95  
    96  func (sr sequentialResolver) ResolveRef(ctx context.Context, ref *Ref) (string, error) {
    97  	for _, resolver := range sr {
    98  		if resolver == nil {
    99  			continue
   100  		}
   101  
   102  		resolvedSource, err := resolver.ResolveRef(ctx, ref)
   103  		if err != nil {
   104  			if errors.Is(err, ErrRefNotFound) {
   105  				continue
   106  			} else {
   107  				return "", err
   108  			}
   109  		}
   110  		return resolvedSource, nil
   111  	}
   112  
   113  	return "", ErrRefNotFound
   114  }