Skip to main content

SemVer (Semantic Version)

NOTE

For now, please do not use any types and methods from the package other than just.semver.

  • just.semver: Fine
  • just.semver.matcher or any other just.semver.xxx packages: You can use it but not recommended as it's currently experimental.

It requires the just-semver-core module.

"io.kevinlee" %% "just-semver-core" % "1.0.0"
"io.kevinlee" %%% "just-semver-core" % "1.0.0"

SemVer.parse

import just.semver.SemVer

val v = SemVer.parse("1.0.0")
// v: Either[just.semver.ParseError, SemVer] = Right(
// value = SemVer(
// major = Major(value = 1),
// minor = Minor(value = 0),
// patch = Patch(value = 0),
// pre = None,
// buildMetadata = None
// )
// )

// To render it to `String`,
v.map(_.render)
// res1: Either[just.semver.ParseError, String] = Right(value = "1.0.0")

// Invalid version
SemVer.parse("a1.0.0")
// res2: Either[just.semver.ParseError, SemVer] = Left(
// value = InvalidVersionStringError(value = "a1.0.0")
// )

SemVer.unsafeParse

import just.semver.SemVer

// parse unsafe - NOT RECOMMENDED!!!
val v = SemVer.unsafeParse("1.0.0")
// v: SemVer = SemVer(
// major = Major(value = 1),
// minor = Minor(value = 0),
// patch = Patch(value = 0),
// pre = None,
// buildMetadata = None
// )

// to String
v.render
// res4: String = "1.0.0"

// Invalid version
SemVer.unsafeParse("a1.0.0")
// java.lang.RuntimeException: Invalid SemVer String. value: a1.0.0
// at scala.sys.package$.error(package.scala:27)
// at just.semver.SemVer$.unsafeParse(SemVer.scala:127)
// at repl.MdocSession$MdocApp3$$anonfun$2.apply(semver.md:42)
// at repl.MdocSession$MdocApp3$$anonfun$2.apply(semver.md:42)

SemVer with pre-release info

import just.semver.SemVer

SemVer.parse("1.0.0-beta1")
// res6: Either[just.semver.ParseError, SemVer] = Right(
// value = SemVer(
// major = Major(value = 1),
// minor = Minor(value = 0),
// patch = Patch(value = 0),
// pre = Some(
// value = PreRelease(
// identifier = List(
// Dsv(values = List(Alphabet(value = "beta"), Num(value = "1")))
// )
// )
// ),
// buildMetadata = None
// )
// )

val v = SemVer.parse("1.0.0-3.123.9a")
// v: Either[just.semver.ParseError, SemVer] = Right(
// value = SemVer(
// major = Major(value = 1),
// minor = Minor(value = 0),
// patch = Patch(value = 0),
// pre = Some(
// value = PreRelease(
// identifier = List(
// Dsv(values = List(Num(value = "3"))),
// Dsv(values = List(Num(value = "123"))),
// Dsv(values = List(Num(value = "9"), Alphabet(value = "a")))
// )
// )
// ),
// buildMetadata = None
// )
// )

v.map(_.render)
// res7: Either[just.semver.ParseError, String] = Right(
// value = "1.0.0-3.123.9a"
// )

SemVer with build meta-info

import just.semver.SemVer

val v = SemVer.parse("1.0.0+100.0.12abc")
// v: Either[just.semver.ParseError, SemVer] = Right(
// value = SemVer(
// major = Major(value = 1),
// minor = Minor(value = 0),
// patch = Patch(value = 0),
// pre = None,
// buildMetadata = Some(
// value = BuildMetaInfo(
// identifier = List(
// Dsv(values = List(Num(value = "100"))),
// Dsv(values = List(Num(value = "0"))),
// Dsv(values = List(Num(value = "12"), Alphabet(value = "abc")))
// )
// )
// )
// )
// )

v.map(_.render)
// res9: Either[just.semver.ParseError, String] = Right(
// value = "1.0.0+100.0.12abc"
// )

SemVer with pre-release info and build meta-info

import just.semver.SemVer

SemVer.parse("1.0.0-beta1")
// res11: Either[just.semver.ParseError, SemVer] = Right(
// value = SemVer(
// major = Major(value = 1),
// minor = Minor(value = 0),
// patch = Patch(value = 0),
// pre = Some(
// value = PreRelease(
// identifier = List(
// Dsv(values = List(Alphabet(value = "beta"), Num(value = "1")))
// )
// )
// ),
// buildMetadata = None
// )
// )

val v = SemVer.parse("1.0.0-3.123.9a+100.0.12abc")
// v: Either[just.semver.ParseError, SemVer] = Right(
// value = SemVer(
// major = Major(value = 1),
// minor = Minor(value = 0),
// patch = Patch(value = 0),
// pre = Some(
// value = PreRelease(
// identifier = List(
// Dsv(values = List(Num(value = "3"))),
// Dsv(values = List(Num(value = "123"))),
// Dsv(values = List(Num(value = "9"), Alphabet(value = "a")))
// )
// )
// ),
// buildMetadata = Some(
// value = BuildMetaInfo(
// identifier = List(
// Dsv(values = List(Num(value = "100"))),
// Dsv(values = List(Num(value = "0"))),
// Dsv(values = List(Num(value = "12"), Alphabet(value = "abc")))
// )
// )
// )
// )
// )

