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 }