зеркало из https://github.com/golang/dep.git
Split out into separate files
This commit is contained in:
Родитель
d7b90e6ec6
Коммит
54a0b20636
|
@ -0,0 +1,123 @@
|
|||
package vsolver
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/Masterminds/semver"
|
||||
)
|
||||
|
||||
type Constraint interface {
|
||||
Type() ConstraintType
|
||||
Body() string
|
||||
Allows(Version) bool
|
||||
UnionAllowsAny(Constraint) bool
|
||||
}
|
||||
|
||||
// NewConstraint constructs an appropriate Constraint object from the input
|
||||
// parameters.
|
||||
func NewConstraint(t ConstraintType, body string) (Constraint, error) {
|
||||
switch t {
|
||||
case C_Branch, C_Version, C_Revision:
|
||||
return basicConstraint{
|
||||
typ: t,
|
||||
body: body,
|
||||
}, nil
|
||||
case C_Semver, C_SemverRange:
|
||||
c, err := semver.NewConstraint(body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return semverConstraint{
|
||||
typ: t,
|
||||
body: body,
|
||||
c: c,
|
||||
}, nil
|
||||
default:
|
||||
return nil, errors.New("Unknown ConstraintType provided")
|
||||
}
|
||||
}
|
||||
|
||||
type basicConstraint struct {
|
||||
// The type of constraint - version, branch, or revision
|
||||
typ ConstraintType
|
||||
// The string text of the constraint
|
||||
body string
|
||||
}
|
||||
|
||||
func (c basicConstraint) Type() ConstraintType {
|
||||
return c.typ
|
||||
}
|
||||
|
||||
func (c basicConstraint) Body() string {
|
||||
return c.body
|
||||
}
|
||||
|
||||
func (c basicConstraint) Allows(v Version) bool {
|
||||
if VTCTCompat[v.Type]&c.typ == 0 {
|
||||
// version and constraint types are incompatible
|
||||
return false
|
||||
}
|
||||
|
||||
// Branches, normal versions, and revisions all must be exact string matches
|
||||
return c.body == v.Info
|
||||
}
|
||||
|
||||
func (c basicConstraint) UnionAllowsAny(c2 Constraint) bool {
|
||||
return (c2.Type() == c.typ && c2.Body() == c.body) || c2.UnionAllowsAny(c)
|
||||
}
|
||||
|
||||
// anyConstraint is an unbounded constraint - it matches all other types of
|
||||
// constraints.
|
||||
type anyConstraint struct{}
|
||||
|
||||
func (c anyConstraint) Type() ConstraintType {
|
||||
return C_ExactMatch | C_FlexMatch
|
||||
}
|
||||
|
||||
func (c anyConstraint) Body() string {
|
||||
return "*"
|
||||
}
|
||||
|
||||
func (c anyConstraint) Allows(v Version) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (c anyConstraint) UnionAllowsAny(_ Constraint) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
type semverConstraint struct {
|
||||
// The type of constraint - single semver, or semver range
|
||||
typ ConstraintType
|
||||
// The string text of the constraint
|
||||
body string
|
||||
c *semver.Constraints
|
||||
}
|
||||
|
||||
func (c semverConstraint) Type() ConstraintType {
|
||||
return c.typ
|
||||
}
|
||||
|
||||
func (c semverConstraint) Body() string {
|
||||
return c.body
|
||||
}
|
||||
|
||||
func (c semverConstraint) Allows(v Version) bool {
|
||||
if VTCTCompat[v.Type]&c.typ == 0 {
|
||||
// version and constraint types are incompatible
|
||||
return false
|
||||
}
|
||||
|
||||
return c.c.Check(v.SemVer)
|
||||
}
|
||||
|
||||
func (c semverConstraint) UnionAllowsAny(c2 Constraint) bool {
|
||||
if c2.Type()&(C_Semver|C_SemverRange) == 0 {
|
||||
// Union only possible if other constraint is semverish
|
||||
return false
|
||||
}
|
||||
|
||||
// TODO figure out how we're doing these union checks
|
||||
return false // FIXME
|
||||
}
|
|
@ -30,7 +30,7 @@ func (e *solveError) Error() string {
|
|||
}
|
||||
|
||||
type noVersionError struct {
|
||||
pi ProjectID
|
||||
pi ProjectIdentifier
|
||||
v string
|
||||
c Constraint
|
||||
deps []Dependency
|
||||
|
|
|
@ -0,0 +1,102 @@
|
|||
package vsolver
|
||||
|
||||
// The type of the version - branch, revision, or version
|
||||
type VersionType uint8
|
||||
|
||||
const (
|
||||
V_Revision VersionType = iota
|
||||
V_Branch
|
||||
V_Version
|
||||
V_Semver
|
||||
)
|
||||
|
||||
type ConstraintType uint8
|
||||
|
||||
const (
|
||||
C_Revision ConstraintType = 1 << iota
|
||||
C_Branch
|
||||
C_Version
|
||||
C_Semver
|
||||
C_SemverRange
|
||||
C_ExactMatch = C_Revision | C_Branch | C_Version | C_Semver
|
||||
C_FlexMatch = C_SemverRange
|
||||
)
|
||||
|
||||
var VTCTCompat = [...]ConstraintType{
|
||||
C_Revision,
|
||||
C_Branch,
|
||||
C_Version,
|
||||
C_Semver | C_SemverRange,
|
||||
}
|
||||
|
||||
type InfoLevel uint
|
||||
|
||||
const (
|
||||
FromCache InfoLevel = 1 << iota
|
||||
)
|
||||
|
||||
// ProjectExistence values represent the extent to which a project "exists."
|
||||
type ProjectExistence uint8
|
||||
|
||||
const (
|
||||
// DoesNotExist indicates that a particular project URI cannot be located,
|
||||
// at any level. It is represented as 1, rather than 0, to differentiate it
|
||||
// from the zero-value (which is ExistenceUnknown).
|
||||
DoesNotExist ProjectExistence = 1 << iota
|
||||
|
||||
// ExistsInLock indicates that a project exists (i.e., is mentioned in) a
|
||||
// lock file.
|
||||
// TODO not sure if it makes sense to have this IF it's just the source
|
||||
// manager's responsibility for putting this together - the implication is
|
||||
// that this is the root lock file, right?
|
||||
ExistsInLock
|
||||
|
||||
// ExistsInVendor indicates that a project exists in a vendor directory at
|
||||
// the predictable location based on import path. It does NOT imply, much
|
||||
// less guarantee, any of the following:
|
||||
// - That the code at the expected location under vendor is at the version
|
||||
// given in a lock file
|
||||
// - That the code at the expected location under vendor is from the
|
||||
// expected upstream project at all
|
||||
// - That, if this flag is not present, the project does not exist at some
|
||||
// unexpected/nested location under vendor
|
||||
// - That the full repository history is available. In fact, the
|
||||
// assumption should be that if only this flag is on, the full repository
|
||||
// history is likely not available locally
|
||||
//
|
||||
// In short, the information encoded in this flag should in no way be
|
||||
// construed as exhaustive.
|
||||
ExistsInVendor
|
||||
|
||||
// ExistsInCache indicates that a project exists on-disk in the local cache.
|
||||
// It does not guarantee that an upstream exists, thus it cannot imply
|
||||
// that the cache is at all correct - up-to-date, or even of the expected
|
||||
// upstream project repository.
|
||||
//
|
||||
// Additionally, this refers only to the existence of the local repository
|
||||
// itself; it says nothing about the existence or completeness of the
|
||||
// separate metadata cache.
|
||||
ExistsInCache
|
||||
|
||||
// ExistsUpstream indicates that a project repository was locatable at the
|
||||
// path provided by a project's URI (a base import path).
|
||||
ExistsUpstream
|
||||
|
||||
// Indicates that the upstream project, in addition to existing, is also
|
||||
// accessible.
|
||||
//
|
||||
// Different hosting providers treat unauthorized access differently:
|
||||
// GitHub, for example, returns 404 (or the equivalent) when attempting unauthorized
|
||||
// access, whereas BitBucket returns 403 (or 302 login redirect). Thus,
|
||||
// while the ExistsUpstream and UpstreamAccessible bits should always only
|
||||
// be on or off together when interacting with Github, it is possible that a
|
||||
// BitBucket provider might report ExistsUpstream, but not UpstreamAccessible.
|
||||
//
|
||||
// For most purposes, non-existence and inaccessibility are treated the
|
||||
// same, but clearly delineating the two allows slightly improved UX.
|
||||
UpstreamAccessible
|
||||
|
||||
// The zero value; indicates that no work has yet been done to determine the
|
||||
// existence level of a project.
|
||||
ExistenceUnknown ProjectExistence = 0
|
||||
)
|
|
@ -0,0 +1,5 @@
|
|||
package vsolver
|
||||
|
||||
// TODO define result structure - should also be interface?
|
||||
type Result struct {
|
||||
}
|
22
solver.go
22
solver.go
|
@ -14,15 +14,16 @@ const (
|
|||
IncompatibleVersionType
|
||||
)
|
||||
|
||||
func NewSolver(pf PackageFetcher) Solver {
|
||||
func NewSolver(sm SourceManager) Solver {
|
||||
return &solver{
|
||||
pf: pf,
|
||||
sm: sm,
|
||||
sel: &selection{},
|
||||
}
|
||||
}
|
||||
|
||||
// solver is a backtracking-style SAT solver.
|
||||
type solver struct {
|
||||
pf PackageFetcher
|
||||
sm SourceManager
|
||||
latest map[ProjectIdentifier]struct{}
|
||||
sel *selection
|
||||
unsel *unselected
|
||||
|
@ -71,6 +72,7 @@ func (s *solver) solve() ([]ProjectID, error) {
|
|||
continue
|
||||
}
|
||||
// TODO handle failures, lolzies
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s.selectVersion(*queue.current())
|
||||
|
@ -84,10 +86,10 @@ func (s *solver) solve() ([]ProjectID, error) {
|
|||
func (s *solver) createVersionQueue(ref ProjectIdentifier) (*VersionQueue, error) {
|
||||
// If on the root package, there's no queue to make
|
||||
if ref == s.rs.ID() {
|
||||
return NewVersionQueue(ref, nil, s.pf)
|
||||
return NewVersionQueue(ref, nil, s.sm)
|
||||
}
|
||||
|
||||
if !s.pf.ProjectExists(ref) {
|
||||
if !s.sm.ProjectExists(ref) {
|
||||
// TODO this check needs to incorporate/admit the possibility that the
|
||||
// upstream no longer exists, but there's something valid in vendor/
|
||||
return nil, newSolveError(fmt.Sprintf("Project '%s' could not be located.", ref), cannotResolve)
|
||||
|
@ -110,7 +112,7 @@ func (s *solver) createVersionQueue(ref ProjectIdentifier) (*VersionQueue, error
|
|||
//}
|
||||
//}
|
||||
|
||||
q, err := NewVersionQueue(ref, lockv, s.pf)
|
||||
q, err := NewVersionQueue(ref, lockv, s.sm)
|
||||
if err != nil {
|
||||
// TODO this particular err case needs to be improved to be ONLY for cases
|
||||
// where there's absolutely nothing findable about a given project name
|
||||
|
@ -198,13 +200,13 @@ func (s *solver) checkVersion(pi *ProjectID) error {
|
|||
|
||||
// TODO msg
|
||||
return &noVersionError{
|
||||
pi: *pi,
|
||||
pi: pi.ID,
|
||||
c: constraint,
|
||||
deps: deps,
|
||||
}
|
||||
}
|
||||
|
||||
if !s.pf.ProjectExists(pi.ID) {
|
||||
if !s.sm.ProjectExists(pi.ID) {
|
||||
// Can get here if the lock file specifies a now-nonexistent project
|
||||
// TODO this check needs to incorporate/accept the possibility that the
|
||||
// upstream no longer exists, but there's something valid in vendor/
|
||||
|
@ -249,7 +251,7 @@ func (s *solver) checkVersion(pi *ProjectID) error {
|
|||
|
||||
// TODO msg
|
||||
return &noVersionError{
|
||||
pi: dep.ProjectID,
|
||||
pi: dep.ID,
|
||||
c: dep.Constraint,
|
||||
deps: selfAndSiblings,
|
||||
}
|
||||
|
@ -268,7 +270,7 @@ func (s *solver) checkVersion(pi *ProjectID) error {
|
|||
//
|
||||
// If it's the root project, also includes dev dependencies, etc.
|
||||
func (s *solver) getDependenciesOf(pi ProjectID) ([]ProjectDep, error) {
|
||||
info, err := s.pf.GetProjectInfo(pi.ID)
|
||||
info, err := s.sm.GetProjectInfo(pi.ID)
|
||||
if err != nil {
|
||||
// TODO revisit this once a decision is made about better-formed errors;
|
||||
// question is, do we expect the fetcher to pass back simple errors, or
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
package vsolver
|
||||
|
||||
type SourceManager interface {
|
||||
GetProjectInfo(ProjectIdentifier) (ProjectInfo, error)
|
||||
ListVersions(ProjectIdentifier) ([]*ProjectID, error)
|
||||
ProjectExists(ProjectIdentifier) bool
|
||||
}
|
322
types.go
322
types.go
|
@ -1,118 +1,5 @@
|
|||
package vsolver
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/Masterminds/semver"
|
||||
)
|
||||
|
||||
// The type of the version - branch, revision, or version
|
||||
type VersionType uint8
|
||||
|
||||
const (
|
||||
V_Revision VersionType = iota
|
||||
V_Branch
|
||||
V_Version
|
||||
V_Semver
|
||||
)
|
||||
|
||||
type ConstraintType uint8
|
||||
|
||||
const (
|
||||
C_Revision ConstraintType = 1 << iota
|
||||
C_Branch
|
||||
C_Version
|
||||
C_Semver
|
||||
C_SemverRange
|
||||
C_ExactMatch = C_Revision | C_Branch | C_Version | C_Semver
|
||||
C_FlexMatch = C_SemverRange
|
||||
)
|
||||
|
||||
var VTCTCompat = [...]ConstraintType{
|
||||
C_Revision,
|
||||
C_Branch,
|
||||
C_Version,
|
||||
C_Semver | C_SemverRange,
|
||||
}
|
||||
|
||||
type InfoLevel uint
|
||||
|
||||
const (
|
||||
FromCache InfoLevel = 1 << iota
|
||||
)
|
||||
|
||||
// ProjectExistence values represent the extent to which a project "exists."
|
||||
type ProjectExistence uint8
|
||||
|
||||
const (
|
||||
// DoesNotExist indicates that a particular project URI cannot be located,
|
||||
// at any level. It is represented as 1, rather than 0, to differentiate it
|
||||
// from the zero-value (which is ExistenceUnknown).
|
||||
DoesNotExist ProjectExistence = 1 << iota
|
||||
|
||||
// ExistsInLock indicates that a project exists (i.e., is mentioned in) a
|
||||
// lock file.
|
||||
// TODO not sure if it makes sense to have this IF it's just the source
|
||||
// manager's responsibility for putting this together - the implication is
|
||||
// that this is the root lock file, right?
|
||||
ExistsInLock
|
||||
|
||||
// ExistsInVendor indicates that a project exists in a vendor directory at
|
||||
// the predictable location based on import path. It does NOT imply, much
|
||||
// less guarantee, any of the following:
|
||||
// - That the code at the expected location under vendor is at the version
|
||||
// given in a lock file
|
||||
// - That the code at the expected location under vendor is from the
|
||||
// expected upstream project at all
|
||||
// - That, if this flag is not present, the project does not exist at some
|
||||
// unexpected/nested location under vendor
|
||||
// - That the full repository history is available. In fact, the
|
||||
// assumption should be that if only this flag is on, the full repository
|
||||
// history is likely not available locally
|
||||
//
|
||||
// In short, the information encoded in this flag should in no way be
|
||||
// construed as exhaustive.
|
||||
ExistsInVendor
|
||||
|
||||
// ExistsInCache indicates that a project exists on-disk in the local cache.
|
||||
// It does not guarantee that an upstream exists, thus it cannot imply
|
||||
// that the cache is at all correct - up-to-date, or even of the expected
|
||||
// upstream project repository.
|
||||
//
|
||||
// Additionally, this refers only to the existence of the local repository
|
||||
// itself; it says nothing about the existence or completeness of the
|
||||
// separate metadata cache.
|
||||
ExistsInCache
|
||||
|
||||
// ExistsUpstream indicates that a project repository was locatable at the
|
||||
// path provided by a project's URI (a base import path).
|
||||
ExistsUpstream
|
||||
|
||||
// Indicates that the upstream project, in addition to existing, is also
|
||||
// accessible.
|
||||
//
|
||||
// Different hosting providers treat unauthorized access differently:
|
||||
// GitHub, for example, returns 404 (or the equivalent) when attempting unauthorized
|
||||
// access, whereas BitBucket returns 403 (or 302 login redirect). Thus,
|
||||
// while the ExistsUpstream and UpstreamAccessible bits should always only
|
||||
// be on or off together when interacting with Github, it is possible that a
|
||||
// BitBucket provider might report ExistsUpstream, but not UpstreamAccessible.
|
||||
//
|
||||
// For most purposes, non-existence and inaccessibility are treated the
|
||||
// same, but clearly delineating the two allows slightly improved UX.
|
||||
UpstreamAccessible
|
||||
|
||||
// The zero value; indicates that no work has yet been done to determine the
|
||||
// existence level of a project.
|
||||
ExistenceUnknown ProjectExistence = 0
|
||||
)
|
||||
|
||||
type PackageFetcher interface {
|
||||
GetProjectInfo(ProjectIdentifier) (ProjectInfo, error)
|
||||
ListVersions(ProjectIdentifier) ([]*ProjectID, error)
|
||||
ProjectExists(ProjectIdentifier) bool
|
||||
}
|
||||
|
||||
type ProjectIdentifier string
|
||||
|
||||
type Solver interface {
|
||||
|
@ -126,135 +13,11 @@ type ProjectID struct {
|
|||
Packages []string
|
||||
}
|
||||
|
||||
type Version struct {
|
||||
// The type of version identifier
|
||||
Type VersionType
|
||||
// The version identifier itself
|
||||
Info string
|
||||
SemVer *semver.Version
|
||||
}
|
||||
|
||||
type ProjectDep struct {
|
||||
ProjectID
|
||||
ID ProjectIdentifier
|
||||
Constraint Constraint
|
||||
}
|
||||
|
||||
type Constraint interface {
|
||||
Type() ConstraintType
|
||||
Body() string
|
||||
Allows(Version) bool
|
||||
UnionAllowsAny(Constraint) bool
|
||||
}
|
||||
|
||||
// NewConstraint constructs an appropriate Constraint object from the input
|
||||
// parameters.
|
||||
func NewConstraint(t ConstraintType, body string) (Constraint, error) {
|
||||
switch t {
|
||||
case C_Branch, C_Version, C_Revision:
|
||||
return basicConstraint{
|
||||
typ: t,
|
||||
body: body,
|
||||
}, nil
|
||||
case C_Semver, C_SemverRange:
|
||||
c, err := semver.NewConstraint(body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return semverConstraint{
|
||||
typ: t,
|
||||
body: body,
|
||||
c: c,
|
||||
}, nil
|
||||
default:
|
||||
return nil, errors.New("Unknown ConstraintType provided")
|
||||
}
|
||||
}
|
||||
|
||||
type basicConstraint struct {
|
||||
// The type of constraint - version, branch, or revision
|
||||
typ ConstraintType
|
||||
// The string text of the constraint
|
||||
body string
|
||||
}
|
||||
|
||||
func (c basicConstraint) Type() ConstraintType {
|
||||
return c.typ
|
||||
}
|
||||
|
||||
func (c basicConstraint) Body() string {
|
||||
return c.body
|
||||
}
|
||||
|
||||
func (c basicConstraint) Allows(v Version) bool {
|
||||
if VTCTCompat[v.Type]&c.typ == 0 {
|
||||
// version and constraint types are incompatible
|
||||
return false
|
||||
}
|
||||
|
||||
// Branches, normal versions, and revisions all must be exact string matches
|
||||
return c.body == v.Info
|
||||
}
|
||||
|
||||
func (c basicConstraint) UnionAllowsAny(c2 Constraint) bool {
|
||||
return (c2.Type() == c.typ && c2.Body() == c.body) || c2.UnionAllowsAny(c)
|
||||
}
|
||||
|
||||
// anyConstraint is an unbounded constraint - it matches all other types of
|
||||
// constraints.
|
||||
type anyConstraint struct{}
|
||||
|
||||
func (c anyConstraint) Type() ConstraintType {
|
||||
return C_ExactMatch | C_FlexMatch
|
||||
}
|
||||
|
||||
func (c anyConstraint) Body() string {
|
||||
return "*"
|
||||
}
|
||||
|
||||
func (c anyConstraint) Allows(v Version) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (c anyConstraint) UnionAllowsAny(_ Constraint) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
type semverConstraint struct {
|
||||
// The type of constraint - single semver, or semver range
|
||||
typ ConstraintType
|
||||
// The string text of the constraint
|
||||
body string
|
||||
c *semver.Constraints
|
||||
}
|
||||
|
||||
func (c semverConstraint) Type() ConstraintType {
|
||||
return c.typ
|
||||
}
|
||||
|
||||
func (c semverConstraint) Body() string {
|
||||
return c.body
|
||||
}
|
||||
|
||||
func (c semverConstraint) Allows(v Version) bool {
|
||||
if VTCTCompat[v.Type]&c.typ == 0 {
|
||||
// version and constraint types are incompatible
|
||||
return false
|
||||
}
|
||||
|
||||
return c.c.Check(v.SemVer)
|
||||
}
|
||||
|
||||
func (c semverConstraint) UnionAllowsAny(c2 Constraint) bool {
|
||||
if c2.Type()&(C_Semver|C_SemverRange) == 0 {
|
||||
// Union only possible if other constraint is semverish
|
||||
return false
|
||||
}
|
||||
|
||||
// TODO figure out how we're doing these union checks
|
||||
return false // FIXME
|
||||
}
|
||||
|
||||
type Dependency struct {
|
||||
Depender ProjectID
|
||||
Dep ProjectDep
|
||||
|
@ -294,86 +57,3 @@ type Lock interface {
|
|||
type lockedProject struct {
|
||||
Name, Revision, Version string
|
||||
}
|
||||
|
||||
// TODO define result structure - should also be interface?
|
||||
type Result struct {
|
||||
}
|
||||
|
||||
type VersionQueue struct {
|
||||
ref ProjectIdentifier
|
||||
pi []*ProjectID
|
||||
failed bool
|
||||
hasLock, allLoaded bool
|
||||
pf PackageFetcher
|
||||
}
|
||||
|
||||
func NewVersionQueue(ref ProjectIdentifier, lockv *ProjectID, pf PackageFetcher) (*VersionQueue, error) {
|
||||
vq := &VersionQueue{
|
||||
ref: ref,
|
||||
//avf: avf,
|
||||
pf: pf,
|
||||
}
|
||||
|
||||
if lockv != nil {
|
||||
vq.hasLock = true
|
||||
vq.pi = append(vq.pi, lockv)
|
||||
} else {
|
||||
var err error
|
||||
//vq.pi, err = vq.avf(vq.ref, nil)
|
||||
// TODO should probably just make the fetcher return semver already, and
|
||||
// update ProjectID to suit
|
||||
vq.pi, err = vq.pf.ListVersions(vq.ref)
|
||||
if err != nil {
|
||||
// TODO pushing this error this early entails that we
|
||||
// unconditionally deep scan (e.g. vendor), as well as hitting the
|
||||
// network.
|
||||
return nil, err
|
||||
}
|
||||
vq.allLoaded = true
|
||||
}
|
||||
|
||||
return vq, nil
|
||||
}
|
||||
|
||||
func (vq *VersionQueue) current() *ProjectID {
|
||||
if len(vq.pi) > 0 {
|
||||
return vq.pi[0]
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (vq *VersionQueue) advance() (err error) {
|
||||
// The current version may have failed, but the next one hasn't
|
||||
vq.failed = false
|
||||
|
||||
if !vq.allLoaded {
|
||||
// Can only get here if no lock was initially provided, so we know we
|
||||
// should have that
|
||||
lockv := vq.pi[0]
|
||||
|
||||
//vq.pi, err = vq.avf(vq.ref)
|
||||
vq.pi, err = vq.pf.ListVersions(vq.ref)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// search for and remove locked version
|
||||
// TODO should be able to avoid O(n) here each time...if it matters
|
||||
for k, pi := range vq.pi {
|
||||
if pi == lockv {
|
||||
// GC-safe deletion for slice w/pointer elements
|
||||
vq.pi, vq.pi[len(vq.pi)-1] = append(vq.pi[:k], vq.pi[k+1:]...), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(vq.pi) > 0 {
|
||||
vq.pi = vq.pi[1:]
|
||||
}
|
||||
|
||||
// normal end of queue. we don't error; it's left to the caller to infer an
|
||||
// empty queue w/a subsequent call to current(), which will return nil.
|
||||
// TODO this approach kinda...sucks
|
||||
return
|
||||
}
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
package vsolver
|
||||
|
||||
import "github.com/Masterminds/semver"
|
||||
|
||||
type Version struct {
|
||||
// The type of version identifier
|
||||
Type VersionType
|
||||
// The version identifier itself
|
||||
Info string
|
||||
SemVer *semver.Version
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
package vsolver
|
||||
|
||||
type VersionQueue struct {
|
||||
ref ProjectIdentifier
|
||||
pi []*ProjectID
|
||||
failed bool
|
||||
hasLock, allLoaded bool
|
||||
sm SourceManager
|
||||
}
|
||||
|
||||
func NewVersionQueue(ref ProjectIdentifier, lockv *ProjectID, sm SourceManager) (*VersionQueue, error) {
|
||||
vq := &VersionQueue{
|
||||
ref: ref,
|
||||
sm: sm,
|
||||
}
|
||||
|
||||
if lockv != nil {
|
||||
vq.hasLock = true
|
||||
vq.pi = append(vq.pi, lockv)
|
||||
} else {
|
||||
var err error
|
||||
//vq.pi, err = vq.avf(vq.ref, nil)
|
||||
// TODO should probably just make the fetcher return semver already, and
|
||||
// update ProjectID to suit
|
||||
vq.pi, err = vq.sm.ListVersions(vq.ref)
|
||||
if err != nil {
|
||||
// TODO pushing this error this early entails that we
|
||||
// unconditionally deep scan (e.g. vendor), as well as hitting the
|
||||
// network.
|
||||
return nil, err
|
||||
}
|
||||
vq.allLoaded = true
|
||||
}
|
||||
|
||||
return vq, nil
|
||||
}
|
||||
|
||||
func (vq *VersionQueue) current() *ProjectID {
|
||||
if len(vq.pi) > 0 {
|
||||
return vq.pi[0]
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (vq *VersionQueue) advance() (err error) {
|
||||
// The current version may have failed, but the next one hasn't
|
||||
vq.failed = false
|
||||
|
||||
if !vq.allLoaded {
|
||||
// Can only get here if no lock was initially provided, so we know we
|
||||
// should have that
|
||||
lockv := vq.pi[0]
|
||||
|
||||
//vq.pi, err = vq.avf(vq.ref)
|
||||
vq.pi, err = vq.sm.ListVersions(vq.ref)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// search for and remove locked version
|
||||
// TODO should be able to avoid O(n) here each time...if it matters
|
||||
for k, pi := range vq.pi {
|
||||
if pi == lockv {
|
||||
// GC-safe deletion for slice w/pointer elements
|
||||
vq.pi, vq.pi[len(vq.pi)-1] = append(vq.pi[:k], vq.pi[k+1:]...), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(vq.pi) > 0 {
|
||||
vq.pi = vq.pi[1:]
|
||||
}
|
||||
|
||||
// normal end of queue. we don't error; it's left to the caller to infer an
|
||||
// empty queue w/a subsequent call to current(), which will return nil.
|
||||
// TODO this approach kinda...sucks
|
||||
return
|
||||
}
|
Загрузка…
Ссылка в новой задаче