13 noneWildcard wildcardType = iota
14 majorWildcard wildcardType = 1
15 minorWildcard wildcardType = 2
16 patchWildcard wildcardType = 3
19 func wildcardTypefromInt(i int) wildcardType {
32 type comparator func(Version, Version) bool
35 compEQ comparator = func(v1 Version, v2 Version) bool {
36 return v1.Compare(v2) == 0
38 compNE = func(v1 Version, v2 Version) bool {
39 return v1.Compare(v2) != 0
41 compGT = func(v1 Version, v2 Version) bool {
42 return v1.Compare(v2) == 1
44 compGE = func(v1 Version, v2 Version) bool {
45 return v1.Compare(v2) >= 0
47 compLT = func(v1 Version, v2 Version) bool {
48 return v1.Compare(v2) == -1
50 compLE = func(v1 Version, v2 Version) bool {
51 return v1.Compare(v2) <= 0
55 type versionRange struct {
60 // rangeFunc creates a Range from the given versionRange.
61 func (vr *versionRange) rangeFunc() Range {
62 return Range(func(v Version) bool {
67 // Range represents a range of versions.
68 // A Range can be used to check if a Version satisfies it:
70 // range, err := semver.ParseRange(">1.0.0 <2.0.0")
71 // range(semver.MustParse("1.1.1") // returns true
72 type Range func(Version) bool
74 // OR combines the existing Range with another Range using logical OR.
75 func (rf Range) OR(f Range) Range {
76 return Range(func(v Version) bool {
81 // AND combines the existing Range with another Range using logical AND.
82 func (rf Range) AND(f Range) Range {
83 return Range(func(v Version) bool {
88 // ParseRange parses a range and returns a Range.
89 // If the range could not be parsed an error is returned.
96 // - "1.0.0", "=1.0.0", "==1.0.0"
97 // - "!1.0.0", "!=1.0.0"
99 // A Range can consist of multiple ranges separated by space:
100 // Ranges can be linked by logical AND:
101 // - ">1.0.0 <2.0.0" would match between both ranges, so "1.1.1" and "1.8.7" but not "1.0.0" or "2.0.0"
102 // - ">1.0.0 <3.0.0 !2.0.3-beta.2" would match every version between 1.0.0 and 3.0.0 except 2.0.3-beta.2
104 // Ranges can also be linked by logical OR:
105 // - "<2.0.0 || >=3.0.0" would match "1.x.x" and "3.x.x" but not "2.x.x"
107 // AND has a higher precedence than OR. It's not possible to use brackets.
109 // Ranges can be combined by both AND and OR
111 // - `>1.0.0 <2.0.0 || >3.0.0 !4.2.1` would match `1.2.3`, `1.9.9`, `3.1.1`, but not `4.2.1`, `2.1.1`
112 func ParseRange(s string) (Range, error) {
113 parts := splitAndTrim(s)
114 orParts, err := splitORParts(parts)
118 expandedParts, err := expandWildcardVersion(orParts)
123 for _, p := range expandedParts {
125 for _, ap := range p {
126 opStr, vStr, err := splitComparatorVersion(ap)
130 vr, err := buildVersionRange(opStr, vStr)
132 return nil, fmt.Errorf("Could not parse Range %q: %s", ap, err)
139 } else { // Combine with existing function
140 andFn = andFn.AND(rf)
146 orFn = orFn.OR(andFn)
153 // splitORParts splits the already cleaned parts by '||'.
154 // Checks for invalid positions of the operator and returns an
156 func splitORParts(parts []string) ([][]string, error) {
157 var ORparts [][]string
159 for i, p := range parts {
162 return nil, fmt.Errorf("First element in range is '||'")
164 ORparts = append(ORparts, parts[last:i])
168 if last == len(parts) {
169 return nil, fmt.Errorf("Last element in range is '||'")
171 ORparts = append(ORparts, parts[last:])
175 // buildVersionRange takes a slice of 2: operator and version
176 // and builds a versionRange, otherwise an error.
177 func buildVersionRange(opStr, vStr string) (*versionRange, error) {
178 c := parseComparator(opStr)
180 return nil, fmt.Errorf("Could not parse comparator %q in %q", opStr, strings.Join([]string{opStr, vStr}, ""))
182 v, err := Parse(vStr)
184 return nil, fmt.Errorf("Could not parse version %q in %q: %s", vStr, strings.Join([]string{opStr, vStr}, ""), err)
187 return &versionRange{
194 // inArray checks if a byte is contained in an array of bytes
195 func inArray(s byte, list []byte) bool {
196 for _, el := range list {
204 // splitAndTrim splits a range string by spaces and cleans whitespaces
205 func splitAndTrim(s string) (result []string) {
208 excludeFromSplit := []byte{'>', '<', '='}
209 for i := 0; i < len(s); i++ {
210 if s[i] == ' ' && !inArray(lastChar, excludeFromSplit) {
212 result = append(result, s[last:i])
215 } else if s[i] != ' ' {
220 result = append(result, s[last:])
223 for i, v := range result {
224 result[i] = strings.Replace(v, " ", "", -1)
227 // parts := strings.Split(s, " ")
228 // for _, x := range parts {
229 // if s := strings.TrimSpace(x); len(s) != 0 {
230 // result = append(result, s)
236 // splitComparatorVersion splits the comparator from the version.
237 // Input must be free of leading or trailing spaces.
238 func splitComparatorVersion(s string) (string, string, error) {
239 i := strings.IndexFunc(s, unicode.IsDigit)
241 return "", "", fmt.Errorf("Could not get version from string: %q", s)
243 return strings.TrimSpace(s[0:i]), s[i:], nil
246 // getWildcardType will return the type of wildcard that the
247 // passed version contains
248 func getWildcardType(vStr string) wildcardType {
249 parts := strings.Split(vStr, ".")
251 wildcard := parts[nparts-1]
253 possibleWildcardType := wildcardTypefromInt(nparts)
255 return possibleWildcardType
261 // createVersionFromWildcard will convert a wildcard version
262 // into a regular version, replacing 'x's with '0's, handling
263 // special cases like '1.x.x' and '1.x'
264 func createVersionFromWildcard(vStr string) string {
266 vStr2 := strings.Replace(vStr, ".x.x", ".x", 1)
267 vStr2 = strings.Replace(vStr2, ".x", ".0", 1)
268 parts := strings.Split(vStr2, ".")
278 // incrementMajorVersion will increment the major version
279 // of the passed version
280 func incrementMajorVersion(vStr string) (string, error) {
281 parts := strings.Split(vStr, ".")
282 i, err := strconv.Atoi(parts[0])
286 parts[0] = strconv.Itoa(i + 1)
288 return strings.Join(parts, "."), nil
291 // incrementMajorVersion will increment the minor version
292 // of the passed version
293 func incrementMinorVersion(vStr string) (string, error) {
294 parts := strings.Split(vStr, ".")
295 i, err := strconv.Atoi(parts[1])
299 parts[1] = strconv.Itoa(i + 1)
301 return strings.Join(parts, "."), nil
304 // expandWildcardVersion will expand wildcards inside versions
305 // following these rules:
307 // * when dealing with patch wildcards:
308 // >= 1.2.x will become >= 1.2.0
309 // <= 1.2.x will become < 1.3.0
310 // > 1.2.x will become >= 1.3.0
311 // < 1.2.x will become < 1.2.0
312 // != 1.2.x will become < 1.2.0 >= 1.3.0
314 // * when dealing with minor wildcards:
315 // >= 1.x will become >= 1.0.0
316 // <= 1.x will become < 2.0.0
317 // > 1.x will become >= 2.0.0
318 // < 1.0 will become < 1.0.0
319 // != 1.x will become < 1.0.0 >= 2.0.0
321 // * when dealing with wildcards without
323 // 1.2.x will become >= 1.2.0 < 1.3.0
324 // 1.x will become >= 1.0.0 < 2.0.0
325 func expandWildcardVersion(parts [][]string) ([][]string, error) {
326 var expandedParts [][]string
327 for _, p := range parts {
328 var newParts []string
329 for _, ap := range p {
330 if strings.Contains(ap, "x") {
331 opStr, vStr, err := splitComparatorVersion(ap)
336 versionWildcardType := getWildcardType(vStr)
337 flatVersion := createVersionFromWildcard(vStr)
339 var resultOperator string
340 var shouldIncrementVersion bool
343 resultOperator = ">="
344 shouldIncrementVersion = true
346 resultOperator = ">="
351 shouldIncrementVersion = true
353 newParts = append(newParts, ">="+flatVersion)
355 shouldIncrementVersion = true
357 newParts = append(newParts, "<"+flatVersion)
358 resultOperator = ">="
359 shouldIncrementVersion = true
362 var resultVersion string
363 if shouldIncrementVersion {
364 switch versionWildcardType {
366 resultVersion, _ = incrementMinorVersion(flatVersion)
368 resultVersion, _ = incrementMajorVersion(flatVersion)
371 resultVersion = flatVersion
374 ap = resultOperator + resultVersion
376 newParts = append(newParts, ap)
378 expandedParts = append(expandedParts, newParts)
381 return expandedParts, nil
384 func parseComparator(s string) comparator {
409 // MustParseRange is like ParseRange but panics if the range cannot be parsed.
410 func MustParseRange(s string) Range {
411 r, err := ParseRange(s)
413 panic(`semver: ParseRange(` + s + `): ` + err.Error())