github.com/golang/dep@v0.5.4/gps/version_queue.go (about) 1 // Copyright 2017 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package gps 6 7 import ( 8 "fmt" 9 "strings" 10 ) 11 12 type failedVersion struct { 13 v Version 14 f error 15 } 16 17 type versionQueue struct { 18 id ProjectIdentifier 19 pi []Version 20 lockv, prefv Version 21 fails []failedVersion 22 b sourceBridge 23 failed bool 24 allLoaded bool 25 adverr error 26 } 27 28 func newVersionQueue(id ProjectIdentifier, lockv, prefv Version, b sourceBridge) (*versionQueue, error) { 29 vq := &versionQueue{ 30 id: id, 31 b: b, 32 } 33 34 // Lock goes in first, if present 35 if lockv != nil { 36 vq.lockv = lockv 37 vq.pi = append(vq.pi, lockv) 38 } 39 40 // Preferred version next 41 if prefv != nil { 42 vq.prefv = prefv 43 vq.pi = append(vq.pi, prefv) 44 } 45 46 if len(vq.pi) == 0 { 47 var err error 48 vq.pi, err = vq.b.listVersions(vq.id) 49 if err != nil { 50 // TODO(sdboyer) pushing this error this early entails that we 51 // unconditionally deep scan (e.g. vendor), as well as hitting the 52 // network. 53 return nil, err 54 } 55 vq.allLoaded = true 56 } 57 58 return vq, nil 59 } 60 61 func (vq *versionQueue) current() Version { 62 if len(vq.pi) > 0 { 63 return vq.pi[0] 64 } 65 66 return nil 67 } 68 69 // advance moves the versionQueue forward to the next available version, 70 // recording the failure that eliminated the current version. 71 func (vq *versionQueue) advance(fail error) error { 72 // Nothing in the queue means...nothing in the queue, nicely enough 73 if vq.adverr != nil || len(vq.pi) == 0 { // should be a redundant check, but just in case 74 return vq.adverr 75 } 76 77 // Record the fail reason and pop the queue 78 vq.fails = append(vq.fails, failedVersion{ 79 v: vq.pi[0], 80 f: fail, 81 }) 82 vq.pi = vq.pi[1:] 83 84 // *now*, if the queue is empty, ensure all versions have been loaded 85 if len(vq.pi) == 0 { 86 if vq.allLoaded { 87 // This branch gets hit when the queue is first fully exhausted, 88 // after a previous advance() already called ListVersions(). 89 return nil 90 } 91 vq.allLoaded = true 92 93 var vltmp []Version 94 vltmp, vq.adverr = vq.b.listVersions(vq.id) 95 if vq.adverr != nil { 96 return vq.adverr 97 } 98 // defensive copy - calling listVersions here means slice contents may 99 // be modified when removing prefv/lockv. 100 vq.pi = make([]Version, len(vltmp)) 101 copy(vq.pi, vltmp) 102 103 // search for and remove lockv and prefv, in a pointer GC-safe manner 104 // 105 // could use the version comparator for binary search here to avoid 106 // O(n) each time...if it matters 107 var delkeys []int 108 for k, pi := range vq.pi { 109 if pi == vq.lockv || pi == vq.prefv { 110 delkeys = append(delkeys, k) 111 } 112 } 113 114 for k, dk := range delkeys { 115 dk -= k 116 copy(vq.pi[dk:], vq.pi[dk+1:]) 117 // write nil to final position for GC safety 118 vq.pi[len(vq.pi)-1] = nil 119 vq.pi = vq.pi[:len(vq.pi)-1] 120 } 121 122 if len(vq.pi) == 0 { 123 // If listing versions added nothing (new), then return now 124 return nil 125 } 126 } 127 128 // We're finally sure that there's something in the queue. Remove the 129 // failure marker, as the current version may have failed, but the next one 130 // hasn't yet 131 vq.failed = false 132 133 // If all have been loaded and the queue is empty, we're definitely out 134 // of things to try. Return empty, though, because vq semantics dictate 135 // that we don't explicitly indicate the end of the queue here. 136 return nil 137 } 138 139 // isExhausted indicates whether or not the queue has definitely been exhausted, 140 // in which case it will return true. 141 // 142 // It may return false negatives - suggesting that there is more in the queue 143 // when a subsequent call to current() will be empty. Plan accordingly. 144 func (vq *versionQueue) isExhausted() bool { 145 if !vq.allLoaded { 146 return false 147 } 148 return len(vq.pi) == 0 149 } 150 151 func (vq *versionQueue) String() string { 152 var vs []string 153 154 for _, v := range vq.pi { 155 vs = append(vs, v.String()) 156 } 157 return fmt.Sprintf("[%s]", strings.Join(vs, ", ")) 158 }