From 48f48dc3a3866bf722fb0df894a7915443d8c665 Mon Sep 17 00:00:00 2001 From: Sam Boyer Date: Fri, 1 Apr 2016 12:08:26 -0400 Subject: [PATCH] Add support for sorting version lists Fixes #3. --- bestiary_test.go | 20 ++++++++++++-- project_manager.go | 12 ++++++++ solve_test.go | 2 +- source_manager.go | 69 +++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 99 insertions(+), 4 deletions(-) diff --git a/bestiary_test.go b/bestiary_test.go index 9a4160bf8c..fcedb3e3b5 100644 --- a/bestiary_test.go +++ b/bestiary_test.go @@ -2,6 +2,7 @@ package vsolver import ( "fmt" + "sort" "strings" "github.com/Masterminds/semver" @@ -160,8 +161,7 @@ var fixtures = []fixture{ "root 0.0.0", "a 1.0.0", "b 1.0.0", - "shared 3.0.0", - //"shared 3.6.9", // this will be correct once #3 is in and we + "shared 3.6.9", //default to upgrading ), }, @@ -189,10 +189,20 @@ var fixtures = []fixture{ type depspecSourceManager struct { specs []depspec + //map[ProjectAtom][]Version + sortup bool } var _ SourceManager = &depspecSourceManager{} +func newdepspecSM(ds []depspec, upgrade bool) *depspecSourceManager { + //TODO precompute the version lists, for speediness? + return &depspecSourceManager{ + specs: ds, + sortup: upgrade, + } +} + func (sm *depspecSourceManager) GetProjectInfo(pa ProjectAtom) (ProjectInfo, error) { for _, ds := range sm.specs { if pa.Name == ds.name.Name && pa.Version.Info == ds.name.Version.Info { @@ -219,6 +229,12 @@ func (sm *depspecSourceManager) ListVersions(name ProjectName) (pi []Version, er err = fmt.Errorf("Project '%s' could not be found", name) } + if sm.sortup { + sort.Sort(upgradeVersionSorter(pi)) + } else { + sort.Sort(downgradeVersionSorter(pi)) + } + return } diff --git a/project_manager.go b/project_manager.go index df2487512a..73689a325a 100644 --- a/project_manager.go +++ b/project_manager.go @@ -2,6 +2,7 @@ package vsolver import ( "fmt" + "sort" "sync" "github.com/Masterminds/semver" @@ -32,6 +33,9 @@ type projectManager struct { // The list of versions. Kept separate from the data cache because this is // accessed in the hot loop; we don't want to rebuild and realloc for it. vlist []Version + // Direction to sort the version list in (true is for upgrade, false for + // downgrade) + sortup bool // The project metadata cache. This is persisted to disk, for reuse across // solver runs. dc *projectDataCache @@ -91,6 +95,14 @@ func (pm *projectManager) ListVersions() (vlist []Version, err error) { pm.dc.VMap[v] = v.Underlying pm.dc.RMap[v.Underlying] = append(pm.dc.RMap[v.Underlying], v) } + + // Sort the versions + // TODO do this as a heap in the original call + if pm.sortup { + sort.Sort(upgradeVersionSorter(pm.vlist)) + } else { + sort.Sort(downgradeVersionSorter(pm.vlist)) + } } return pm.vlist, nil diff --git a/solve_test.go b/solve_test.go index 5793ba84e0..863bf381bb 100644 --- a/solve_test.go +++ b/solve_test.go @@ -14,7 +14,7 @@ func TestBasicSolves(t *testing.T) { func solveAndBasicChecks(fixnum int, t *testing.T) Result { fix := fixtures[fixnum] - sm := &depspecSourceManager{specs: fix.ds} + sm := newdepspecSM(fix.ds, true) l := logrus.New() if testing.Verbose() { diff --git a/source_manager.go b/source_manager.go index 7c29d6ccec..e1c97919f9 100644 --- a/source_manager.go +++ b/source_manager.go @@ -32,6 +32,7 @@ type sourceManager struct { cachedir, basedir string pms map[ProjectName]*pmState anafac func(ProjectName) ProjectAnalyzer + sortup bool //pme map[ProjectName]error } @@ -44,11 +45,12 @@ type pmState struct { vlist []Version // TODO temporary until we have a coherent, overall cache structure } -func NewSourceManager(cachedir, basedir string) (SourceManager, error) { +func NewSourceManager(cachedir, basedir string, upgrade bool) (SourceManager, error) { // TODO try to create dir if doesn't exist return &sourceManager{ cachedir: cachedir, pms: make(map[ProjectName]*pmState), + sortup: upgrade, }, nil // TODO drop file lock on cachedir somewhere, here. Caller needs a panic @@ -125,3 +127,68 @@ func (sm *sourceManager) getProjectManager(n ProjectName) (*pmState, error) { sm.pms[n] = pms return pms, nil } + +type upgradeVersionSorter []Version +type downgradeVersionSorter []Version + +func (vs upgradeVersionSorter) Len() int { + return len(vs) +} + +func (vs upgradeVersionSorter) Swap(i, j int) { + vs[i], vs[j] = vs[j], vs[i] +} + +func (vs downgradeVersionSorter) Len() int { + return len(vs) +} + +func (vs downgradeVersionSorter) Swap(i, j int) { + vs[i], vs[j] = vs[j], vs[i] +} + +func (vs upgradeVersionSorter) Less(i, j int) bool { + l, r := vs[i], vs[j] + + // Start by always sorting higher vtypes earlier + // TODO need a new means when we get rid of those types + if l.Type != r.Type { + return l.Type > r.Type + } + + switch l.Type { + case V_Branch, V_Version, V_Revision: + return l.Info < r.Info + } + + // This ensures that pre-release versions are always sorted after ALL + // full-release versions + lpre, rpre := l.SemVer.Prerelease() == "", r.SemVer.Prerelease() == "" + if (lpre && !rpre) || (!lpre && rpre) { + return lpre + } + return l.SemVer.GreaterThan(r.SemVer) +} + +func (vs downgradeVersionSorter) Less(i, j int) bool { + l, r := vs[i], vs[j] + + // Start by always sorting higher vtypes earlier + // TODO need a new means when we get rid of those types + if l.Type != r.Type { + return l.Type > r.Type + } + + switch l.Type { + case V_Branch, V_Version, V_Revision: + return l.Info < r.Info + } + + // This ensures that pre-release versions are always sorted after ALL + // full-release versions + lpre, rpre := l.SemVer.Prerelease() == "", r.SemVer.Prerelease() == "" + if (lpre && !rpre) || (!lpre && rpre) { + return lpre + } + return l.SemVer.LessThan(r.SemVer) +}