Skip to content

Commit

Permalink
Close #223 - [just-semver-decver] Add DecVerExt which is DecVer
Browse files Browse the repository at this point in the history
… with pre-release and build metadata
  • Loading branch information
kevin-lee committed Jul 26, 2024
1 parent 0b08cb7 commit be80145
Show file tree
Hide file tree
Showing 14 changed files with 1,586 additions and 73 deletions.
54 changes: 34 additions & 20 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ import sbtcrossproject.CrossProject

lazy val core = module("core", crossProject(JVMPlatform, JSPlatform, NativePlatform))
.settings(
libraryDependencies ++= List(libs.tests.scalaCollectionCompat),
// (Compile / compile) / scalacOptions ++= (if (isGhaPublishing) List.empty[String]
// else ProjectInfo.commonWarts(scalaVersion.value)),
// (Test / compile) / scalacOptions ++= (if (isGhaPublishing) List.empty[String]
Expand Down Expand Up @@ -94,7 +95,10 @@ lazy val coreJs = core.js.settings(Test / fork := false)
lazy val coreNative = core.native.settings(nativeSettings)

lazy val decver = module("decver", crossProject(JVMPlatform, JSPlatform, NativePlatform))
.dependsOn(core)
.settings(
libraryDependencies ++= List(libs.tests.scalaCollectionCompat),
)
.dependsOn(core % props.IncludeTest)

lazy val decverJvm = decver.jvm
lazy val decverJs = decver.js.settings(Test / fork := false)
Expand Down Expand Up @@ -168,10 +172,11 @@ lazy val props =
val isWartRemover: ModuleID => Boolean =
m => m.name == "wartremover"

// final val ProjectScalaVersion: String = "3.1.3"
// final val ProjectScalaVersion: String = "3.3.1"
final val ProjectScalaVersion: String = "2.13.12"
final val CrossScalaVersions: List[String] =
// val ProjectScalaVersion: String = "2.12.18"
// val ProjectScalaVersion: String = "3.1.3"
// val ProjectScalaVersion: String = "3.3.1"
val ProjectScalaVersion: String = "2.13.12"
val CrossScalaVersions: List[String] =
(
if (isGhaPublishing) {
// Publish version and the project version are the same so this logic is no longer required.
Expand All @@ -189,30 +194,40 @@ lazy val props =
).distinct
)

val IncludeTest = "compile->compile;test->test"

val SonatypeCredentialHost = "s01.oss.sonatype.org"
val SonatypeRepository = s"https://$SonatypeCredentialHost/service/local"

final val HedgehogVersion = "0.9.0"

final val HedgehogLatestVersion = "0.10.1"

val ScalaCollectionCompatVersion = "2.12.0"

}

