Skip to content

Instantly share code, notes, and snippets.

@Jarred-Sumner
Created August 23, 2021 13:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Jarred-Sumner/d6069033db005fe8d3c102f3d3055015 to your computer and use it in GitHub Desktop.
Save Jarred-Sumner/d6069033db005fe8d3c102f3d3055015 to your computer and use it in GitHub Desktop.
//go:generate go-enum -f=$GOFILE --marshal
// originally a fork of https://github.com/blang/semver iirc
package node_semver
import (
"math"
"math/bits"
"sort"
"strconv"
"strings"
"sync"
jsoniter "github.com/json-iterator/go"
)
type WildcardType byte
const (
NoneWildcard WildcardType = iota
MajorWildcard WildcardType = 1
MinorWildcard WildcardType = 2
PatchWildcard WildcardType = 3
)
type comparator func(Version, Version) bool
var (
compEQ comparator = func(v1 Version, v2 Version) bool {
return v1.Compare(v2) == 0
}
compNE comparator = func(v1 Version, v2 Version) bool {
return v1.Compare(v2) != 0
}
compGT comparator = func(v1 Version, v2 Version) bool {
return v1.Compare(v2) == 1
}
compGE comparator = func(v1 Version, v2 Version) bool {
return v1.Compare(v2) >= 0
}
compLT comparator = func(v1 Version, v2 Version) bool {
return v1.Compare(v2) == -1
}
compLE comparator = func(v1 Version, v2 Version) bool {
return v1.Compare(v2) <= 0
}
)
type versionRange struct {
v Version
c comparator
}
type Version struct {
Major uint64
Minor uint64
Patch uint64
Pre []string
Build []string
}
// MarshalJSON implements the encoding/json.Marshaler interface.
func (v Version) MarshalJSON() ([]byte, error) {
return jsoniter.Marshal(v.String())
}
// UnmarshalJSON implements the encoding/json.Unmarshaler interface.
func (v Version) UnmarshalJSON(data []byte) (err error) {
var versionString string
if err = jsoniter.Unmarshal(data, &versionString); err != nil {
return
}
v = *Tokenize(versionString).Version
return
}
// Version to string
func (v Version) String() string {
b := strings.Builder{}
majorLen := bits.Len64(v.Major) / 8
if majorLen == 0 {
majorLen = 1
}
minorLen := bits.Len64(v.Major) / 8
if minorLen == 0 {
minorLen = 1
}
patchLen := bits.Len64(v.Major) / 8
if patchLen == 0 {
patchLen = 1
}
preLen := len(v.Pre)
for _, pre := range v.Pre {
preLen += len(pre)
}
buildLen := len(v.Build)
for _, pre := range v.Build {
preLen += len(pre)
}
b.Grow(majorLen + minorLen + patchLen + preLen + buildLen + 2)
b.WriteString(strconv.FormatUint(v.Major, 10))
b.WriteRune('.')
b.WriteString(strconv.FormatUint(v.Minor, 10))
b.WriteRune('.')
b.WriteString(strconv.FormatUint(v.Patch, 10))
if len(v.Pre) > 0 {
b.WriteRune('-')
b.WriteString(v.Pre[0])
for _, pre := range v.Pre[1:] {
b.WriteRune('.')
b.WriteString(pre)
}
}
if len(v.Build) > 0 {
b.WriteRune('+')
b.WriteString(v.Build[0])
for _, pre := range v.Build[1:] {
b.WriteRune('.')
b.WriteString(pre)
}
}
return b.String()
}
func (v *Version) Reset() {
v.Major = 0
v.Minor = 0
v.Patch = 0
v.Build = nil
v.Pre = nil
}
func (v *Version) CopyTo(other *Version) {
other.Major = v.Major
other.Minor = v.Minor
other.Patch = v.Patch
other.Build = v.Build
other.Pre = v.Pre
}
func (vv Version) ToRange(comp comparator) Range {
return Range(func(v Version) bool {
return comp(v, vv)
})
}
func (vv *Version) ToWildcardRange(wildcard WildcardType) Range {
switch wildcard {
case MajorWildcard:
{
vv.Reset()
return vv.ToRange(compGE)
}
case MinorWildcard:
{
v := Version{
Major: vv.Major + 1,
}
vv.Reset()
vv.Major = v.Major - 1
return v.ToRange(compLT).AND(vv.ToRange(compGE))
}
case PatchWildcard:
{
v := Version{
Major: vv.Major,
Minor: vv.Minor + 1,
}
vv.Build = nil
vv.Pre = nil
return v.ToRange(compLT).AND(vv.ToRange(compGE))
}
default:
{
return vv.ToRange(compEQ)
}
}
}
func (v *Version) IsEmpty() bool {
return v.Build == nil && v.Major == 0 && v.Minor == 0 && v.Patch == 0 && v.Pre == nil
}
// Equals checks if v is equal to o.
func (v Version) Equals(o Version) bool {
return (v.Compare(o) == 0)
}
// EQ checks if v is equal to o.
func (v Version) EQ(o Version) bool {
return (v.Compare(o) == 0)
}
// NE checks if v is not equal to o.
func (v Version) NE(o Version) bool {
return (v.Compare(o) != 0)
}
// GT checks if v is greater than o.
func (v Version) GT(o Version) bool {
return (v.Compare(o) == 1)
}
// GTE checks if v is greater than or equal to o.
func (v Version) GTE(o Version) bool {
return (v.Compare(o) >= 0)
}
// GE checks if v is greater than or equal to o.
func (v Version) GE(o Version) bool {
return (v.Compare(o) >= 0)
}
// LT checks if v is less than o.
func (v Version) LT(o Version) bool {
return (v.Compare(o) == -1)
}
// LTE checks if v is less than or equal to o.
func (v Version) LTE(o Version) bool {
return (v.Compare(o) <= 0)
}
// LE checks if v is less than or equal to o.
func (v Version) LE(o Version) bool {
return (v.Compare(o) <= 0)
}
func (v Version) SortableCompare(o Version) int {
hasPre := len(v.Pre) > 0
otherHasPre := len(o.Pre) > 0
if hasPre != otherHasPre && otherHasPre {
return 1
}
if v.Major > o.Major {
return 1
} else if v.Major < o.Major {
return -1
} else if v.Minor > o.Minor {
return 1
} else if v.Minor < o.Minor {
return -1
} else if v.Patch > o.Patch {
return 1
} else if v.Patch < o.Patch {
return -1
}
hasBuild := len(v.Build) > 0
otherHasBuild := len(o.Build) > 0
if hasBuild && !otherHasBuild {
return 1
} else if !hasBuild && otherHasBuild {
return -1
}
// i := 0
// buildComparator := 0
// preComparator := 0
// if hasPre && otherHasPre {
// for ; i < len(v.Pre) && i < len(o.Pre); i++ {
// if comp := strings.Compare(v.Pre[i], o.Pre[i]); comp == 0 {
// continue
// } else if comp == 1 {
// preComparator = 1
// break
// } else {
// preComparator = -1
// break
// }
// }
// } else if hasPre {
// preComparator = -1
// } else if otherHasPre {
// preComparator = 1
// }
// if hasBuild && !hasPre && otherHasPre {
// return 1
// }
// if hasBuild && hasPre && !otherHasPre && otherHasBuild {
// return -1
// }
// if hasPre && hasBuild && otherHasPre && !otherHasBuild {
// return 1
// } else if hasPre && hasBuild && otherHasPre && otherHasBuild {
// return 0
// } else if hasPre && !hasBuild && !otherHasPre && !otherHasBuild {
// return -1
// } else if !hasPre && hasBuild && !otherHasPre && !otherHasBuild {
// return 1
// } else if !hasPre && hasBuild && otherHasPre && !otherHasBuild {
// return 1
// }
// if hasPre && otherHasPre {
// return 0
// } // if hasBuild && otherHasBuild {
// for ; i < len(v.Build) && i < len(o.Build); i++ {
// if comp := strings.Compare(v.Build[i], o.Build[i]); comp == 0 {
// continue
// } else if comp == 1 {
// buildComparator = 1
// break
// } else {
// buildComparator = -1
// break
// }
// }
// } else if hasBuild {
// buildComparator = 1
// } else if otherHasBuild {
// buildComparator = -1
// }
// if preComparator != 0 {
// return preComparator
// }
return 0
// return buildComparator
}
// Compare compares Versions v to o:
// -1 == v is less than o
// 0 == v is equal to o
// 1 == v is greater than o
func (v Version) Compare(o Version) int {
hasPre := len(v.Pre) > 0
otherHasPre := len(o.Pre) > 0
if hasPre != otherHasPre && otherHasPre {
return 1
}
// if hasPre && !otherHasPre {
// return -1
// } else if otherHasPre && !hasPre {
// return 1
// }
if v.Major > o.Major {
return 1
} else if v.Major < o.Major {
return -1
} else if v.Minor > o.Minor {
return 1
} else if v.Minor < o.Minor {
return -1
} else if v.Patch > o.Patch {
return 1
} else if v.Patch < o.Patch {
return -1
}
hasBuild := len(v.Build) > 0
otherHasBuild := len(o.Build) > 0
if hasBuild && !otherHasBuild {
return 1
} else if !hasBuild && otherHasBuild {
return -1
}
// i := 0
// buildComparator := 0
// preComparator := 0
// if hasPre && otherHasPre {
// for ; i < len(v.Pre) && i < len(o.Pre); i++ {
// if comp := strings.Compare(v.Pre[i], o.Pre[i]); comp == 0 {
// continue
// } else if comp == 1 {
// preComparator = 1
// break
// } else {
// preComparator = -1
// break
// }
// }
// } else if hasPre {
// preComparator = -1
// } else if otherHasPre {
// preComparator = 1
// }
// if hasBuild && !hasPre && otherHasPre {
// return 1
// }
// if hasBuild && hasPre && !otherHasPre && otherHasBuild {
// return -1
// }
// if hasPre && hasBuild && otherHasPre && !otherHasBuild {
// return 1
// } else if hasPre && hasBuild && otherHasPre && otherHasBuild {
// return 0
// } else if hasPre && !hasBuild && !otherHasPre && !otherHasBuild {
// return -1
// } else if !hasPre && hasBuild && !otherHasPre && !otherHasBuild {
// return 1
// } else if !hasPre && hasBuild && otherHasPre && !otherHasBuild {
// return 1
// }
// if hasPre && otherHasPre {
// return 0
// } // if hasBuild && otherHasBuild {
// for ; i < len(v.Build) && i < len(o.Build); i++ {
// if comp := strings.Compare(v.Build[i], o.Build[i]); comp == 0 {
// continue
// } else if comp == 1 {
// buildComparator = 1
// break
// } else {
// buildComparator = -1
// break
// }
// }
// } else if hasBuild {
// buildComparator = 1
// } else if otherHasBuild {
// buildComparator = -1
// }
// if preComparator != 0 {
// return preComparator
// }
// return buildComparator
return 0
}
// rangeFunc creates a Range from the given versionRange.
func (vr *versionRange) rangeFunc() Range {
return Range(func(v Version) bool {
return vr.c(v, vr.v)
})
}
type Range func(Version) bool
// OR combines the existing Range with another Range using logical OR.
func (rf Range) OR(f Range) Range {
return Range(func(v Version) bool {
return rf(v) || f(v)
})
}
// AND combines the existing Range with another Range using logical AND.
func (rf Range) AND(f Range) Range {
return Range(func(v Version) bool {
return rf(v) && f(v)
})
}
/*ENUM(
Major
Minor
Patch
Prerelease
Prepatch
Preminor
Premajor
)*/
type VersionLevel byte
func (v *Version) IncrementBy(level VersionLevel, dest *Version) {
switch level {
case VersionLevelMajor:
{
dest.Major = v.Major + 1
dest.Minor = 0
dest.Patch = 0
if len(dest.Build) > 0 {
dest.Build = nil
}
}
case VersionLevelMinor:
{
dest.Major = v.Major
dest.Minor = v.Minor + 1
dest.Patch = 0
if len(dest.Build) > 0 {
dest.Build = nil
}
}
case VersionLevelPatch:
{
dest.Major = v.Major
dest.Minor = v.Minor
dest.Patch = v.Patch + 1
if len(dest.Build) > 0 {
dest.Build = nil
}
}
case VersionLevelPrerelease:
{
dest.Major = v.Major
dest.Minor = v.Minor
dest.Patch = v.Patch
}
}
}
/*ENUM(
None
Version
Range
)*/
type TokenizeResultValue byte
type TokenizeResult struct {
Version *Version
Range Range
Value TokenizeResultValue
}
func (t *TokenizeResult) TestString(input string) bool {
alt := Tokenize(input)
return t.Test(&alt)
}
func (t *TokenizeResult) Test(o *TokenizeResult) bool {
if o.Value == TokenizeResultValueVersion && t.Value == TokenizeResultValueVersion {
return o.Version.EQ(*t.Version)
} else if o.Value == TokenizeResultValueVersion && t.Value == TokenizeResultValueRange {
return t.Range(*o.Version)
} else if o.Value == TokenizeResultValueRange && t.Value == TokenizeResultValueVersion {
return o.Range(*t.Version)
} else {
return false
}
}
func (t *TokenizeResult) IsVersion() bool {
return t.Value == TokenizeResultValueVersion
}
func (t *TokenizeResult) IsRange() bool {
return t.Value == TokenizeResultValueRange
}
var scratchVersionPool = sync.Pool{
New: func() interface{} {
v := Version{}
return &v
},
}
func buildRangeFunc(comp comparator, major uint64, minor uint64, patch uint64) Range {
vv := Version{Major: major, Minor: minor, Patch: patch}
return Range(func(v Version) bool {
return comp(v, vv)
})
}
var preparsedTable = map[string]TokenizeResult{
"*": {Range: buildRangeFunc(compGE, 0, 0, 0), Value: TokenizeResultValueRange},
"X": {Range: buildRangeFunc(compGE, 0, 0, 0), Value: TokenizeResultValueRange},
"x": {Range: buildRangeFunc(compGE, 0, 0, 0), Value: TokenizeResultValueRange},
"0": {Range: buildRangeFunc(compGE, 0, 0, 0).AND(buildRangeFunc(compLT, 1, 0, 0)), Value: TokenizeResultValueRange},
"1": {Range: buildRangeFunc(compGE, 1, 0, 0).AND(buildRangeFunc(compLT, 2, 0, 0)), Value: TokenizeResultValueRange},
"2": {Range: buildRangeFunc(compGE, 2, 0, 0).AND(buildRangeFunc(compLT, 3, 0, 0)), Value: TokenizeResultValueRange},
"3": {Range: buildRangeFunc(compGE, 3, 0, 0).AND(buildRangeFunc(compLT, 4, 0, 0)), Value: TokenizeResultValueRange},
"4": {Range: buildRangeFunc(compGE, 4, 0, 0).AND(buildRangeFunc(compLT, 5, 0, 0)), Value: TokenizeResultValueRange},
"5": {Range: buildRangeFunc(compGE, 5, 0, 0).AND(buildRangeFunc(compLT, 6, 0, 0)), Value: TokenizeResultValueRange},
"6": {Range: buildRangeFunc(compGE, 6, 0, 0).AND(buildRangeFunc(compLT, 7, 0, 0)), Value: TokenizeResultValueRange},
"7": {Range: buildRangeFunc(compGE, 7, 0, 0).AND(buildRangeFunc(compLT, 8, 0, 0)), Value: TokenizeResultValueRange},
"8": {Range: buildRangeFunc(compGE, 8, 0, 0).AND(buildRangeFunc(compLT, 9, 0, 0)), Value: TokenizeResultValueRange},
"9": {Range: buildRangeFunc(compGE, 9, 0, 0).AND(buildRangeFunc(compLT, 10, 0, 0)), Value: TokenizeResultValueRange},
"10": {Range: buildRangeFunc(compGE, 10, 0, 0).AND(buildRangeFunc(compLT, 11, 0, 0)), Value: TokenizeResultValueRange},
"": {Range: buildRangeFunc(compGE, 0, 0, 0), Value: TokenizeResultValueRange},
}
func (result *TokenizeResult) AppendVersion(v *Version) {
switch result.Value {
case TokenizeResultValueNone:
{
result.Version = v
result.Value = TokenizeResultValueVersion
}
case TokenizeResultValueRange:
{
result.Range = result.Range.OR(v.ToRange(compEQ))
result.Value = TokenizeResultValueRange
}
case TokenizeResultValueVersion:
{
result.Range = result.Version.ToRange(compEQ).OR(v.ToRange(compEQ))
result.Value = TokenizeResultValueRange
}
}
}
func (result *TokenizeResult) AppendRange(r Range) {
switch result.Value {
case TokenizeResultValueNone:
{
result.Range = r
}
case TokenizeResultValueVersion:
{
result.Range = result.Version.ToRange(compEQ).AND(r)
result.Version = nil
}
case TokenizeResultValueRange:
{
result.Range = result.Range.AND(r)
}
}
result.Value = TokenizeResultValueRange
}
func (result *TokenizeResult) AppendORRange(r Range) {
switch result.Value {
case TokenizeResultValueNone:
{
result.Range = r
}
case TokenizeResultValueVersion:
{
result.Range = result.Version.ToRange(compEQ).OR(r)
result.Version = nil
}
case TokenizeResultValueRange:
{
result.Range = result.Range.OR(r)
}
}
result.Value = TokenizeResultValueRange
}
func (result *TokenizeResult) AppendWildcard(wildcard WildcardType, v *Version) {
if wildcard == NoneWildcard {
result.AppendVersion(v)
return
}
switch result.Value {
case TokenizeResultValueNone:
{
result.Range = v.ToWildcardRange(wildcard)
}
case TokenizeResultValueVersion:
{
result.Range = result.Version.ToRange(compEQ).AND(v.ToWildcardRange(wildcard))
result.Version = nil
}
case TokenizeResultValueRange:
{
result.Range = result.Range.AND(v.ToWildcardRange(wildcard))
}
}
result.Value = TokenizeResultValueRange
}
/*ENUM(
None
OR
GT
GE
LT
LE
Version
Tilda
Caret
)
*/
type sevmerTokenType byte
type semverToken struct {
Token sevmerTokenType
Wildcard WildcardType
}
func (t *semverToken) ToRange(v *Version) Range {
switch t.Token {
// Allows changes that do not modify the left-most non-zero element in the [major, minor, patch] tuple. In other words, this allows patch and minor updates for versions 1.0.0 and above, patch updates for versions 0.X >=0.1.0, and no updates for versions 0.0.X.
case SevmerTokenTypeCaret:
{
if v.Major == 0 {
return v.ToWildcardRange(PatchWildcard)
}
if t.Wildcard == MinorWildcard || (t.Wildcard == NoneWildcard && v.Minor == 0 && v.Patch == 0) {
r1 := v.ToRange(compGE)
v2 := &Version{}
v.CopyTo(v2)
v2.Minor = math.MaxUint64
v2.Patch = math.MaxUint64
return r1.AND(v2.ToRange(compLE))
}
if v.Minor == 0 && v.Patch == 0 {
return v.ToWildcardRange(PatchWildcard)
} else {
r1 := v.ToRange(compGE)
v2 := &Version{}
v.CopyTo(v2)
v2.Minor = math.MaxUint64
v2.Patch = math.MaxUint64
return r1.AND(v2.ToRange(compLE))
}
}
case SevmerTokenTypeTilda:
{
if v.Minor == 0 || t.Wildcard == MinorWildcard || t.Wildcard == MajorWildcard {
return v.ToWildcardRange(MinorWildcard)
} else {
return v.ToWildcardRange(PatchWildcard)
}
}
case SevmerTokenTypeGE:
{
return v.ToRange(compGE)
}
case SevmerTokenTypeLE:
{
return v.ToRange(compLE)
}
case SevmerTokenTypeGT:
{
switch t.Wildcard {
case PatchWildcard:
{
v.Patch = math.MaxUint64
}
case MinorWildcard:
{
v.Minor = math.MaxUint64
}
}
return v.ToRange(compGT)
}
case SevmerTokenTypeLT:
{
switch t.Wildcard {
case PatchWildcard:
{
v.Patch = 0
v.Minor = 0
}
case MinorWildcard:
{
v.Minor = 0
v.Patch = 0
}
}
return v.ToRange(compLT)
}
}
return v.ToWildcardRange(t.Wildcard)
}
func Tokenize(input string) TokenizeResult {
if preparsedRange, hasPreparsedRange := preparsedTable[input]; hasPreparsedRange {
return preparsedRange
}
i := 0
length := len(input)
lastNonwhitespace := -1
version := scratchVersionPool.Get().(*Version)
version.Reset()
result := TokenizeResult{
Version: nil,
Range: nil,
}
var token = semverToken{
Token: SevmerTokenTypeNone,
Wildcard: NoneWildcard,
}
isOR := false
count := 0
wipToken := SevmerTokenTypeNone
skipRound := false
for i < length {
skipRound = false
switch input[i] {
case '>':
{
if token.Token == SevmerTokenTypeVersion {
isOR = false
}
if input[i+1] == '=' {
wipToken = SevmerTokenTypeGE
i++
} else {
wipToken = SevmerTokenTypeGT
}
i++
for i < length && input[i] == ' ' {
i++
}
}
case '<':
{
if input[i+1] == '=' {
wipToken = SevmerTokenTypeGE
i++
} else {
wipToken = SevmerTokenTypeLT
}
i++
for i < length && input[i] == ' ' {
i++
}
}
case '=', 'v':
{
wipToken = SevmerTokenTypeVersion
isOR = true
i++
for i < length && input[i] == ' ' {
i++
}
}
case '~':
{
wipToken = SevmerTokenTypeTilda
i++
if input[i] == '>' {
i++
}
for i < length && input[i] == ' ' {
i++
}
}
case '^':
{
wipToken = SevmerTokenTypeCaret
i++
for i < length && input[i] == ' ' {
i++
}
}
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'X', 'x', '*':
{
wipToken = SevmerTokenTypeVersion
isOR = true
}
case '|':
{
i++
for i < length && input[i] == '|' {
i++
}
for i < length && input[i] == ' ' {
i++
}
isOR = true
wipToken = SevmerTokenTypeNone
skipRound = true
}
case '-':
{
i++
for i < length && input[i] == ' ' {
i++
}
}
case ' ':
{
i++
for i < length && input[i] == ' ' {
i++
}
skipRound = true
}
default:
{
i++
wipToken = SevmerTokenTypeNone
skipRound = true
}
}
if !skipRound {
lastNonwhitespace = i
if count == 0 && wipToken == SevmerTokenTypeVersion {
v := &Version{}
version.Reset()
token.Token = wipToken
token.Wildcard, _, i = parseVersion(input[i:], version)
version.CopyTo(v)
if token.Wildcard == NoneWildcard {
result.AppendVersion(v)
} else {
result.AppendRange(token.ToRange(v))
}
count++
token.Token = SevmerTokenTypeNone
token.Wildcard = NoneWildcard
version.Reset()
} else if count == 0 {
v := &Version{}
version.Reset()
token.Token = wipToken
token.Wildcard, _, i = parseVersion(input[i:], version)
version.CopyTo(v)
result.AppendRange(token.ToRange(v))
token.Token = wipToken
version.Reset()
count++
} else if isOR {
if token.Token != SevmerTokenTypeNone && count > 1 {
v := &Version{}
version.CopyTo(v)
or := token.ToRange(v)
version.Reset()
token.Token = wipToken
token.Wildcard, _, i = parseVersion(input[i:], version)
v2 := &Version{}
version.CopyTo(v2)
result.AppendORRange(or.OR(token.ToRange(v2)))
} else {
token.Token = wipToken
token.Wildcard, _, i = parseVersion(input[i:], version)
v2 := &Version{}
version.CopyTo(v2)
result.AppendORRange(token.ToRange(v2))
}
count++
} else {
count++
v := &Version{}
version.CopyTo(v)
or := token.ToRange(v)
version.Reset()
token.Token = wipToken
token.Wildcard, _, i = parseVersion(input[i:], version)
v2 := &Version{}
version.CopyTo(v2)
result.AppendRange(or.AND(token.ToRange(v2)))
}
i += lastNonwhitespace + 1
isOR = false
}
}
scratchVersionPool.Put(version)
return result
}
func parseVersion(vStr string, parts *Version) (_wildcard WildcardType, isValid bool, stoppedAt int) {
partI := 0
partStartI := -1
lastCharI := 0
isValid = true
isDone := false
count := len(vStr)
if count == 0 {
return _wildcard, false, 0
}
for i, char := range vStr {
if isDone {
break
}
stoppedAt = i
switch char {
case ' ':
{
if partI >= 2 {
isDone = true
break
}
}
case '|', '^', '#', '&', '%', '!':
{
isDone = true
stoppedAt--
break
}
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
{
if partStartI == -1 {
partStartI = i
}
lastCharI = i
}
case '.':
{
if partStartI > -1 && partI <= 2 {
switch partI {
case 0:
{
parts.Major = normalizeVersionPart(vStr[partStartI:i])
}
case 1:
{
parts.Minor = normalizeVersionPart(vStr[partStartI:i])
}
}
partStartI = -1
partI++
// "fo.o.b.ar"
} else if partI > 2 || partStartI == -1 {
isValid = false
isDone = true
break
}
}
case '-', '+':
{
if partI == 2 && partStartI > -1 {
parts.Patch = normalizeVersionPart(vStr[partStartI:i])
_wildcard = NoneWildcard
partStartI = i
partI = 3
isDone = true
break
} else {
isValid = false
isDone = true
break
}
}
case 'x', '*', 'X':
{
if partStartI == -1 {
partStartI = i
}
lastCharI = i
// We want min wildcard
if _wildcard == NoneWildcard {
switch partI {
case 0:
{
_wildcard = MajorWildcard
}
case 1:
{
_wildcard = MinorWildcard
}
case 2:
{
_wildcard = PatchWildcard
}
}
}
}
default:
{
lastCharI = 0
isValid = false
isDone = true
break
}
}
}
if isValid {
isValid = partI > -1
}
if partStartI == -1 {
partStartI = 0
}
if lastCharI == -1 || partStartI > lastCharI {
lastCharI = len(vStr) - 1
}
// Where did we leave off?
switch partI {
// That means they used a match like this:
// "1"
// So its a wildcard minor
case 0:
{
if _wildcard == NoneWildcard {
_wildcard = MinorWildcard
}
parts.Major = normalizeVersionPart(vStr[partStartI : lastCharI+1])
}
case 3:
{
stoppedAt = parseBuild(parts, vStr[partStartI:])
stoppedAt += partStartI
}
case 1:
{
if _wildcard == NoneWildcard {
_wildcard = PatchWildcard
}
parts.Minor = normalizeVersionPart(vStr[partStartI : lastCharI+1])
}
case 2:
{
parts.Patch = normalizeVersionPart(vStr[partStartI : lastCharI+1])
}
}
return _wildcard, isValid, stoppedAt
}
func parseBuild(parts *Version, input string) int {
if len(input) == 0 {
return 0
}
buildCount := 0
preCount := 0
stoppedAt := 0
for i, char := range input {
if char == ' ' {
stoppedAt = i
break
} else if char == '-' {
preCount++
} else if char == '+' {
buildCount++
}
}
if buildCount == 0 && preCount == 0 {
return stoppedAt
}
buildSegments := make([]string, 0, buildCount)
preSegments := make([]string, 0, preCount)
start := 0
trailingIsBuild := false
stoppedAt = 0
for i, char := range input {
stoppedAt = i
if char == ' ' {
break
}
if char == '-' || char == '+' {
if i > start && trailingIsBuild {
buildSegments = append(buildSegments, input[start:i+1])
} else if i > start && !trailingIsBuild {
preSegments = append(preSegments, input[start:i+1])
}
start = i + 1
trailingIsBuild = char == '+'
}
}
if start > 0 && trailingIsBuild {
buildSegments = append(buildSegments, input[start:])
} else if start > 0 && !trailingIsBuild {
preSegments = append(preSegments, input[start:])
}
parts.Build = buildSegments
parts.Pre = preSegments
return stoppedAt
}
func normalizeVersionPart(part string) uint64 {
var b strings.Builder
// First, we count.
var count int
needsNormalization := false
for _, char := range part {
switch char {
case 'x', '*':
{
needsNormalization = true
count++
}
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
{
count++
}
default:
{
needsNormalization = true
}
}
}
if count == 0 {
return 0
} else if !needsNormalization {
comp, _ := strconv.ParseUint(part, 10, 64)
return comp
}
b.Grow(count)
for _, char := range part {
switch char {
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
{
b.WriteRune(char)
}
case '*', 'x':
{
b.WriteRune('0')
}
}
}
comp, _ := strconv.ParseUint(b.String(), 10, 64)
return comp
}
func NewVersion(input string) (v *Version, e error) {
return v, e
}
// func IsRange(input string) bool {
// }
// Versions represents multiple versions.
type Versions []Version
// Len returns length of version collection
func (s Versions) Len() int {
return len(s)
}
// Swap swaps two versions inside the collection by its indices
func (s Versions) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
// Less checks if version at index i is less than version at index j
func (s Versions) Less(i, j int) bool {
return s[i].LT(s[j])
}
// Sort sorts a slice of versions
func Sort(versions []Version) {
sort.Sort(Versions(versions))
}
// Code generated by go-enum
// DO NOT EDIT!
package node_semver
import (
"fmt"
)
const (
// TokenizeResultValueNone is a TokenizeResultValue of type None.
TokenizeResultValueNone TokenizeResultValue = iota
// TokenizeResultValueVersion is a TokenizeResultValue of type Version.
TokenizeResultValueVersion
// TokenizeResultValueRange is a TokenizeResultValue of type Range.
TokenizeResultValueRange
)
const _TokenizeResultValueName = "NoneVersionRange"
var _TokenizeResultValueMap = map[TokenizeResultValue]string{
0: _TokenizeResultValueName[0:4],
1: _TokenizeResultValueName[4:11],
2: _TokenizeResultValueName[11:16],
}
// String implements the Stringer interface.
func (x TokenizeResultValue) String() string {
if str, ok := _TokenizeResultValueMap[x]; ok {
return str
}
return fmt.Sprintf("TokenizeResultValue(%d)", x)
}
var _TokenizeResultValueValue = map[string]TokenizeResultValue{
_TokenizeResultValueName[0:4]: 0,
_TokenizeResultValueName[4:11]: 1,
_TokenizeResultValueName[11:16]: 2,
}
// ParseTokenizeResultValue attempts to convert a string to a TokenizeResultValue
func ParseTokenizeResultValue(name string) (TokenizeResultValue, error) {
if x, ok := _TokenizeResultValueValue[name]; ok {
return x, nil
}
return TokenizeResultValue(0), fmt.Errorf("%s is not a valid TokenizeResultValue", name)
}
// MarshalText implements the text marshaller method
func (x TokenizeResultValue) MarshalText() ([]byte, error) {
return []byte(x.String()), nil
}
// UnmarshalText implements the text unmarshaller method
func (x *TokenizeResultValue) UnmarshalText(text []byte) error {
name := string(text)
tmp, err := ParseTokenizeResultValue(name)
if err != nil {
return err
}
*x = tmp
return nil
}
const (
// VersionLevelMajor is a VersionLevel of type Major.
VersionLevelMajor VersionLevel = iota
// VersionLevelMinor is a VersionLevel of type Minor.
VersionLevelMinor
// VersionLevelPatch is a VersionLevel of type Patch.
VersionLevelPatch
// VersionLevelPrerelease is a VersionLevel of type Prerelease.
VersionLevelPrerelease
// VersionLevelPrepatch is a VersionLevel of type Prepatch.
VersionLevelPrepatch
// VersionLevelPreminor is a VersionLevel of type Preminor.
VersionLevelPreminor
// VersionLevelPremajor is a VersionLevel of type Premajor.
VersionLevelPremajor
)
const _VersionLevelName = "MajorMinorPatchPrereleasePrepatchPreminorPremajor"
var _VersionLevelMap = map[VersionLevel]string{
0: _VersionLevelName[0:5],
1: _VersionLevelName[5:10],
2: _VersionLevelName[10:15],
3: _VersionLevelName[15:25],
4: _VersionLevelName[25:33],
5: _VersionLevelName[33:41],
6: _VersionLevelName[41:49],
}
// String implements the Stringer interface.
func (x VersionLevel) String() string {
if str, ok := _VersionLevelMap[x]; ok {
return str
}
return fmt.Sprintf("VersionLevel(%d)", x)
}
var _VersionLevelValue = map[string]VersionLevel{
_VersionLevelName[0:5]: 0,
_VersionLevelName[5:10]: 1,
_VersionLevelName[10:15]: 2,
_VersionLevelName[15:25]: 3,
_VersionLevelName[25:33]: 4,
_VersionLevelName[33:41]: 5,
_VersionLevelName[41:49]: 6,
}
// ParseVersionLevel attempts to convert a string to a VersionLevel
func ParseVersionLevel(name string) (VersionLevel, error) {
if x, ok := _VersionLevelValue[name]; ok {
return x, nil
}
return VersionLevel(0), fmt.Errorf("%s is not a valid VersionLevel", name)
}
// MarshalText implements the text marshaller method
func (x VersionLevel) MarshalText() ([]byte, error) {
return []byte(x.String()), nil
}
// UnmarshalText implements the text unmarshaller method
func (x *VersionLevel) UnmarshalText(text []byte) error {
name := string(text)
tmp, err := ParseVersionLevel(name)
if err != nil {
return err
}
*x = tmp
return nil
}
const (
// SevmerTokenTypeNone is a sevmerTokenType of type None.
SevmerTokenTypeNone sevmerTokenType = iota
// SevmerTokenTypeOR is a sevmerTokenType of type OR.
SevmerTokenTypeOR
// SevmerTokenTypeGT is a sevmerTokenType of type GT.
SevmerTokenTypeGT
// SevmerTokenTypeGE is a sevmerTokenType of type GE.
SevmerTokenTypeGE
// SevmerTokenTypeLT is a sevmerTokenType of type LT.
SevmerTokenTypeLT
// SevmerTokenTypeLE is a sevmerTokenType of type LE.
SevmerTokenTypeLE
// SevmerTokenTypeVersion is a sevmerTokenType of type Version.
SevmerTokenTypeVersion
// SevmerTokenTypeTilda is a sevmerTokenType of type Tilda.
SevmerTokenTypeTilda
// SevmerTokenTypeCaret is a sevmerTokenType of type Caret.
SevmerTokenTypeCaret
)
const _sevmerTokenTypeName = "NoneORGTGELTLEVersionTildaCaret"
var _sevmerTokenTypeMap = map[sevmerTokenType]string{
0: _sevmerTokenTypeName[0:4],
1: _sevmerTokenTypeName[4:6],
2: _sevmerTokenTypeName[6:8],
3: _sevmerTokenTypeName[8:10],
4: _sevmerTokenTypeName[10:12],
5: _sevmerTokenTypeName[12:14],
6: _sevmerTokenTypeName[14:21],
7: _sevmerTokenTypeName[21:26],
8: _sevmerTokenTypeName[26:31],
}
// String implements the Stringer interface.
func (x sevmerTokenType) String() string {
if str, ok := _sevmerTokenTypeMap[x]; ok {
return str
}
return fmt.Sprintf("sevmerTokenType(%d)", x)
}
var _sevmerTokenTypeValue = map[string]sevmerTokenType{
_sevmerTokenTypeName[0:4]: 0,
_sevmerTokenTypeName[4:6]: 1,
_sevmerTokenTypeName[6:8]: 2,
_sevmerTokenTypeName[8:10]: 3,
_sevmerTokenTypeName[10:12]: 4,
_sevmerTokenTypeName[12:14]: 5,
_sevmerTokenTypeName[14:21]: 6,
_sevmerTokenTypeName[21:26]: 7,
_sevmerTokenTypeName[26:31]: 8,
}
// ParsesevmerTokenType attempts to convert a string to a sevmerTokenType
func ParsesevmerTokenType(name string) (sevmerTokenType, error) {
if x, ok := _sevmerTokenTypeValue[name]; ok {
return x, nil
}
return sevmerTokenType(0), fmt.Errorf("%s is not a valid sevmerTokenType", name)
}
// MarshalText implements the text marshaller method
func (x sevmerTokenType) MarshalText() ([]byte, error) {
return []byte(x.String()), nil
}
// UnmarshalText implements the text unmarshaller method
func (x *sevmerTokenType) UnmarshalText(text []byte) error {
name := string(text)
tmp, err := ParsesevmerTokenType(name)
if err != nil {
return err
}
*x = tmp
return nil
}
package node_semver_test
import (
"fmt"
"math/rand"
"sort"
"testing"
goblin "github.com/franela/goblin"
"github.com/jarred-sumner/devserverless/resolver/node_semver"
"github.com/jarred-sumner/devserverless/resolver/node_semver/fixtures"
. "github.com/onsi/gomega"
"github.com/stretchr/testify/assert"
)
func assertNotRange(t *testing.T, r *node_semver.TokenizeResult, input string) {
assert.False(t, r.Value == node_semver.TokenizeResultValueRange, fmt.Sprintf("Expected %s to not be a Range", input))
}
func TestMajorOnlyVersion(t *testing.T) {
result := node_semver.Tokenize("1.0.0")
assert.NotNil(t, result.Version)
assert.Equal(t, result.Version.Major, uint64(1))
assert.Equal(t, result.Version.Minor, uint64(0))
assert.Equal(t, result.Version.Patch, uint64(0))
assertNotRange(t, &result, "1.0.0")
}
func TestGTVersion(t *testing.T) {
result := node_semver.Tokenize(">1.0.0")
v := node_semver.Version{
Major: 1,
Minor: 0,
Patch: 1,
}
assert.True(t, result.Range(v))
v.Major = 0
assert.False(t, result.Range(v))
v.Major = 1
v.Minor = 1
assert.True(t, result.Range(v))
v.Major = 1
v.Minor = 0
v.Patch = 2
assert.True(t, result.Range(v))
}
func TestGTLTVersion(t *testing.T) {
result := node_semver.Tokenize(">=1.0.0 <1.0.9")
v := node_semver.Version{
Major: 1,
Minor: 0,
Patch: 1,
}
assert.True(t, result.Range(v))
v.Major = 0
assert.False(t, result.Range(v))
v.Major = 1
v.Minor = 1
assert.False(t, result.Range(v))
v.Major = 1
v.Minor = 0
v.Patch = 2
assert.True(t, result.Range(v))
}
func TestMajorMinorVersion(t *testing.T) {
result := node_semver.Tokenize("1.2.0")
assert.NotNil(t, result.Version)
assert.Equal(t, result.Version.Major, uint64(1))
assert.Equal(t, result.Version.Minor, uint64(2))
assert.Equal(t, result.Version.Patch, uint64(0))
assertNotRange(t, &result, "1.2.0")
}
func TestMajorMinorPatchVersion(t *testing.T) {
result := node_semver.Tokenize("1.2.3")
assert.NotNil(t, result.Version)
assert.Equal(t, result.Version.Major, uint64(1))
assert.Equal(t, result.Version.Minor, uint64(2))
assert.Equal(t, result.Version.Patch, uint64(3))
assertNotRange(t, &result, "1.2.3")
}
func TestMultipleVersions(t *testing.T) {
result := node_semver.Tokenize("1.2.3 1.2.9")
v0 := node_semver.Version{
Major: 1,
Minor: 2,
Patch: 3,
}
v1 := node_semver.Version{
Major: 1,
Minor: 2,
Patch: 9,
}
assert.True(t, result.Range(v0))
assert.True(t, result.Range(v1))
v1.Major = 2
assert.False(t, result.Range(v1))
}
func TestMultipleVersionsWithOR(t *testing.T) {
result := node_semver.Tokenize("1.2.3 || 1.2.9")
v0 := node_semver.Version{
Major: 1,
Minor: 2,
Patch: 3,
}
v1 := node_semver.Version{
Major: 1,
Minor: 2,
Patch: 9,
}
assert.True(t, result.Range(v0))
assert.True(t, result.Range(v1))
}
func TestSimplePre(t *testing.T) {
result := node_semver.Tokenize("1.2.3-pre")
v0 := node_semver.Version{
Major: 1,
Minor: 2,
Patch: 3,
Pre: []string{"pre"},
}
assert.True(t, result.IsVersion())
assert.True(t, result.Version.EQ(v0))
}
func TestSimpleBuild(t *testing.T) {
result := node_semver.Tokenize("1.2.3+build")
v0 := node_semver.Version{
Major: 1,
Minor: 2,
Patch: 3,
Build: []string{"build"},
}
assert.True(t, result.IsVersion())
assert.True(t, result.Version.EQ(v0))
}
func TestSimplePreBuild(t *testing.T) {
result := node_semver.Tokenize("1.2.3+build-pre")
v0 := node_semver.Version{
Major: 1,
Minor: 2,
Patch: 3,
Build: []string{"build"},
Pre: []string{"pre"},
}
assert.True(t, result.IsVersion())
assert.True(t, result.Version.EQ(v0))
}
func TestMultiPreBuild(t *testing.T) {
result := node_semver.Tokenize("1.2.3+build-pre 1.2.8+build-pre")
v0 := node_semver.Version{
Major: 1,
Minor: 2,
Patch: 3,
Build: []string{"build"},
Pre: []string{"pre"},
}
assert.True(t, result.IsRange())
assert.True(t, result.Range(v0))
}
func TestWildcardPatch(t *testing.T) {
result := node_semver.Tokenize("1.2.*")
v0 := node_semver.Version{
Major: 1,
Minor: 2,
Patch: 8,
}
assert.True(t, result.IsRange())
assert.True(t, result.Range(v0))
v0.Minor++
assert.False(t, result.Range(v0))
}
func TestWildcardMinor(t *testing.T) {
result := node_semver.Tokenize("1.*")
v0 := node_semver.Version{
Major: 1,
Minor: 2,
Patch: 8,
}
assert.True(t, result.IsRange())
assert.True(t, result.Range(v0))
v0.Major++
assert.False(t, result.Range(v0))
v0.Major--
}
func TestCaretWildcardMinor(t *testing.T) {
result := node_semver.Tokenize("^1.*")
v0 := node_semver.Version{
Major: 1,
Minor: 2,
Patch: 8,
}
assert.True(t, result.IsRange())
assert.True(t, result.Range(v0))
v0.Major++
assert.False(t, result.Range(v0))
v0.Major--
}
func TestWildcardMajor(t *testing.T) {
result := node_semver.Tokenize("*")
v0 := node_semver.Version{
Major: 1,
Minor: 2,
Patch: 8,
}
assert.True(t, result.IsRange())
assert.True(t, result.Range(v0))
v0.Major = 0
assert.True(t, result.Range(v0))
v0.Minor = 0
assert.True(t, result.Range(v0))
v0.Patch = 0
assert.True(t, result.Range(v0))
}
func TestMultipleVersionsWithORLT(t *testing.T) {
result := node_semver.Tokenize("<1.2.3 || 1.2.9")
v0 := node_semver.Version{
Major: 1,
Minor: 2,
Patch: 0,
}
v1 := node_semver.Version{
Major: 1,
Minor: 2,
Patch: 9,
}
assert.True(t, result.Range(v0))
assert.True(t, result.Range(v1))
}
func TestMultipleVersionsWithORLTFlip(t *testing.T) {
result := node_semver.Tokenize("1.2.9 || <1.2.3")
v0 := node_semver.Version{
Major: 1,
Minor: 2,
Patch: 0,
}
v1 := node_semver.Version{
Major: 1,
Minor: 2,
Patch: 9,
}
assert.True(t, result.Range(v0))
assert.True(t, result.Range(v1))
v1.Patch = 8
assert.False(t, result.Range(v1))
}
func TestMultipleVersionsAnd(t *testing.T) {
assertRangeMatch(t, ">1.2.1 <1.2.3", "1.2.2")
assertRangeNotMatch(t, ">1.2.1 <1.2.3", "1.2.1")
assertRangeNotMatch(t, ">1.2.1 <1.2.3", "1.2.3")
assertRangeNotMatch(t, ">1.2.1 <1.2.3", "1.3.2")
assertRangeNotMatch(t, ">1.2.1 <1.2.3", "1.1.2")
assertRangeNotMatch(t, ">1.2.1 <1.2.3", "2.1.2")
}
func TestCaretVersion(t *testing.T) {
assertRangeMatch(t, "^1.2.1", "1.2.1")
assertRangeMatch(t, "^1.2.1", "1.3.0")
assertRangeMatch(t, "^1.2.1", "1.3.1")
assertRangeNotMatch(t, "^1.2.1", "1.2.0")
assertRangeNotMatch(t, "^ 1.2.1 ", "1.1.1")
assertRangeNotMatch(t, "^1.2.1", "2.0.0")
}
func TestCaretWithWhitespace(t *testing.T) {
assertRangeMatch(t, "^ 1 .X || 2.4", "1.2.1")
assertRangeMatch(t, "^ 1 .X || 2.4", "1.3.0")
assertRangeMatch(t, "^ 1 .X || 2.4", "1.3.1")
assertRangeMatch(t, "^ 1 .X || 2.4", "2.4.2")
assertRangeNotMatch(t, "^ 1.x || 2.4", "2.2.0")
assertRangeNotMatch(t, "^ 1.x || 2.4", "2.3.0")
assertRangeNotMatch(t, "^ 1.x || 2.4", "2.5.0")
assertRangeNotMatch(t, "^ 1.x || 2.4", "2.0.0")
}
func TestWhitespaceCheckDoesntPRoduceInvalidResults(t *testing.T) {
assertRangeMatch(t, "^ 1 || 2.4", "1.2.1")
assertRangeMatch(t, "^ 1 || 2.4", "1.3.0")
assertRangeMatch(t, "^ 1 || 2.4", "1.3.1")
assertRangeMatch(t, "^ 1 || 2.4", "2.4.2")
assertRangeNotMatch(t, "^ 1 || 2.4", "2.2.0")
assertRangeNotMatch(t, "^ 1 || 2.4", "2.3.0")
assertRangeNotMatch(t, "^ 1 || 2.4", "2.5.0")
assertRangeNotMatch(t, "^ 1 || 2.4", "2.0.0")
}
func TestTildaMajor(t *testing.T) {
assertRangeMatch(t, "^ 1 ", "1.0.0")
assertRangeMatch(t, "^ 1 ", "1.2.0")
assertRangeMatch(t, "^ 1 ", "1.1.0")
assertRangeNotMatch(t, "^ 1 ", "2.0.0")
assertRangeNotMatch(t, "^ 1 ", "0.0.0")
assertRangeNotMatch(t, "^ 1 ", "0.5.0")
}
func TestDoubleWildcardMinor(t *testing.T) {
result := node_semver.Tokenize("2 || 3")
v := node_semver.Version{
Major: 1,
Minor: 2,
Patch: 3,
}
assert.False(t, result.Range(v))
v.Major++
assert.True(t, result.Range(v))
v.Major++
assert.True(t, result.Range(v))
v.Major++
assert.False(t, result.Range(v))
}
func TestMultiWildcardMinor(t *testing.T) {
result := node_semver.Tokenize("2 || 3 || 4 || 5")
v := node_semver.Version{
Major: 1,
Minor: 2,
Patch: 3,
}
assert.False(t, result.Range(v))
v.Major++
assert.True(t, result.Range(v))
v.Major++
assert.True(t, result.Range(v))
v.Major++
assert.True(t, result.Range(v))
v.Major++
assert.True(t, result.Range(v))
v.Major++
assert.False(t, result.Range(v))
}
func assertRangeMatch(t *testing.T, input string, other string) {
defer func() {
recover()
}()
result := node_semver.Tokenize(input)
v := node_semver.Tokenize(other).Version
assert.Truef(t, result.Range(*v), "Expected %s to match %s", input, other)
}
func assertRangeNotMatch(t *testing.T, input string, other string) {
defer func() {
recover()
}()
result := node_semver.Tokenize(input)
assert.Equal(t, result.Value, node_semver.TokenizeResultValueRange)
v := node_semver.Tokenize(other).Version
assert.NotNil(t, v)
assert.Falsef(t, result.Range(*v), "Expected %s NOT to match %s", input, other)
}
func TestCaretBehavior(t *testing.T) {
// assertRangeNotMatch(t, "^1.20.0||^2.0.0", "1.19.0")
assertRangeMatch(t, "^1.20.0||^2.0.0", "2.42.2")
assertRangeMatch(t, "^1.20.0||^2.0.0", "1.20.0")
assertRangeMatch(t, "^1.20.0||^2.0.0", "1.25.2")
assertRangeMatch(t, "^1.20.0||^2.0.0", "2.0.0")
assertRangeMatch(t, "^1.20.0||^2.0.0", "2.10.4")
}
func TestMultiCaret(t *testing.T) {
assertRangeMatch(t, "^1.2.3 || ^2.4", "1.2.3")
assertRangeMatch(t, "^1.2.3 || ^2.4", "1.3.0")
assertRangeMatch(t, "^1.2.3 || ^2.4", "2.4.0")
assertRangeMatch(t, "^1.2.3 || ^2.4", "2.4.1")
assertRangeMatch(t, "^1.2.3 || ^2.4", "2.4.2")
assertRangeMatch(t, "^1.2.3 || ^2.4", "2.5.0")
assertRangeMatch(t, "^1.2.3 || ^2.4", "2.5.1")
assertRangeMatch(t, "^1.2.3 || ^2.4", "2.6.0")
assertRangeMatch(t, "^1.2.3 || ^2.4", "2.6.1")
assertRangeNotMatch(t, "^1.2.3 || ^2.4", "1.2.0")
assertRangeNotMatch(t, "^1.2.3 || ^2.4", "1.2.1")
assertRangeNotMatch(t, "^1.2.3 || ^2.4", "1.2.2")
assertRangeNotMatch(t, "^1.2.3 || ^2.4", "2.3.0")
assertRangeNotMatch(t, "^1.2.3 || ^2.4", "1.0.0")
}
func BenchmarkSimple(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
for n := 0; n < b.N; n++ {
node_semver.Tokenize("1.2.3")
}
}
func BenchmarkSimpleCheck(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
for n := 0; n < b.N; n++ {
tok := node_semver.Tokenize("1.2.3")
tok.TestString("1.2.3")
}
}
func BenchmarkAdvancedCheck(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
for n := 0; n < b.N; n++ {
tok := node_semver.Tokenize("^1.2.3")
tok.TestString("1.2.8")
}
}
func BenchmarkCaret(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
for n := 0; n < b.N; n++ {
node_semver.Tokenize("^1.2.3")
}
}
func BenchmarkCaretComplex(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
for n := 0; n < b.N; n++ {
node_semver.Tokenize("^1.2.3 || ^2.3")
}
}
func TestEqualVersions(t *testing.T) {
for _, v := range fixtures.EqualVersions() {
result := node_semver.Tokenize(v[0])
plain := node_semver.Tokenize(v[1]).Version
assert.NotNil(t, &result.Version, v[0])
assert.True(t, result.Version.EQ(*plain))
}
}
func TestLTRange(t *testing.T) {
assertRangeMatch(t, "<2.0.0", "0.2.9")
}
func shuffleInPlace(isArray []string) {
l := len(isArray) - 1
for i := 0; i <= l; i++ {
n := rand.Intn(l)
// swap
x := isArray[i]
isArray[i] = isArray[n]
isArray[n] = x
}
}
func TestRanges(t *testing.T) {
g := goblin.Goblin(t)
RegisterFailHandler(func(m string, _ ...int) { g.Fail(m) })
g.Describe("TestStringify", func() {
for _, v := range fixtures.Strings() {
g.It(fmt.Sprintf("%s should stringify to %s", v, v), func() {
result := node_semver.Tokenize(v)
Expect(result.Version.String()).To(Equal(v))
})
}
})
g.Describe("Manual Ranges", func() {
for _, v := range fixtures.ManualRanges() {
g.It(fmt.Sprintf("%s should match %s", v[0], v[1]), func() {
result := node_semver.Tokenize(v[0])
Expect(result.TestString(v[1])).To(Equal(true))
})
}
})
g.Describe("Compare", func() {
g.It("Major > Minor", func() {
major := node_semver.Version{Major: 1, Minor: 0, Patch: 0}
minor := node_semver.Version{Major: 0, Minor: 1}
Expect(major.Compare(minor)).To(Equal(1))
})
g.It("Minor > Patch", func() {
major := node_semver.Version{Major: 0, Minor: 1, Patch: 0}
minor := node_semver.Version{Patch: 1}
Expect(major.Compare(minor)).To(Equal(1))
})
g.It("Pre > Not Pre", func() {
major := node_semver.Version{Major: 0, Minor: 1, Patch: 0}
minor := node_semver.Version{Patch: 1}
Expect(major.Compare(minor)).To(Equal(1))
})
g.It("Sorting Test", func() {
correct := fixtures.StringsToSort()
shuffled := fixtures.StringsToSort()
shuffleInPlace(shuffled)
sortable := make(node_semver.Versions, len(shuffled))
for i, v := range shuffled {
sortable[i] = *node_semver.Tokenize(v).Version
}
sort.Sort(sortable)
stringified := make([]string, len(sortable))
for i, v := range sortable {
stringified[i] = v.String()
}
Expect(stringified).To(Equal(correct))
})
})
g.Describe("TestRangeInclusive", func() {
for _, v := range fixtures.RangesInclusive() {
g.It(fmt.Sprintf("%s should match %s", v[0], v[1]), func() {
result := node_semver.Tokenize(v[0])
Expect(result.TestString(v[1])).To(BeTrue())
})
}
})
g.Describe("TestRangeExclusive", func() {
for _, v := range fixtures.RangeExclude() {
g.It(fmt.Sprintf("%s should not match %s", v[0], v[1]), func() {
result := node_semver.Tokenize(v[0])
Expect(result.TestString(v[1])).To(Equal(false))
})
}
})
// [range, version, options]
// g.Describe("TestVersionGreaterThanRange", func() {
// for _, v := range fixtures.VersionGTRange() {
// version := v[1]
// g.It(fmt.Sprintf("%s should be greater than %s", version, v[0]), func() {
// result := node_semver.Tokenize(version)
// Expect(result.TestString(v[0])).To(Equal(false))
// })
// }
// })
// g.Describe("TestVersionNOTGreaterThanRange", func() {
// for _, v := range fixtures.VersionsNotGTRange() {
// version := v[1]
// g.It(fmt.Sprintf("%s should NOT be greater than %s", version, v[0]), func() {
// result := node_semver.Tokenize(version)
// Expect(result.TestString(v[0])).To(Equal(false))
// })
// }
// })
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment