Last active
March 22, 2019 18:40
-
-
Save huntc/35f6cec0a47ce7ef62c0 to your computer and use it in GitHub Desktop.
Semantic version case class
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import scala.util.Try | |
type PreRelease = Either[String, Int] | |
object SemVer { | |
val pattern = """^(\d+)\.(\d+)\.(\d+)(-(([1-9a-zA-Z][0-9a-zA-Z-]*)(\.([1-9a-zA-Z][0-9a-zA-Z-]*))))?$""".r | |
def apply(s: String): SemVer = | |
s match { | |
case pattern(major, minor, patch, _, _, preRelease1, _, preRelease2) => | |
val majorCapture = major.toInt | |
val minorCapture = minor.toInt | |
val patchCapture = patch.toInt | |
preRelease1 match { | |
case preRelease1Capture: Any => | |
preRelease2 match { | |
case preRelease2Capture: Any => | |
SemVer( | |
majorCapture, | |
minorCapture, | |
patchCapture, | |
Some(toPreRelease(preRelease1Capture) -> Some(toPreRelease(preRelease2Capture)))) | |
case _ => | |
SemVer( | |
majorCapture, | |
minorCapture, | |
patchCapture, | |
Some(toPreRelease(preRelease1Capture) -> None)) | |
} | |
case _ => | |
SemVer( | |
majorCapture, | |
minorCapture, | |
patchCapture, | |
None) | |
} | |
} | |
private def toPreRelease(s: String): PreRelease = | |
Try(Right(s.toInt)).getOrElse(Left(s)) | |
private def comparePreReleases(preRelease: PreRelease, thatPreRelease: PreRelease): Int = | |
(preRelease, thatPreRelease) match { | |
case (Left(value), Left(thatValue)) => | |
value compare thatValue | |
case (Left(_), Right(_)) => | |
-1 | |
case (Right(value), Right(thatValue)) => | |
value compare thatValue | |
case (Right(_), Left(_)) => | |
1 | |
} | |
} | |
case class SemVer(major: Int, minor: Int, patch: Int, preRelease: Option[(PreRelease, Option[PreRelease])]) extends Ordered[SemVer] { | |
import SemVer._ | |
def compare(that: SemVer): Int = { | |
import scala.math.Ordered.orderingToOrdered | |
val result = (major, minor, patch) compare (that.major, that.minor, that.patch) | |
if (result == 0) | |
(preRelease, that.preRelease) match { | |
case (Some(p), Some(thatP)) => | |
(p, thatP) match { | |
case ((preRelease1, Some(preRelease2)), (thatPreRelease1, Some(thatPreRelease2))) => | |
val preRelease1Result = comparePreReleases(preRelease1, thatPreRelease1) | |
if (preRelease1Result == 0) comparePreReleases(preRelease2, thatPreRelease2) else preRelease1Result | |
case ((preRelease1, None), (thatPreRelease1, Some(thatPreRelease2))) => | |
val preRelease1Result = comparePreReleases(preRelease1, thatPreRelease1) | |
if (preRelease1Result == 0) 1 else preRelease1Result | |
case ((preRelease1, Some(preRelease2)), (thatPreRelease1, None)) => | |
val preRelease1Result = comparePreReleases(preRelease1, thatPreRelease1) | |
if (preRelease1Result == 0) -1 else preRelease1Result | |
case ((preRelease1, None), (thatPreRelease1, None)) => | |
comparePreReleases(preRelease1, thatPreRelease1) | |
} | |
case (Some(_), None) => | |
-1 | |
case (None, Some(_)) => | |
1 | |
case (None, None) => | |
0 | |
} | |
else | |
result | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Examples | |
assert((SemVer("1.1.1") :: SemVer("1.2.0") :: Nil).sorted == List(SemVer(1,1,1,None), SemVer(1,2,0,None))) | |
assert((SemVer("3.1.1") :: SemVer("1.2.0") :: Nil).sorted == List(SemVer(1,2,0,None), SemVer(3,1,1,None))) | |
assert((SemVer("1.1.1") :: SemVer("1.1.1") :: Nil).sorted == List(SemVer(1,1,1,None), SemVer(1,1,1,None))) | |
assert((SemVer("1.1.1") :: SemVer("1.1.1-beta.1") :: Nil).sorted == List(SemVer(1,1,1,Some((Left("beta"),Some(Right(1))))), SemVer(1,1,1,None))) | |
assert((SemVer("1.1.1-beta.2") :: SemVer("1.1.1-beta.1") :: Nil).sorted == List(SemVer(1,1,1,Some((Left("beta"),Some(Right(1))))), SemVer(1,1,1,Some((Left("beta"),Some(Right(2))))))) | |
assert((SemVer("1.1.1-beta.2") :: SemVer("1.1.1-alpha.1") :: Nil).sorted == List(SemVer(1,1,1,Some((Left("alpha"),Some(Right(1))))), SemVer(1,1,1,Some((Left("beta"),Some(Right(2))))))) |
Let’s make it Apache2
Thanks - I really appreciate it. Makes my life so much easier knowing I can use this. I'm not a Scala expert and this is so much cleaner and uses the idioms of Scala that are still not natural to me yet. Thanks again.
Here is an update to the RegEx to be a bit more compliant with SemanticVersion 2.0 spec. https://semver.org/
^(\d+)\.(\d+)\.(\d+)([-+]*(([1-9a-zA-Z][0-9a-zA-Z-]*)(\.([0-9a-zA-Z][0-9a-zA-Z-+]*))*))?$
Examples of what it parses correctly:
// 1.2.3-Beta-123.0123, 1.2.3-Alpha-4.3, 123.22.1, 123.22.1+Gamma-044.22, 123.22.1+Theta+044.22, 1.9.0++Beta.1.22+ABC, 1.2.3-Beta-0123.145+ABC
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
What license does this code have?