github.com/tooploox/oya@v0.0.21-0.20230524103240-1cda1861aad6/pkg/deptree/internal/reqs.go (about) 1 package internal 2 3 import ( 4 "fmt" 5 "path/filepath" 6 "sync" 7 8 "github.com/pkg/errors" 9 "github.com/tooploox/oya/pkg/oyafile" 10 "github.com/tooploox/oya/pkg/pack" 11 "github.com/tooploox/oya/pkg/raw" 12 "github.com/tooploox/oya/pkg/repo" 13 ) 14 15 type Reqs struct { 16 rootDir string 17 installDirs []string 18 cache map[string][]pack.Pack 19 mtx sync.Mutex 20 } 21 22 func NewReqs(rootDir string, installDirs []string) *Reqs { 23 return &Reqs{ 24 rootDir: rootDir, 25 installDirs: installDirs, 26 cache: make(map[string][]pack.Pack), 27 } 28 } 29 30 func (r *Reqs) Reqs(pack pack.Pack) ([]pack.Pack, error) { 31 reqs, found := r.cachedReqs(pack) 32 if found { 33 return reqs, nil 34 } 35 reqs, found, err := r.lookupReqs(pack) 36 if err != nil { 37 return nil, err 38 } 39 if !found { 40 // BUG(bilus): Custom error. 41 return nil, errors.Errorf("pack not found: %v", pack.ImportPath()) 42 } 43 r.cacheReqs(pack, reqs) 44 return reqs, nil 45 } 46 47 func (r *Reqs) lookupReqs(pack pack.Pack) ([]pack.Pack, bool, error) { 48 reqs, found, err := r.localReqs(pack) 49 if err != nil { 50 return nil, false, err 51 } 52 if found { 53 return reqs, true, nil 54 } 55 reqs, err = r.remoteReqs(pack) 56 if err != nil { 57 return nil, false, err 58 } 59 return reqs, true, nil 60 } 61 62 func (r *Reqs) cachedReqs(pack pack.Pack) ([]pack.Pack, bool) { 63 r.mtx.Lock() 64 reqs, found := r.cache[id(pack)] 65 r.mtx.Unlock() 66 if found { 67 return reqs, true 68 } 69 return nil, false 70 } 71 72 func (r *Reqs) cacheReqs(pack pack.Pack, reqs []pack.Pack) { 73 r.mtx.Lock() 74 r.cache[id(pack)] = reqs 75 r.mtx.Unlock() 76 } 77 78 func id(pack pack.Pack) string { 79 return fmt.Sprintf("%v@%v", pack.ImportPath(), pack.Version()) 80 } 81 82 func (r *Reqs) localReqs(pack pack.Pack) ([]pack.Pack, bool, error) { 83 o, found, err := r.LoadLocalOyafile(pack) 84 if err != nil { 85 return nil, false, err 86 } 87 if found { 88 packs, err := toPacks(o.Requires) 89 if err != nil { 90 return nil, false, err 91 } 92 return packs, true, nil 93 } 94 return nil, false, nil 95 } 96 97 func (r *Reqs) LoadLocalOyafile(pack pack.Pack) (*oyafile.Oyafile, bool, error) { 98 if path, ok := pack.ReplacementPath(); ok { 99 var fullPath string 100 if filepath.IsAbs(path) { 101 fullPath = path 102 } else { 103 fullPath = filepath.Join(r.rootDir, path) 104 } 105 o, found, err := oyafile.LoadFromDir(fullPath, r.rootDir) 106 if !found { 107 return nil, false, errors.Errorf("no %v found at the replacement path %v for %q", raw.DefaultName, fullPath, pack.ImportPath()) 108 } 109 if err != nil { 110 return nil, false, errors.Wrapf(err, "error resolving replacement path %v for %q", fullPath, pack.ImportPath()) 111 112 } 113 return o, true, nil 114 115 } 116 for _, installDir := range r.installDirs { 117 o, found, err := oyafile.LoadFromDir(pack.InstallPath(installDir), r.rootDir) 118 if err != nil { 119 continue 120 } 121 if !found { 122 continue 123 } 124 return o, true, nil 125 } 126 return nil, false, nil 127 } 128 129 func (r *Reqs) remoteReqs(p pack.Pack) ([]pack.Pack, error) { 130 l, err := repo.Open(p.ImportPath()) 131 if err != nil { 132 return nil, err 133 } 134 return l.Reqs(p.Version()) 135 } 136 137 func toPacks(references []oyafile.PackReference) ([]pack.Pack, error) { 138 packs := make([]pack.Pack, len(references)) 139 for i, reference := range references { 140 repo, err := repo.Open(reference.ImportPath) 141 if err != nil { 142 return nil, err 143 } 144 if packs[i], err = repo.Version(reference.Version); err != nil { 145 return nil, err 146 } 147 } 148 return packs, nil 149 }