github.com/tooploox/oya@v0.0.21-0.20230524103240-1cda1861aad6/pkg/mvs/mvs.go (about)

     1  package mvs
     2  
     3  import (
     4  	"fmt"
     5  	"sync"
     6  
     7  	"github.com/tooploox/oya/pkg/errors"
     8  	"github.com/tooploox/oya/pkg/mvs/internal"
     9  	"github.com/tooploox/oya/pkg/pack"
    10  	"github.com/tooploox/oya/pkg/types"
    11  )
    12  
    13  type ErrResolvingReqs struct {
    14  }
    15  
    16  func (e ErrResolvingReqs) Error() string {
    17  	return "problem getting requirements"
    18  }
    19  
    20  type Reqs interface {
    21  	Reqs(pack pack.Pack) ([]pack.Pack, error)
    22  }
    23  
    24  func max(p1, p2 pack.Pack) pack.Pack {
    25  	if p1.Version().LessThan(p2.Version()) {
    26  		return p2
    27  	} else {
    28  		return p1
    29  	}
    30  }
    31  
    32  func Hash(pack pack.Pack) string {
    33  	return fmt.Sprintf("%v@%v", pack.ImportPath(), pack.Version())
    34  }
    35  
    36  type Job struct {
    37  	Trace []errors.Location
    38  	Pack  pack.Pack
    39  }
    40  
    41  func (j Job) Payload() interface{} {
    42  	return j.Pack
    43  }
    44  
    45  func (j Job) ID() interface{} {
    46  	return Hash(j.Pack)
    47  }
    48  
    49  // List creates a list of requirements based on initial list of required packs, taking inter-pack requirements into account.
    50  func List(required []pack.Pack, reqs Reqs) ([]pack.Pack, error) {
    51  	mtx := sync.Mutex{}
    52  	latest := make(map[types.ImportPath]pack.Pack)
    53  	queue := internal.Work{}
    54  	for _, r := range required {
    55  		queue.Add(Job{nil, r})
    56  	}
    57  	var firstErr error
    58  	queue.Do(10,
    59  		func(j internal.Job) {
    60  			if firstErr != nil {
    61  				return
    62  			}
    63  			job, ok := j.(Job)
    64  			if !ok {
    65  				panic("Internal error: expected pack.Pack passed to work queue")
    66  			}
    67  			crnt := job.Pack
    68  			trace := duplicate(job.Trace)
    69  			mtx.Lock()
    70  			if l, ok := latest[crnt.ImportPath()]; !ok || Hash(max(l, crnt)) != Hash(l) {
    71  				latest[crnt.ImportPath()] = crnt
    72  			}
    73  			mtx.Unlock()
    74  
    75  			reqs, err := reqs.Reqs(crnt)
    76  			if err != nil {
    77  				mtx.Lock()
    78  				firstErr = errors.Wrap(
    79  					err,
    80  					ErrResolvingReqs{},
    81  					trace...,
    82  				)
    83  				mtx.Unlock()
    84  				return
    85  			}
    86  
    87  			for _, req := range reqs {
    88  				queue.Add(Job{append(trace, toLocation(crnt)), req})
    89  			}
    90  		})
    91  
    92  	if firstErr != nil {
    93  		return nil, firstErr
    94  	}
    95  
    96  	packs := make([]pack.Pack, 0)
    97  	for _, pack := range latest {
    98  		packs = append(packs, pack)
    99  	}
   100  	return packs, nil
   101  }
   102  
   103  func duplicate(trace []errors.Location) []errors.Location {
   104  	dup := make([]errors.Location, len(trace))
   105  	for i, t := range trace {
   106  		dup[i] = t
   107  	}
   108  	return dup
   109  }
   110  
   111  func toLocation(pack pack.Pack) errors.Location {
   112  	importPath := pack.ImportPath()
   113  	return errors.Location{
   114  		Name:        importPath.String(),
   115  		VerboseName: fmt.Sprintf("required by %v", importPath),
   116  	}
   117  }