2016-03-16 23:34:09 +03:00
|
|
|
package vsolver
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
|
|
|
|
"github.com/Masterminds/semver"
|
|
|
|
)
|
|
|
|
|
|
|
|
type Constraint interface {
|
|
|
|
Type() ConstraintType
|
|
|
|
Body() string
|
2016-03-30 17:39:22 +03:00
|
|
|
Admits(Version) bool
|
|
|
|
AdmitsAny(Constraint) bool
|
2016-04-01 18:32:40 +03:00
|
|
|
Intersect(Constraint) Constraint
|
2016-03-16 23:34:09 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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
|
|
|
|
}
|
|
|
|
|
2016-03-30 17:39:22 +03:00
|
|
|
func (c basicConstraint) Admits(v Version) bool {
|
2016-03-16 23:34:09 +03:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2016-03-30 17:39:22 +03:00
|
|
|
func (c basicConstraint) AdmitsAny(c2 Constraint) bool {
|
|
|
|
return (c2.Type() == c.typ && c2.Body() == c.body) || c2.AdmitsAny(c)
|
2016-03-16 23:34:09 +03:00
|
|
|
}
|
|
|
|
|
2016-04-01 18:32:40 +03:00
|
|
|
func (c basicConstraint) Intersect(c2 Constraint) Constraint {
|
|
|
|
if c.AdmitsAny(c2) {
|
|
|
|
return c
|
|
|
|
}
|
|
|
|
|
|
|
|
return noneConstraint{}
|
|
|
|
}
|
|
|
|
|
2016-03-16 23:34:09 +03:00
|
|
|
// anyConstraint is an unbounded constraint - it matches all other types of
|
|
|
|
// constraints.
|
|
|
|
type anyConstraint struct{}
|
|
|
|
|
2016-04-01 18:32:40 +03:00
|
|
|
func (anyConstraint) Type() ConstraintType {
|
2016-03-16 23:34:09 +03:00
|
|
|
return C_ExactMatch | C_FlexMatch
|
|
|
|
}
|
|
|
|
|
2016-04-01 18:32:40 +03:00
|
|
|
func (anyConstraint) Body() string {
|
2016-03-16 23:34:09 +03:00
|
|
|
return "*"
|
|
|
|
}
|
|
|
|
|
2016-04-01 18:32:40 +03:00
|
|
|
func (anyConstraint) Admits(v Version) bool {
|
2016-03-16 23:34:09 +03:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2016-04-01 18:32:40 +03:00
|
|
|
func (anyConstraint) AdmitsAny(Constraint) bool {
|
2016-03-16 23:34:09 +03:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2016-04-01 18:32:40 +03:00
|
|
|
func (anyConstraint) Intersect(c Constraint) Constraint {
|
|
|
|
return c
|
|
|
|
}
|
|
|
|
|
2016-03-16 23:34:09 +03:00
|
|
|
type semverConstraint struct {
|
|
|
|
// The type of constraint - single semver, or semver range
|
|
|
|
typ ConstraintType
|
|
|
|
// The string text of the constraint
|
|
|
|
body string
|
2016-04-01 18:32:40 +03:00
|
|
|
c semver.Constraint
|
2016-03-16 23:34:09 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
func (c semverConstraint) Type() ConstraintType {
|
|
|
|
return c.typ
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c semverConstraint) Body() string {
|
|
|
|
return c.body
|
|
|
|
}
|
|
|
|
|
2016-03-30 17:39:22 +03:00
|
|
|
func (c semverConstraint) Admits(v Version) bool {
|
2016-03-16 23:34:09 +03:00
|
|
|
if VTCTCompat[v.Type]&c.typ == 0 {
|
|
|
|
// version and constraint types are incompatible
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2016-04-01 18:32:40 +03:00
|
|
|
return c.c.Admits(v.SemVer) == nil
|
2016-03-16 23:34:09 +03:00
|
|
|
}
|
|
|
|
|
2016-03-30 17:39:22 +03:00
|
|
|
func (c semverConstraint) AdmitsAny(c2 Constraint) bool {
|
2016-03-16 23:34:09 +03:00
|
|
|
if c2.Type()&(C_Semver|C_SemverRange) == 0 {
|
|
|
|
// Union only possible if other constraint is semverish
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2016-04-01 18:32:40 +03:00
|
|
|
return c.c.AdmitsAny(c2.(semverConstraint).c)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c semverConstraint) Intersect(c2 Constraint) Constraint {
|
|
|
|
// TODO This won't actually be OK, long term
|
|
|
|
if sv, ok := c2.(semverConstraint); ok {
|
|
|
|
i := c.c.Intersect(sv.c)
|
|
|
|
if !semver.IsNone(i) {
|
|
|
|
return semverConstraint{
|
|
|
|
typ: C_SemverRange, // TODO get rid of the range/non-range distinction
|
|
|
|
c: i,
|
|
|
|
body: i.String(), // TODO this is costly - defer it by making it a method
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return noneConstraint{}
|
|
|
|
}
|
|
|
|
|
|
|
|
type noneConstraint struct{}
|
|
|
|
|
|
|
|
func (noneConstraint) Type() ConstraintType {
|
|
|
|
return C_FlexMatch | C_ExactMatch
|
|
|
|
}
|
|
|
|
|
|
|
|
func (noneConstraint) Body() string {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
|
|
|
func (noneConstraint) Admits(Version) bool {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
func (noneConstraint) AdmitsAny(Constraint) bool {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
func (noneConstraint) Intersect(Constraint) Constraint {
|
|
|
|
return noneConstraint{}
|
2016-03-16 23:34:09 +03:00
|
|
|
}
|