lazy val libs =
new {

def hedgehogLibs(scalaVersion: String): List[ModuleID] = {
val hedgehogVersion =
if (scalaVersion.startsWith("3.0"))
props.HedgehogVersion
else
props.HedgehogLatestVersion
lazy val tests = new {

List(
"qa.hedgehog" %% "hedgehog-core" % hedgehogVersion % Test,
"qa.hedgehog" %% "hedgehog-runner" % hedgehogVersion % Test,
"qa.hedgehog" %% "hedgehog-sbt" % hedgehogVersion % Test
)
def hedgehogLibs(scalaVersion: String): List[ModuleID] = {
val hedgehogVersion =
if (scalaVersion.startsWith("3.0"))
props.HedgehogVersion
else
props.HedgehogLatestVersion

List(
"qa.hedgehog" %% "hedgehog-core" % hedgehogVersion % Test,
"qa.hedgehog" %% "hedgehog-runner" % hedgehogVersion % Test,
"qa.hedgehog" %% "hedgehog-sbt" % hedgehogVersion % Test
)
}

val scalaCollectionCompat =
"org.scala-lang.modules" %% "scala-collection-compat" % props.ScalaCollectionCompatVersion % Test
}
}

Expand All @@ -237,7 +252,6 @@ def module(projectName: String, crossProject: CrossProject.Builder): CrossProjec
.in(file(s"modules/$prefixedName"))
.settings(
name := prefixedName,
testFrameworks ~= (testFws => (TestFramework("hedgehog.sbt.Framework") +: testFws).distinct),
)
.settings(
libraryDependencies ++= {
Expand Down Expand Up @@ -265,14 +279,14 @@ def module(projectName: String, crossProject: CrossProject.Builder): CrossProjec
libraryDependencies :=
crossVersionProps(Seq.empty[ModuleID], SemVer.parseUnsafe(scalaVersion.value)) {
case (SemVer.Major(3), SemVer.Minor(0), _) =>
libs.hedgehogLibs(scalaVersion.value) ++ libraryDependencies.value ++
libs.tests.hedgehogLibs(scalaVersion.value) ++ libraryDependencies.value ++
libraryDependencies.value.filterNot(m => m.organization == "org.wartremover" && m.name == "wartremover")

case (Major(3), _, _) =>
libs.hedgehogLibs(scalaVersion.value) ++ libraryDependencies.value
libs.tests.hedgehogLibs(scalaVersion.value) ++ libraryDependencies.value

case x =>
libs.hedgehogLibs(scalaVersion.value) ++ libraryDependencies.value
libs.tests.hedgehogLibs(scalaVersion.value) ++ libraryDependencies.value
},
libraryDependencies := (
if (isScala3(scalaVersion.value)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,42 +27,49 @@ object AdditionalInfo extends Compat {
buildMetaInfo.identifier.map(Dsv.render).mkString(".")
}

def parsePreRelease(value: String): Either[ParseError, Option[PreRelease]] =
def parsePreRelease(value: String): Either[AdditionalInfoParseError, Option[PreRelease]] =
parse(
value,
{
case a @ Dsv(Num(n) :: Nil) =>
if ((n === "0") || n.takeWhile(_ === '0').length === 0)
Right(a)
else
Left(ParseError.leadingZeroNumError(n))
Left(AdditionalInfoParseError.leadingZeroNumError(n))
case a @ Dsv(_) =>
Right(a)
}
).map(_.map(PreRelease.apply))

def parseBuildMetaInfo(value: String): Either[ParseError, Option[BuildMetaInfo]] =
def parseBuildMetaInfo(value: String): Either[AdditionalInfoParseError, Option[BuildMetaInfo]] =
parse(value, Right.apply).map(_.map(BuildMetaInfo.apply))

def parse(
value: String,
validator: Dsv => Either[ParseError, Dsv]
): Either[ParseError, Option[List[Dsv]]] = {
val alphaNumHyphens: Either[ParseError, List[Dsv]] =
validator: Dsv => Either[AdditionalInfoParseError, Dsv]
): Either[AdditionalInfoParseError, Option[List[Dsv]]] = {
val alphaNumHyphens: Either[AdditionalInfoParseError, List[Dsv]] =
Option(value)
.map(_.split("\\."))
.map(_.map(Dsv.parse)) match {
case Some(preRelease) =>
preRelease.foldRight(List.empty[Dsv].asRight[ParseError]) { (x, acc) =>
x.flatMap(validator) match {
preRelease.foldRight(List.empty[Dsv].asRight[AdditionalInfoParseError]) { (x, acc) =>
x.left
.map {
case Dsv.DsvParseError.InvalidAlphaNumHyphenError(c, rest) =>
AdditionalInfoParseError.invalidAlphaNumHyphenError(c, rest)
case Dsv.DsvParseError.EmptyAlphaNumHyphenError =>
AdditionalInfoParseError.emptyAlphaNumHyphenError
}
.flatMap(validator) match {
case Right(alp) =>
acc.map(alps => alp :: alps)
case Left(error) =>
error.asLeft[List[Dsv]]
}
}
case None =>
List.empty[Dsv].asRight[ParseError]
List.empty[Dsv].asRight[AdditionalInfoParseError]
}
alphaNumHyphens.map {
case Nil =>
Expand All @@ -71,4 +78,20 @@ object AdditionalInfo extends Compat {
xs.some
}
}

sealed trait AdditionalInfoParseError
object AdditionalInfoParseError {
final case class LeadingZeroNumError(n: String) extends AdditionalInfoParseError

final case class InvalidAlphaNumHyphenError(c: Char, rest: List[Char]) extends AdditionalInfoParseError
case object EmptyAlphaNumHyphenError extends AdditionalInfoParseError

def leadingZeroNumError(n: String): AdditionalInfoParseError = LeadingZeroNumError(n)

def invalidAlphaNumHyphenError(c: Char, rest: List[Char]): AdditionalInfoParseError =
InvalidAlphaNumHyphenError(c, rest)

def emptyAlphaNumHyphenError: AdditionalInfoParseError = EmptyAlphaNumHyphenError
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,13 @@ object Dsv extends Compat {
@inline def render: String = Dsv.render(dsv)
}

def render(alphaNumHyphenGroup: Dsv): String =
alphaNumHyphenGroup.values.map(Anh.render).mkString
def render(dsv: Dsv): String =
dsv.values.map(Anh.render).mkString

def parse(value: String): Either[ParseError, Dsv] = {
def parse(value: String): Either[DsvParseError, Dsv] = {

@tailrec
def accumulate(cs: List[Char], chars: Anh, acc: Vector[Anh]): Either[ParseError, Vector[Anh]] =
def accumulate(cs: List[Char], chars: Anh, acc: Vector[Anh]): Either[DsvParseError, Vector[Anh]] =
cs match {
case x :: xs =>
if (x.isDigit) {
Expand All @@ -50,7 +50,7 @@ object Dsv extends Compat {
}
} else {
Left(
ParseError.invalidAlphaNumHyphenError(x, xs)
DsvParseError.invalidAlphaNumHyphenError(x, xs)
)
}

Expand All @@ -69,14 +69,25 @@ object Dsv extends Compat {
accumulate(xs, Alphabet(x.toString), Vector.empty)
else
Left(
ParseError.invalidAlphaNumHyphenError(x, xs)
DsvParseError.invalidAlphaNumHyphenError(x, xs)
)

result.map(groups => Dsv(groups.toList))

case Nil =>
Left(ParseError.emptyAlphaNumHyphenError)
Left(DsvParseError.emptyAlphaNumHyphenError)
}

}

sealed trait DsvParseError
object DsvParseError {
final case class InvalidAlphaNumHyphenError(c: Char, rest: List[Char]) extends DsvParseError
case object EmptyAlphaNumHyphenError extends DsvParseError

def invalidAlphaNumHyphenError(c: Char, rest: List[Char]): DsvParseError = InvalidAlphaNumHyphenError(c, rest)

def emptyAlphaNumHyphenError: DsvParseError = EmptyAlphaNumHyphenError

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,16 @@ object ParseError {

def semVerMatchersParseErrors(error: SemVerMatchers.ParseErrors): ParseError = SemVerMatchersParseErrors(error)

def fromAdditionalInfoParserError(additionalInfoParseError: AdditionalInfo.AdditionalInfoParseError): ParseError =
additionalInfoParseError match {
case AdditionalInfo.AdditionalInfoParseError.LeadingZeroNumError(n) =>
ParseError.leadingZeroNumError(n)
case AdditionalInfo.AdditionalInfoParseError.InvalidAlphaNumHyphenError(c, rest) =>
ParseError.invalidAlphaNumHyphenError(c, rest)
case AdditionalInfo.AdditionalInfoParseError.EmptyAlphaNumHyphenError =>
ParseError.emptyAlphaNumHyphenError
}

@SuppressWarnings(Array("org.wartremover.warts.Recursion"))
def render(parseError: ParseError): String = parseError match {
case InvalidAlphaNumHyphenError(c, rest) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,11 +135,11 @@ object SemVer {
val metaInfo = AdditionalInfo.parseBuildMetaInfo(meta)
(preRelease, metaInfo) match {
case (Left(preError), Left(metaError)) =>
Left(ParseError.combine(preError, metaError))
Left(ParseError.combine(ParseError.fromAdditionalInfoParserError(preError), ParseError.fromAdditionalInfoParserError(metaError)))
case (Left(preError), _) =>
Left(ParseError.preReleaseParseError(preError))
Left(ParseError.preReleaseParseError(ParseError.fromAdditionalInfoParserError(preError)))
case (_, Left(metaError)) =>
Left(ParseError.buildMetadataParseError(metaError))
Left(ParseError.buildMetadataParseError(ParseError.fromAdditionalInfoParserError(metaError)))
case (Right(preR), Right(metaI)) =>
Right(
SemVer(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package just.semver

import just.Common._
import just.Common.*

/** @author Kevin Lee
* @since 2018-10-21
Expand All @@ -25,49 +25,49 @@ object AdditionalInfo extends Compat {
}
}

def parsePreRelease(value: String): Either[ParseError, Option[PreRelease]] =
def parsePreRelease(value: String): Either[AdditionalInfoParseError, Option[PreRelease]] =
parse(
value,
{
case a @ Dsv(Num(n) :: Nil) =>
if ((n === "0") || n.takeWhile(_ === '0').length == 0)
if ((n === "0") || n.takeWhile(_ === '0').length === 0)
Right(a)
else
Left(ParseError.leadingZeroNumError(n))
Left(AdditionalInfoParseError.leadingZeroNumError(n))
case a @ Dsv(_) =>
Right(a)
}
).map(_.map(PreRelease.apply))

def parseBuildMetaInfo(value: String): Either[ParseError, Option[BuildMetaInfo]] =
def parseBuildMetaInfo(value: String): Either[AdditionalInfoParseError, Option[BuildMetaInfo]] =
parse(value, Right.apply).map(_.map(BuildMetaInfo.apply))

def parse(
value: String,
validator: Dsv => Either[ParseError, Dsv]
): Either[ParseError, Option[List[Dsv]]] = {
val alphaNumHyphens: Either[ParseError, List[Dsv]] =
validator: Dsv => Either[AdditionalInfoParseError, Dsv]
): Either[AdditionalInfoParseError, Option[List[Dsv]]] = {
val alphaNumHyphens: Either[AdditionalInfoParseError, List[Dsv]] =
Option(value)
.flatMap { s =>
Option(s.split("\\.")).map { array =>
array.nn.toList.collect {
case s: String =>
s
}
}
}
.map(_.split("\\."))
.map(_.map(Dsv.parse)) match {
case Some(preRelease) =>
preRelease.foldRight(List.empty[Dsv].asRight[ParseError]) { (x, acc) =>
x.flatMap(validator) match {
preRelease.foldRight(List.empty[Dsv].asRight[AdditionalInfoParseError]) { (x, acc) =>
x.left
.map {
case Dsv.DsvParseError.InvalidAlphaNumHyphenError(c, rest) =>
AdditionalInfoParseError.invalidAlphaNumHyphenError(c, rest)
case Dsv.DsvParseError.EmptyAlphaNumHyphenError =>
AdditionalInfoParseError.emptyAlphaNumHyphenError
}
.flatMap(validator) match {
case Right(alp) =>
acc.map(alps => alp :: alps)
case Left(error) =>
error.asLeft[List[Dsv]]
}
}
case None =>
List.empty[Dsv].asRight[ParseError]
List.empty[Dsv].asRight[AdditionalInfoParseError]
}
alphaNumHyphens.map {
case Nil =>
Expand All @@ -76,4 +76,20 @@ object AdditionalInfo extends Compat {
xs.some
}
}

sealed trait AdditionalInfoParseError
object AdditionalInfoParseError {
final case class LeadingZeroNumError(n: String) extends AdditionalInfoParseError

final case class InvalidAlphaNumHyphenError(c: Char, rest: List[Char]) extends AdditionalInfoParseError
case object EmptyAlphaNumHyphenError extends AdditionalInfoParseError

def leadingZeroNumError(n: String): AdditionalInfoParseError = LeadingZeroNumError(n)

def invalidAlphaNumHyphenError(c: Char, rest: List[Char]): AdditionalInfoParseError =
InvalidAlphaNumHyphenError(c, rest)

def emptyAlphaNumHyphenError: AdditionalInfoParseError = EmptyAlphaNumHyphenError
}

}
Loading

0 comments on commit be80145

Please sign in to comment.