1212 verEqLater, # >= V -- Equal or later
1313 verEqEarlier, # <= V -- Equal or earlier
1414 verIntersect, # > V & < V
15+ verTilde, # ~= V
16+ verCaret, # ^= V
1517 verEq, # V
1618 verAny, # *
1719 verSpecial # #head
2325 ver* : Version
2426 of verSpecial:
2527 spe* : Version
26- of verIntersect:
28+ of verIntersect, verTilde, verCaret :
2729 verILeft, verIRight: VersionRange
2830 of verAny:
2931 nil
@@ -108,7 +110,7 @@ proc `==`*(range1: VersionRange, range2: VersionRange): bool =
108110 range1.ver == range2.ver
109111 of verSpecial:
110112 range1.spe == range2.spe
111- of verIntersect:
113+ of verIntersect, verTilde, verCaret :
112114 range1.verILeft == range2.verILeft and range1.verIRight == range2.verIRight
113115 of verAny: true
114116
@@ -126,14 +128,45 @@ proc withinRange*(ver: Version, ran: VersionRange): bool =
126128 return ver == ran.ver
127129 of verSpecial:
128130 return ver == ran.spe
129- of verIntersect:
131+ of verIntersect, verTilde, verCaret :
130132 return withinRange (ver, ran.verILeft) and withinRange (ver, ran.verIRight)
131133 of verAny:
132134 return true
133135
134136proc contains * (ran: VersionRange , ver: Version ): bool =
135137 return withinRange (ver, ran)
136138
139+ proc getNextIncompatibleVersion (version: string , semver: bool ): string =
140+ # # try to get next higher version to exclude according to semver semantic
141+ var numbers = version.split ('.' )
142+ let originalNumberLen = numbers.len
143+ while numbers.len < 3 :
144+ numbers.add (" 0" )
145+ var zeros = 0
146+ for n in 0 ..< 2 :
147+ if numbers[n] == " 0" :
148+ inc (zeros)
149+ else : break
150+ var increasePosition = 0
151+ if (semver):
152+ if originalNumberLen > 1 :
153+ case zeros
154+ of 0 :
155+ increasePosition = 0
156+ of 1 :
157+ increasePosition = 1
158+ else :
159+ increasePosition = 2
160+ else :
161+ increasePosition = max (0 , originalNumberLen - 2 )
162+
163+ numbers[increasePosition] = $ (numbers[increasePosition].parseInt () + 1 )
164+ var zeroPosition = increasePosition + 1
165+ while zeroPosition < numbers.len:
166+ numbers[zeroPosition] = " 0"
167+ inc (zeroPosition)
168+ result = numbers.join (" ." )
169+
137170proc makeRange * (version: string , op: string ): VersionRange =
138171 if version == " " :
139172 raise newException (ParseVersionError ,
@@ -149,6 +182,13 @@ proc makeRange*(version: string, op: string): VersionRange =
149182 result = VersionRange (kind: verEqEarlier)
150183 of " " , " ==" :
151184 result = VersionRange (kind: verEq)
185+ of " ^=" , " ~=" :
186+ result = VersionRange (kind: if op == " ^=" : verCaret else : verTilde)
187+ result .verILeft = makeRange (version, " >=" )
188+ var excludedVersion = getNextIncompatibleVersion (version,
189+ semver = (op == " ^=" ))
190+ result .verIRight = makeRange (excludedVersion, " <" )
191+ return
152192 else :
153193 raise newException (ParseVersionError , " Invalid operator: " & op)
154194 result .ver = Version (version)
@@ -169,7 +209,7 @@ proc parseVersionRange*(s: string): VersionRange =
169209 var version = " "
170210 while i < s.len:
171211 case s[i]
172- of '>' , '<' , '=' :
212+ of '>' , '<' , '=' , '~' , '^' :
173213 op.add (s[i])
174214 of '&' :
175215 result = VersionRange (kind: verIntersect)
@@ -186,7 +226,6 @@ proc parseVersionRange*(s: string): VersionRange =
186226 " Having more than one `&` in a version range is pointless" )
187227
188228 return
189-
190229 of '0' .. '9' , '.' :
191230 version.add (s[i])
192231
@@ -246,6 +285,10 @@ proc `$`*(verRange: VersionRange): string =
246285 return $ verRange.spe
247286 of verIntersect:
248287 return $ verRange.verILeft & " & " & $ verRange.verIRight
288+ of verTilde:
289+ return " ~= " & $ verRange.verILeft
290+ of verCaret:
291+ return " ^= " & $ verRange.verILeft
249292 of verAny:
250293 return " any version"
251294
@@ -259,7 +302,7 @@ proc getSimpleString*(verRange: VersionRange): string =
259302 result = $ verRange.spe
260303 of verLater, verEarlier, verEqLater, verEqEarlier, verEq:
261304 result = $ verRange.ver
262- of verIntersect:
305+ of verIntersect, verTilde, verCaret :
263306 result = getSimpleString (verRange.verILeft) & " _" &
264307 getSimpleString (verRange.verIRight)
265308 of verAny:
@@ -316,12 +359,37 @@ when isMainModule:
316359 doAssert (newVersion (" " ) < newVersion (" 0.1.0" ))
317360
318361 var versions = toOrderedTable [Version , string ]({
362+ newVersion (" 0.0.1" ): " v0.0.1" ,
363+ newVersion (" 0.0.2" ): " v0.0.2" ,
319364 newVersion (" 0.1.1" ): " v0.1.1" ,
365+ newVersion (" 0.2.2" ): " v0.2.2" ,
320366 newVersion (" 0.2.3" ): " v0.2.3" ,
321- newVersion (" 0.5" ): " v0.5"
367+ newVersion (" 0.5" ): " v0.5" ,
368+ newVersion (" 1.2" ): " v1.2" ,
369+ newVersion (" 2.2.2" ): " v2.2.2" ,
370+ newVersion (" 2.2.3" ): " v2.2.3" ,
371+ newVersion (" 2.3.2" ): " v2.3.2" ,
372+ newVersion (" 3.2" ): " v3.2" ,
373+ newVersion (" 3.3.2" ): " v3.3.2"
322374 })
323375 doAssert findLatest (parseVersionRange (" >= 0.1 & <= 0.4" ), versions) ==
324376 (newVersion (" 0.2.3" ), " v0.2.3" )
377+ doAssert findLatest (parseVersionRange (" ^= 0.1" ), versions) ==
378+ (newVersion (" 0.1.1" ), " v0.1.1" )
379+ doAssert findLatest (parseVersionRange (" ^= 0" ), versions) ==
380+ (newVersion (" 0.5" ), " v0.5" )
381+ doAssert findLatest (parseVersionRange (" ~= 2" ), versions) ==
382+ (newVersion (" 2.3.2" ), " v2.3.2" )
383+ doAssert findLatest (parseVersionRange (" ^= 0.0.1" ), versions) ==
384+ (newVersion (" 0.0.1" ), " v0.0.1" )
385+ doAssert findLatest (parseVersionRange (" ^= 2.2.2" ), versions) ==
386+ (newVersion (" 2.3.2" ), " v2.3.2" )
387+ doAssert findLatest (parseVersionRange (" ^= 2.1.1.1" ), versions) ==
388+ (newVersion (" 2.3.2" ), " v2.3.2" )
389+ doAssert findLatest (parseVersionRange (" ~= 2.2" ), versions) ==
390+ (newVersion (" 2.3.2" ), " v2.3.2" )
391+ doAssert findLatest (parseVersionRange (" ~= 0.2.2" ), versions) ==
392+ (newVersion (" 0.2.3" ), " v0.2.3" )
325393
326394 # TODO : Allow these in later versions?
327395 # doAssert newVersion("0.1-rc1") < newVersion("0.2")
0 commit comments