v.map(_.render)
// res12: Either[just.semver.ParseError, String] = Right(
// value = "1.0.0-3.123.9a+100.0.12abc"
// )

Compare SemVer

import just.semver.SemVer

for {
a <- SemVer.parse("1.0.0")
b <- SemVer.parse("1.0.1")
} yield a < b
// res14: Either[just.semver.ParseError, Boolean] = Right(value = true)

for {
a <- SemVer.parse("1.0.1")
b <- SemVer.parse("1.0.0")
} yield a < b
// res15: Either[just.semver.ParseError, Boolean] = Right(value = false)

for {
a <- SemVer.parse("1.0.0")
b <- SemVer.parse("1.0.1")
} yield a <= b
// res16: Either[just.semver.ParseError, Boolean] = Right(value = true)

for {
a <- SemVer.parse("1.0.0")
b <- SemVer.parse("1.0.0")
} yield a <= b
// res17: Either[just.semver.ParseError, Boolean] = Right(value = true)

for {
a <- SemVer.parse("1.0.0")
b <- SemVer.parse("1.0.0")
} yield a == b
// res18: Either[just.semver.ParseError, Boolean] = Right(value = true)

for {
a <- SemVer.parse("1.0.1")
b <- SemVer.parse("1.0.0")
} yield a > b
// res19: Either[just.semver.ParseError, Boolean] = Right(value = true)

for {
a <- SemVer.parse("1.0.0")
b <- SemVer.parse("1.0.1")
} yield a > b
// res20: Either[just.semver.ParseError, Boolean] = Right(value = false)

for {
a <- SemVer.parse("1.0.0")
b <- SemVer.parse("1.0.1")
} yield a >= b
// res21: Either[just.semver.ParseError, Boolean] = Right(value = false)

for {
a <- SemVer.parse("1.0.0")
b <- SemVer.parse("1.0.0")
} yield a >= b
// res22: Either[just.semver.ParseError, Boolean] = Right(value = true)

for {
a <- SemVer.parse("1.0.1")
b <- SemVer.parse("1.0.0")
} yield a >= b
// res23: Either[just.semver.ParseError, Boolean] = Right(value = true)

Matchers

SemVer.unsafeParse("1.0.0").unsafeMatches("1.0.0 - 2.0.0")
// res24: Boolean = true
SemVer.unsafeParse("1.5.0").unsafeMatches("1.0.0 - 2.0.0")
// res25: Boolean = true
SemVer.unsafeParse("2.0.0").unsafeMatches("1.0.0 - 2.0.0")
// res26: Boolean = true
SemVer.unsafeParse("0.9.9").unsafeMatches("1.0.0 - 2.0.0")
// res27: Boolean = false
SemVer.unsafeParse("2.0.1").unsafeMatches("1.0.0 - 2.0.0")
// res28: Boolean = false

SemVer.unsafeParse("1.0.0").unsafeMatches(">1.0.0 <2.0.0")
// res29: Boolean = false
SemVer.unsafeParse("1.0.0").unsafeMatches(">=1.0.0 <=2.0.0")
// res30: Boolean = true
SemVer.unsafeParse("1.5.0").unsafeMatches(">1.0.0 <2.0.0")
// res31: Boolean = true
SemVer.unsafeParse("2.0.0").unsafeMatches(">1.0.0 <2.0.0")
// res32: Boolean = false
SemVer.unsafeParse("2.0.0").unsafeMatches(">=1.0.0 <=2.0.0")
// res33: Boolean = true
SemVer.unsafeParse("0.9.9").unsafeMatches(">=1.0.0 <=2.0.0")
// res34: Boolean = false
SemVer.unsafeParse("2.0.1").unsafeMatches(">=1.0.0 <=2.0.0")
// res35: Boolean = false

SemVer.unsafeParse("1.0.0").unsafeMatches("1.0.0 - 2.0.0 || >3.0.0 <4.0.0")
// res36: Boolean = true
SemVer.unsafeParse("2.0.0").unsafeMatches("1.0.0 - 2.0.0 || >3.0.0 <4.0.0")
// res37: Boolean = true
SemVer.unsafeParse("3.0.0").unsafeMatches("1.0.0 - 2.0.0 || >3.0.0 <=4.0.0")
// res38: Boolean = false
SemVer.unsafeParse("3.0.1").unsafeMatches("1.0.0 - 2.0.0 || >3.0.0 <=4.0.0")
// res39: Boolean = true
SemVer.unsafeParse("4.0.0").unsafeMatches("1.0.0 - 2.0.0 || >3.0.0 <=4.0.0")
// res40: Boolean = true