github.com/bilus/oya@v0.0.3-0.20190301162104-da4acbd394c6/pkg/mvs/mvs.go (about) 1 package mvs 2 3 import ( 4 "fmt" 5 "sync" 6 7 "github.com/bilus/oya/pkg/mvs/internal" 8 "github.com/bilus/oya/pkg/pack" 9 "github.com/bilus/oya/pkg/types" 10 ) 11 12 type Reqs interface { 13 Reqs(pack pack.Pack) ([]pack.Pack, error) 14 } 15 16 func max(p1, p2 pack.Pack) pack.Pack { 17 if p1.Version().LessThan(p2.Version()) { 18 return p2 19 } else { 20 return p1 21 } 22 } 23 24 func Hash(pack pack.Pack) string { 25 return fmt.Sprintf("%v@%v", pack.ImportPath(), pack.Version()) 26 } 27 28 type Job struct { 29 pack.Pack 30 } 31 32 func (j Job) Payload() interface{} { 33 return j.Pack 34 } 35 36 func (j Job) ID() interface{} { 37 return Hash(j.Pack) 38 } 39 40 // List creates a list of requirements based on initial list of required packs, taking inter-pack requirements into account. 41 func List(required []pack.Pack, reqs Reqs) ([]pack.Pack, error) { 42 mtx := sync.Mutex{} 43 latest := make(map[types.ImportPath]pack.Pack) 44 queue := internal.Work{} 45 for _, r := range required { 46 queue.Add(Job{r}) 47 } 48 var firstErr error 49 queue.Do(10, 50 func(job internal.Job) { 51 if firstErr != nil { 52 return 53 } 54 mtx.Lock() 55 crnt, ok := job.Payload().(pack.Pack) 56 if !ok { 57 mtx.Unlock() 58 panic("Internal error: expected pack.Pack passed to work queue") 59 } 60 if l, ok := latest[crnt.ImportPath()]; !ok || Hash(max(l, crnt)) != Hash(l) { 61 latest[crnt.ImportPath()] = crnt 62 } 63 mtx.Unlock() 64 65 reqs, err := reqs.Reqs(crnt) 66 if err != nil { 67 firstErr = err 68 return 69 } 70 71 for _, req := range reqs { 72 queue.Add(Job{req}) 73 } 74 }) 75 76 if firstErr != nil { 77 return nil, firstErr 78 } 79 80 packs := make([]pack.Pack, 0) 81 for _, pack := range latest { 82 packs = append(packs, pack) 83 } 84 return packs, nil 85 }