constraint.go 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. package version
  2. import (
  3. "fmt"
  4. "reflect"
  5. "regexp"
  6. "strings"
  7. )
  8. // Constraint represents a single constraint for a version, such as
  9. // ">= 1.0".
  10. type Constraint struct {
  11. f constraintFunc
  12. check *Version
  13. original string
  14. }
  15. // Constraints is a slice of constraints. We make a custom type so that
  16. // we can add methods to it.
  17. type Constraints []*Constraint
  18. type constraintFunc func(v, c *Version) bool
  19. var constraintOperators map[string]constraintFunc
  20. var constraintRegexp *regexp.Regexp
  21. func init() {
  22. constraintOperators = map[string]constraintFunc{
  23. "": constraintEqual,
  24. "=": constraintEqual,
  25. "!=": constraintNotEqual,
  26. ">": constraintGreaterThan,
  27. "<": constraintLessThan,
  28. ">=": constraintGreaterThanEqual,
  29. "<=": constraintLessThanEqual,
  30. "~>": constraintPessimistic,
  31. }
  32. ops := make([]string, 0, len(constraintOperators))
  33. for k := range constraintOperators {
  34. ops = append(ops, regexp.QuoteMeta(k))
  35. }
  36. constraintRegexp = regexp.MustCompile(fmt.Sprintf(
  37. `^\s*(%s)\s*(%s)\s*$`,
  38. strings.Join(ops, "|"),
  39. VersionRegexpRaw))
  40. }
  41. // NewConstraint will parse one or more constraints from the given
  42. // constraint string. The string must be a comma-separated list of
  43. // constraints.
  44. func NewConstraint(v string) (Constraints, error) {
  45. vs := strings.Split(v, ",")
  46. result := make([]*Constraint, len(vs))
  47. for i, single := range vs {
  48. c, err := parseSingle(single)
  49. if err != nil {
  50. return nil, err
  51. }
  52. result[i] = c
  53. }
  54. return Constraints(result), nil
  55. }
  56. // Check tests if a version satisfies all the constraints.
  57. func (cs Constraints) Check(v *Version) bool {
  58. for _, c := range cs {
  59. if !c.Check(v) {
  60. return false
  61. }
  62. }
  63. return true
  64. }
  65. // Returns the string format of the constraints
  66. func (cs Constraints) String() string {
  67. csStr := make([]string, len(cs))
  68. for i, c := range cs {
  69. csStr[i] = c.String()
  70. }
  71. return strings.Join(csStr, ",")
  72. }
  73. // Check tests if a constraint is validated by the given version.
  74. func (c *Constraint) Check(v *Version) bool {
  75. return c.f(v, c.check)
  76. }
  77. func (c *Constraint) String() string {
  78. return c.original
  79. }
  80. func parseSingle(v string) (*Constraint, error) {
  81. matches := constraintRegexp.FindStringSubmatch(v)
  82. if matches == nil {
  83. return nil, fmt.Errorf("Malformed constraint: %s", v)
  84. }
  85. check, err := NewVersion(matches[2])
  86. if err != nil {
  87. return nil, err
  88. }
  89. return &Constraint{
  90. f: constraintOperators[matches[1]],
  91. check: check,
  92. original: v,
  93. }, nil
  94. }
  95. func prereleaseCheck(v, c *Version) bool {
  96. switch vPre, cPre := v.Prerelease() != "", c.Prerelease() != ""; {
  97. case cPre && vPre:
  98. // A constraint with a pre-release can only match a pre-release version
  99. // with the same base segments.
  100. return reflect.DeepEqual(c.Segments64(), v.Segments64())
  101. case !cPre && vPre:
  102. // A constraint without a pre-release can only match a version without a
  103. // pre-release.
  104. return false
  105. case cPre && !vPre:
  106. // OK, except with the pessimistic operator
  107. case !cPre && !vPre:
  108. // OK
  109. }
  110. return true
  111. }
  112. //-------------------------------------------------------------------
  113. // Constraint functions
  114. //-------------------------------------------------------------------
  115. func constraintEqual(v, c *Version) bool {
  116. return v.Equal(c)
  117. }
  118. func constraintNotEqual(v, c *Version) bool {
  119. return !v.Equal(c)
  120. }
  121. func constraintGreaterThan(v, c *Version) bool {
  122. return prereleaseCheck(v, c) && v.Compare(c) == 1
  123. }
  124. func constraintLessThan(v, c *Version) bool {
  125. return prereleaseCheck(v, c) && v.Compare(c) == -1
  126. }
  127. func constraintGreaterThanEqual(v, c *Version) bool {
  128. return prereleaseCheck(v, c) && v.Compare(c) >= 0
  129. }
  130. func constraintLessThanEqual(v, c *Version) bool {
  131. return prereleaseCheck(v, c) && v.Compare(c) <= 0
  132. }
  133. func constraintPessimistic(v, c *Version) bool {
  134. // Using a pessimistic constraint with a pre-release, restricts versions to pre-releases
  135. if !prereleaseCheck(v, c) || (c.Prerelease() != "" && v.Prerelease() == "") {
  136. return false
  137. }
  138. // If the version being checked is naturally less than the constraint, then there
  139. // is no way for the version to be valid against the constraint
  140. if v.LessThan(c) {
  141. return false
  142. }
  143. // We'll use this more than once, so grab the length now so it's a little cleaner
  144. // to write the later checks
  145. cs := len(c.segments)
  146. // If the version being checked has less specificity than the constraint, then there
  147. // is no way for the version to be valid against the constraint
  148. if cs > len(v.segments) {
  149. return false
  150. }
  151. // Check the segments in the constraint against those in the version. If the version
  152. // being checked, at any point, does not have the same values in each index of the
  153. // constraints segments, then it cannot be valid against the constraint.
  154. for i := 0; i < c.si-1; i++ {
  155. if v.segments[i] != c.segments[i] {
  156. return false
  157. }
  158. }
  159. // Check the last part of the segment in the constraint. If the version segment at
  160. // this index is less than the constraints segment at this index, then it cannot
  161. // be valid against the constraint
  162. if c.segments[cs-1] > v.segments[cs-1] {
  163. return false
  164. }
  165. // If nothing has rejected the version by now, it's valid
  166. return true
  167. }