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  }