github.com/golang/dep@v0.5.4/gps/version.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  	"sort"
    10  
    11  	"github.com/Masterminds/semver"
    12  	"github.com/golang/dep/gps/internal/pb"
    13  )
    14  
    15  // VersionType indicates a type for a Version that conveys some additional
    16  // semantics beyond that which is literally embedded on the Go type.
    17  type VersionType uint8
    18  
    19  // VersionTypes for the four major classes of version.
    20  const (
    21  	IsRevision VersionType = iota
    22  	IsVersion
    23  	IsSemver
    24  	IsBranch
    25  )
    26  
    27  // Version represents one of the different types of versions used by gps.
    28  //
    29  // Version composes Constraint, because all versions can be used as a constraint
    30  // (where they allow one, and only one, version - themselves), but constraints
    31  // are not necessarily discrete versions.
    32  //
    33  // Version is an interface, but it contains private methods, which restricts it
    34  // to gps's own internal implementations. We do this for the confluence of
    35  // two reasons: the implementation of Versions is complete (there is no case in
    36  // which we'd need other types), and the implementation relies on type magic
    37  // under the hood, which would be unsafe to do if other dynamic types could be
    38  // hiding behind the interface.
    39  type Version interface {
    40  	Constraint
    41  
    42  	// Indicates the type of version - Revision, Branch, Version, or Semver.
    43  	Type() VersionType
    44  }
    45  
    46  // PairedVersion represents a normal Version, but paired with its corresponding,
    47  // underlying Revision.
    48  type PairedVersion interface {
    49  	Version
    50  
    51  	// Revision returns the immutable Revision that identifies this Version.
    52  	Revision() Revision
    53  
    54  	// Unpair returns the surface-level UnpairedVersion that half of the pair.
    55  	//
    56  	// It does NOT modify the original PairedVersion.
    57  	Unpair() UnpairedVersion
    58  
    59  	// Ensures it is impossible to be both a PairedVersion and an
    60  	// UnpairedVersion.
    61  	_pair(int)
    62  }
    63  
    64  // UnpairedVersion represents a normal Version, with a method for creating a
    65  // VersionPair by indicating the version's corresponding, underlying Revision.
    66  type UnpairedVersion interface {
    67  	Version
    68  	// Pair takes the underlying Revision that this UnpairedVersion corresponds
    69  	// to and unites them into a PairedVersion.
    70  	Pair(Revision) PairedVersion
    71  	// Ensures it is impossible to be both a PairedVersion and an
    72  	// UnpairedVersion.
    73  	_pair(bool)
    74  }
    75  
    76  // types are weird
    77  func (branchVersion) _pair(bool) {}
    78  func (plainVersion) _pair(bool)  {}
    79  func (semVersion) _pair(bool)    {}
    80  func (versionPair) _pair(int)    {}
    81  
    82  // NewBranch creates a new Version to represent a floating version (in
    83  // general, a branch).
    84  func NewBranch(body string) UnpairedVersion {
    85  	return branchVersion{
    86  		name: body,
    87  		// We always set isDefault to false here, because the property is
    88  		// specifically designed to be internal-only: only the SourceManager
    89  		// gets to mark it. This is OK because nothing that client code is
    90  		// responsible for needs to care about has to touch it it.
    91  		//
    92  		// TODO(sdboyer) ...maybe. this just ugly.
    93  		isDefault: false,
    94  	}
    95  }
    96  
    97  func newDefaultBranch(body string) UnpairedVersion {
    98  	return branchVersion{
    99  		name:      body,
   100  		isDefault: true,
   101  	}
   102  }
   103  
   104  // NewVersion creates a Semver-typed Version if the provided version string is
   105  // valid semver, and a plain/non-semver version if not.
   106  func NewVersion(body string) UnpairedVersion {
   107  	sv, err := semver.NewVersion(body)
   108  
   109  	if err != nil {
   110  		return plainVersion(body)
   111  	}
   112  	return semVersion{sv: sv}
   113  }
   114  
   115  // A Revision represents an immutable versioning identifier.
   116  type Revision string
   117  
   118  // String converts the Revision back into a string.
   119  func (r Revision) String() string {
   120  	return string(r)
   121  }
   122  
   123  // ImpliedCaretString follows the same rules as String(), but in accordance with
   124  // the Constraint interface will always print a leading "=", as all Versions,
   125  // when acting as a Constraint, act as exact matches.
   126  func (r Revision) ImpliedCaretString() string {
   127  	return r.String()
   128  }
   129  
   130  func (r Revision) typedString() string {
   131  	return "r-" + string(r)
   132  }
   133  
   134  // Type indicates the type of version - for revisions, "revision".
   135  func (r Revision) Type() VersionType {
   136  	return IsRevision
   137  }
   138  
   139  // Matches is the Revision acting as a constraint; it checks to see if the provided
   140  // version is the same Revision as itself.
   141  func (r Revision) Matches(v Version) bool {
   142  	switch tv := v.(type) {
   143  	case Revision:
   144  		return r == tv
   145  	case versionPair:
   146  		return r == tv.r
   147  	}
   148  
   149  	return false
   150  }
   151  
   152  // MatchesAny is the Revision acting as a constraint; it checks to see if the provided
   153  // version is the same Revision as itself.
   154  func (r Revision) MatchesAny(c Constraint) bool {
   155  	switch tc := c.(type) {
   156  	case anyConstraint:
   157  		return true
   158  	case noneConstraint:
   159  		return false
   160  	case Revision:
   161  		return r == tc
   162  	case versionPair:
   163  		return r == tc.r
   164  	}
   165  
   166  	return false
   167  }
   168  
   169  // Intersect computes the intersection of the Constraint with the provided
   170  // Constraint. For Revisions, this can only be another, exactly equal
   171  // Revision, or a PairedVersion whose underlying Revision is exactly equal.
   172  func (r Revision) Intersect(c Constraint) Constraint {
   173  	switch tc := c.(type) {
   174  	case anyConstraint:
   175  		return r
   176  	case noneConstraint:
   177  		return none
   178  	case Revision:
   179  		if r == tc {
   180  			return r
   181  		}
   182  	case versionPair:
   183  		if r == tc.r {
   184  			return r
   185  		}
   186  	}
   187  
   188  	return none
   189  }
   190  
   191  func (r Revision) identical(c Constraint) bool {
   192  	r2, ok := c.(Revision)
   193  	if !ok {
   194  		return false
   195  	}
   196  	return r == r2
   197  }
   198  
   199  func (r Revision) copyTo(msg *pb.Constraint) {
   200  	msg.Type = pb.Constraint_Revision
   201  	msg.Value = string(r)
   202  }
   203  
   204  type branchVersion struct {
   205  	name      string
   206  	isDefault bool
   207  }
   208  
   209  func (v branchVersion) String() string {
   210  	return string(v.name)
   211  }
   212  
   213  func (v branchVersion) ImpliedCaretString() string {
   214  	return v.String()
   215  }
   216  
   217  func (v branchVersion) typedString() string {
   218  	return fmt.Sprintf("b-%s", v.String())
   219  }
   220  
   221  func (v branchVersion) Type() VersionType {
   222  	return IsBranch
   223  }
   224  
   225  func (v branchVersion) Matches(v2 Version) bool {
   226  	switch tv := v2.(type) {
   227  	case branchVersion:
   228  		return v.name == tv.name
   229  	case versionPair:
   230  		if tv2, ok := tv.v.(branchVersion); ok {
   231  			return tv2.name == v.name
   232  		}
   233  	}
   234  	return false
   235  }
   236  
   237  func (v branchVersion) MatchesAny(c Constraint) bool {
   238  	switch tc := c.(type) {
   239  	case anyConstraint:
   240  		return true
   241  	case noneConstraint:
   242  		return false
   243  	case branchVersion:
   244  		return v.name == tc.name
   245  	case versionPair:
   246  		if tc2, ok := tc.v.(branchVersion); ok {
   247  			return tc2.name == v.name
   248  		}
   249  	}
   250  
   251  	return false
   252  }
   253  
   254  func (v branchVersion) Intersect(c Constraint) Constraint {
   255  	switch tc := c.(type) {
   256  	case anyConstraint:
   257  		return v
   258  	case noneConstraint:
   259  		return none
   260  	case branchVersion:
   261  		if v.name == tc.name {
   262  			return v
   263  		}
   264  	case versionPair:
   265  		if tc2, ok := tc.v.(branchVersion); ok {
   266  			if v.name == tc2.name {
   267  				return v
   268  			}
   269  		}
   270  	}
   271  
   272  	return none
   273  }
   274  
   275  func (v branchVersion) Pair(r Revision) PairedVersion {
   276  	return versionPair{
   277  		v: v,
   278  		r: r,
   279  	}
   280  }
   281  
   282  func (v branchVersion) identical(c Constraint) bool {
   283  	v2, ok := c.(branchVersion)
   284  	if !ok {
   285  		return false
   286  	}
   287  	return v == v2
   288  }
   289  
   290  func (v branchVersion) copyTo(msg *pb.Constraint) {
   291  	if v.isDefault {
   292  		msg.Type = pb.Constraint_DefaultBranch
   293  	} else {
   294  		msg.Type = pb.Constraint_Branch
   295  	}
   296  	msg.Value = v.name
   297  }
   298  
   299  type plainVersion string
   300  
   301  func (v plainVersion) String() string {
   302  	return string(v)
   303  }
   304  
   305  func (v plainVersion) ImpliedCaretString() string {
   306  	return v.String()
   307  }
   308  
   309  func (v plainVersion) typedString() string {
   310  	return fmt.Sprintf("pv-%s", v.String())
   311  }
   312  
   313  func (v plainVersion) Type() VersionType {
   314  	return IsVersion
   315  }
   316  
   317  func (v plainVersion) Matches(v2 Version) bool {
   318  	switch tv := v2.(type) {
   319  	case plainVersion:
   320  		return v == tv
   321  	case versionPair:
   322  		if tv2, ok := tv.v.(plainVersion); ok {
   323  			return tv2 == v
   324  		}
   325  	}
   326  	return false
   327  }
   328  
   329  func (v plainVersion) MatchesAny(c Constraint) bool {
   330  	switch tc := c.(type) {
   331  	case anyConstraint:
   332  		return true
   333  	case noneConstraint:
   334  		return false
   335  	case plainVersion:
   336  		return v == tc
   337  	case versionPair:
   338  		if tc2, ok := tc.v.(plainVersion); ok {
   339  			return tc2 == v
   340  		}
   341  	}
   342  
   343  	return false
   344  }
   345  
   346  func (v plainVersion) Intersect(c Constraint) Constraint {
   347  	switch tc := c.(type) {
   348  	case anyConstraint:
   349  		return v
   350  	case noneConstraint:
   351  		return none
   352  	case plainVersion:
   353  		if v == tc {
   354  			return v
   355  		}
   356  	case versionPair:
   357  		if tc2, ok := tc.v.(plainVersion); ok {
   358  			if v == tc2 {
   359  				return v
   360  			}
   361  		}
   362  	}
   363  
   364  	return none
   365  }
   366  
   367  func (v plainVersion) Pair(r Revision) PairedVersion {
   368  	return versionPair{
   369  		v: v,
   370  		r: r,
   371  	}
   372  }
   373  
   374  func (v plainVersion) identical(c Constraint) bool {
   375  	v2, ok := c.(plainVersion)
   376  	if !ok {
   377  		return false
   378  	}
   379  	return v == v2
   380  }
   381  
   382  func (v plainVersion) copyTo(msg *pb.Constraint) {
   383  	msg.Type = pb.Constraint_Version
   384  	msg.Value = string(v)
   385  }
   386  
   387  type semVersion struct {
   388  	sv semver.Version
   389  }
   390  
   391  func (v semVersion) String() string {
   392  	str := v.sv.Original()
   393  	if str == "" {
   394  		str = v.sv.String()
   395  	}
   396  	return str
   397  }
   398  
   399  func (v semVersion) ImpliedCaretString() string {
   400  	return v.sv.ImpliedCaretString()
   401  }
   402  
   403  func (v semVersion) typedString() string {
   404  	return fmt.Sprintf("sv-%s", v.String())
   405  }
   406  
   407  func (v semVersion) Type() VersionType {
   408  	return IsSemver
   409  }
   410  
   411  func (v semVersion) Matches(v2 Version) bool {
   412  	switch tv := v2.(type) {
   413  	case semVersion:
   414  		return v.sv.Equal(tv.sv)
   415  	case versionPair:
   416  		if tv2, ok := tv.v.(semVersion); ok {
   417  			return tv2.sv.Equal(v.sv)
   418  		}
   419  	}
   420  	return false
   421  }
   422  
   423  func (v semVersion) MatchesAny(c Constraint) bool {
   424  	switch tc := c.(type) {
   425  	case anyConstraint:
   426  		return true
   427  	case noneConstraint:
   428  		return false
   429  	case semVersion:
   430  		return v.sv.Equal(tc.sv)
   431  	case semverConstraint:
   432  		return tc.Intersect(v) != none
   433  	case versionPair:
   434  		if tc2, ok := tc.v.(semVersion); ok {
   435  			return tc2.sv.Equal(v.sv)
   436  		}
   437  	}
   438  
   439  	return false
   440  }
   441  
   442  func (v semVersion) Intersect(c Constraint) Constraint {
   443  	switch tc := c.(type) {
   444  	case anyConstraint:
   445  		return v
   446  	case noneConstraint:
   447  		return none
   448  	case semVersion:
   449  		if v.sv.Equal(tc.sv) {
   450  			return v
   451  		}
   452  	case semverConstraint:
   453  		return tc.Intersect(v)
   454  	case versionPair:
   455  		if tc2, ok := tc.v.(semVersion); ok {
   456  			if v.sv.Equal(tc2.sv) {
   457  				return v
   458  			}
   459  		}
   460  	}
   461  
   462  	return none
   463  }
   464  
   465  func (v semVersion) Pair(r Revision) PairedVersion {
   466  	return versionPair{
   467  		v: v,
   468  		r: r,
   469  	}
   470  }
   471  
   472  func (v semVersion) identical(c Constraint) bool {
   473  	v2, ok := c.(semVersion)
   474  	if !ok {
   475  		return false
   476  	}
   477  	return v == v2
   478  }
   479  
   480  func (v semVersion) copyTo(msg *pb.Constraint) {
   481  	msg.Type = pb.Constraint_Semver
   482  	msg.Value = v.String() //TODO better encoding which doesn't require re-parsing
   483  }
   484  
   485  type versionPair struct {
   486  	v UnpairedVersion
   487  	r Revision
   488  }
   489  
   490  func (v versionPair) String() string {
   491  	return v.v.String()
   492  }
   493  
   494  func (v versionPair) ImpliedCaretString() string {
   495  	return v.v.ImpliedCaretString()
   496  }
   497  
   498  func (v versionPair) typedString() string {
   499  	return fmt.Sprintf("%s-%s", v.Unpair().typedString(), v.Revision().typedString())
   500  }
   501  
   502  func (v versionPair) Type() VersionType {
   503  	return v.v.Type()
   504  }
   505  
   506  func (v versionPair) Revision() Revision {
   507  	return v.r
   508  }
   509  
   510  func (v versionPair) Unpair() UnpairedVersion {
   511  	return v.v
   512  }
   513  
   514  func (v versionPair) Matches(v2 Version) bool {
   515  	switch tv2 := v2.(type) {
   516  	case versionPair:
   517  		return v.r == tv2.r
   518  	case Revision:
   519  		return v.r == tv2
   520  	}
   521  
   522  	switch tv := v.v.(type) {
   523  	case plainVersion, branchVersion:
   524  		if tv.Matches(v2) {
   525  			return true
   526  		}
   527  	case semVersion:
   528  		if tv2, ok := v2.(semVersion); ok {
   529  			if tv.sv.Equal(tv2.sv) {
   530  				return true
   531  			}
   532  		}
   533  	}
   534  
   535  	return false
   536  }
   537  
   538  func (v versionPair) MatchesAny(c2 Constraint) bool {
   539  	return c2.Matches(v)
   540  }
   541  
   542  func (v versionPair) Intersect(c2 Constraint) Constraint {
   543  	switch tc := c2.(type) {
   544  	case anyConstraint:
   545  		return v
   546  	case noneConstraint:
   547  		return none
   548  	case versionPair:
   549  		if v.r == tc.r {
   550  			return v.r
   551  		}
   552  	case Revision:
   553  		if v.r == tc {
   554  			return v.r
   555  		}
   556  	case semverConstraint:
   557  		if tv, ok := v.v.(semVersion); ok {
   558  			if tc.Intersect(tv) == v.v {
   559  				return v
   560  			}
   561  		}
   562  		// If the semver intersection failed, we know nothing could work
   563  		return none
   564  	}
   565  
   566  	switch tv := v.v.(type) {
   567  	case plainVersion, branchVersion:
   568  		if c2.Matches(v) {
   569  			return v
   570  		}
   571  	case semVersion:
   572  		if tv2, ok := c2.(semVersion); ok {
   573  			if tv.sv.Equal(tv2.sv) {
   574  				return v
   575  			}
   576  		}
   577  	}
   578  
   579  	return none
   580  }
   581  
   582  func (v versionPair) identical(c Constraint) bool {
   583  	v2, ok := c.(versionPair)
   584  	if !ok {
   585  		return false
   586  	}
   587  	if v.r != v2.r {
   588  		return false
   589  	}
   590  	return v.v.identical(v2.v)
   591  }
   592  
   593  func (v versionPair) copyTo(*pb.Constraint) {
   594  	panic("versionPair should never be serialized; it is solver internal-only")
   595  }
   596  
   597  // compareVersionType is a sort func helper that makes a coarse-grained sorting
   598  // decision based on version type.
   599  //
   600  // Make sure that l and r have already been converted from versionPair (if
   601  // applicable).
   602  func compareVersionType(l, r Version) int {
   603  	// Big fugly double type switch. No reflect, because this can be smack in a hot loop
   604  	switch l.(type) {
   605  	case Revision:
   606  		switch r.(type) {
   607  		case Revision:
   608  			return 0
   609  		case branchVersion, plainVersion, semVersion:
   610  			return 1
   611  		}
   612  
   613  	case plainVersion:
   614  		switch r.(type) {
   615  		case Revision:
   616  			return -1
   617  		case plainVersion:
   618  			return 0
   619  		case branchVersion, semVersion:
   620  			return 1
   621  		}
   622  
   623  	case branchVersion:
   624  		switch r.(type) {
   625  		case Revision, plainVersion:
   626  			return -1
   627  		case branchVersion:
   628  			return 0
   629  		case semVersion:
   630  			return 1
   631  		}
   632  
   633  	case semVersion:
   634  		switch r.(type) {
   635  		case Revision, branchVersion, plainVersion:
   636  			return -1
   637  		case semVersion:
   638  			return 0
   639  		}
   640  	}
   641  	panic("unknown version type")
   642  }
   643  
   644  // SortForUpgrade sorts a slice of []Version in roughly descending order, so
   645  // that presumably newer versions are visited first. The rules are:
   646  //
   647  //  - All semver versions come first, and sort mostly according to the semver
   648  //  2.0 spec (as implemented by github.com/Masterminds/semver lib), with one
   649  //  exception:
   650  //  - Semver versions with a prerelease are after *all* non-prerelease semver.
   651  //  Within this subset they are sorted first by their numerical component, then
   652  //  lexicographically by their prerelease version.
   653  //  - The default branch(es) is next; the exact semantics of that are specific
   654  //  to the underlying source.
   655  //  - All other branches come next, sorted lexicographically.
   656  //  - All non-semver versions (tags) are next, sorted lexicographically.
   657  //  - Revisions, if any, are last, sorted lexicographically. Revisions do not
   658  //  typically appear in version lists, so the only invariant we maintain is
   659  //  determinism - deeper semantics, like chronology or topology, do not matter.
   660  //
   661  // So, given a slice of the following versions:
   662  //
   663  //  - Branch: master devel
   664  //  - Semver tags: v1.0.0, v1.1.0, v1.1.0-alpha1
   665  //  - Non-semver tags: footag
   666  //  - Revision: f6e74e8d
   667  //
   668  // Sorting for upgrade will result in the following slice:
   669  //
   670  //  [v1.1.0 v1.0.0 v1.1.0-alpha1 master devel footag f6e74e8d]
   671  func SortForUpgrade(vl []Version) {
   672  	sort.Sort(upgradeVersionSorter(vl))
   673  }
   674  
   675  // SortPairedForUpgrade has the same behavior as SortForUpgrade, but operates on
   676  // []PairedVersion types.
   677  func SortPairedForUpgrade(vl []PairedVersion) {
   678  	sort.Sort(pvupgradeVersionSorter(vl))
   679  }
   680  
   681  // SortForDowngrade sorts a slice of []Version in roughly ascending order, so
   682  // that presumably older versions are visited first.
   683  //
   684  // This is *not* the same as reversing SortForUpgrade (or you could simply
   685  // sort.Reverse()). The type precedence is the same, including the semver vs.
   686  // semver-with-prerelease relation. Lexicographical comparisons within
   687  // non-semver tags, branches, and revisions remains the same as well; because we
   688  // treat these domains as having no ordering relation, there can be no real
   689  // concept of "upgrade" vs "downgrade", so there is no reason to reverse them.
   690  //
   691  // Thus, the only binary relation that is reversed for downgrade is within-type
   692  // comparisons for semver.
   693  //
   694  // So, given a slice of the following versions:
   695  //
   696  //  - Branch: master devel
   697  //  - Semver tags: v1.0.0, v1.1.0, v1.1.0-alpha1
   698  //  - Non-semver tags: footag
   699  //  - Revision: f6e74e8d
   700  //
   701  // Sorting for downgrade will result in the following slice:
   702  //
   703  //  [v1.0.0 v1.1.0 v1.1.0-alpha1 footag devel master f6e74e8d]
   704  func SortForDowngrade(vl []Version) {
   705  	sort.Sort(downgradeVersionSorter(vl))
   706  }
   707  
   708  // SortPairedForDowngrade has the same behavior as SortForDowngrade, but
   709  // operates on []PairedVersion types.
   710  func SortPairedForDowngrade(vl []PairedVersion) {
   711  	sort.Sort(pvdowngradeVersionSorter(vl))
   712  }
   713  
   714  type upgradeVersionSorter []Version
   715  
   716  func (vs upgradeVersionSorter) Len() int {
   717  	return len(vs)
   718  }
   719  
   720  func (vs upgradeVersionSorter) Swap(i, j int) {
   721  	vs[i], vs[j] = vs[j], vs[i]
   722  }
   723  
   724  func (vs upgradeVersionSorter) Less(i, j int) bool {
   725  	l, r := vs[i], vs[j]
   726  	return vLess(l, r, false)
   727  }
   728  
   729  type pvupgradeVersionSorter []PairedVersion
   730  
   731  func (vs pvupgradeVersionSorter) Len() int {
   732  	return len(vs)
   733  }
   734  
   735  func (vs pvupgradeVersionSorter) Swap(i, j int) {
   736  	vs[i], vs[j] = vs[j], vs[i]
   737  }
   738  func (vs pvupgradeVersionSorter) Less(i, j int) bool {
   739  	l, r := vs[i], vs[j]
   740  	return vLess(l, r, false)
   741  }
   742  
   743  type downgradeVersionSorter []Version
   744  
   745  func (vs downgradeVersionSorter) Len() int {
   746  	return len(vs)
   747  }
   748  
   749  func (vs downgradeVersionSorter) Swap(i, j int) {
   750  	vs[i], vs[j] = vs[j], vs[i]
   751  }
   752  
   753  func (vs downgradeVersionSorter) Less(i, j int) bool {
   754  	l, r := vs[i], vs[j]
   755  	return vLess(l, r, true)
   756  }
   757  
   758  type pvdowngradeVersionSorter []PairedVersion
   759  
   760  func (vs pvdowngradeVersionSorter) Len() int {
   761  	return len(vs)
   762  }
   763  
   764  func (vs pvdowngradeVersionSorter) Swap(i, j int) {
   765  	vs[i], vs[j] = vs[j], vs[i]
   766  }
   767  func (vs pvdowngradeVersionSorter) Less(i, j int) bool {
   768  	l, r := vs[i], vs[j]
   769  	return vLess(l, r, true)
   770  }
   771  
   772  func vLess(l, r Version, down bool) bool {
   773  	if tl, ispair := l.(versionPair); ispair {
   774  		l = tl.v
   775  	}
   776  	if tr, ispair := r.(versionPair); ispair {
   777  		r = tr.v
   778  	}
   779  
   780  	switch compareVersionType(l, r) {
   781  	case -1:
   782  		return true
   783  	case 1:
   784  		return false
   785  	case 0:
   786  		break
   787  	default:
   788  		panic("unreachable")
   789  	}
   790  
   791  	switch tl := l.(type) {
   792  	case branchVersion:
   793  		tr := r.(branchVersion)
   794  		if tl.isDefault != tr.isDefault {
   795  			// If they're not both defaults, then return the left val: if left
   796  			// is the default, then it is "less" (true) b/c we want it earlier.
   797  			// Else the right is the default, and so the left should be later
   798  			// (false).
   799  			return tl.isDefault
   800  		}
   801  		return l.String() < r.String()
   802  	case Revision, plainVersion:
   803  		// All that we can do now is alpha sort
   804  		return l.String() < r.String()
   805  	}
   806  
   807  	// This ensures that pre-release versions are always sorted after ALL
   808  	// full-release versions
   809  	lsv, rsv := l.(semVersion).sv, r.(semVersion).sv
   810  	lpre, rpre := lsv.Prerelease() == "", rsv.Prerelease() == ""
   811  	if (lpre && !rpre) || (!lpre && rpre) {
   812  		return lpre
   813  	}
   814  
   815  	if down {
   816  		return lsv.LessThan(rsv)
   817  	}
   818  	return lsv.GreaterThan(rsv)
   819  }
   820  
   821  func hidePair(pvl []PairedVersion) []Version {
   822  	vl := make([]Version, 0, len(pvl))
   823  	for _, v := range pvl {
   824  		vl = append(vl, v)
   825  	}
   826  	return vl
   827  }
   828  
   829  // VersionComponentStrings decomposes a Version into the underlying number, branch and revision.
   830  func VersionComponentStrings(v Version) (revision string, branch string, version string) {
   831  	switch tv := v.(type) {
   832  	case UnpairedVersion:
   833  	case Revision:
   834  		revision = tv.String()
   835  	case PairedVersion:
   836  		revision = tv.Revision().String()
   837  	}
   838  
   839  	switch v.Type() {
   840  	case IsBranch:
   841  		branch = v.String()
   842  	case IsSemver, IsVersion:
   843  		version = v.String()
   844  	}
   845  
   846  	return
   847  }