-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
13 changed files
with
822 additions
and
753 deletions.
There are no files selected for viewing
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
package just.semver | ||
|
||
import just.Common.compareElems | ||
|
||
import just.fp.compat.EitherCompat | ||
import just.fp.syntax._ | ||
|
||
/** | ||
* @author Kevin Lee | ||
* @since 2018-10-21 | ||
*/ | ||
object AdditionalInfo { | ||
|
||
import AlphaNumHyphen._ | ||
|
||
final case class Identifier(values: List[AlphaNumHyphenGroup]) extends AnyVal | ||
|
||
object Identifier { | ||
|
||
def compare(a: Identifier, b: Identifier): Int = | ||
compareElems(a.values, b.values) | ||
|
||
def render(identifier: Identifier): String = | ||
identifier.values.map(AlphaNumHyphenGroup.render).mkString(".") | ||
|
||
} | ||
|
||
final case class PreRelease(identifier: Identifier) | ||
final case class BuildMetaInfo(identifier: Identifier) | ||
|
||
def parsePreRelease(value: String): Either[ParseError, Option[PreRelease]] = | ||
EitherCompat.map(parse(value, { | ||
case a @ AlphaNumHyphenGroup(Num(n) :: Nil) => | ||
if ((n === "0") || n.takeWhile(_ === '0').length === 0) | ||
Right(a) | ||
else | ||
Left(ParseError.leadingZeroNumError(n)) | ||
case a @ AlphaNumHyphenGroup(_) => | ||
Right(a) | ||
}))(_.map(PreRelease)) | ||
|
||
def parseBuildMetaInfo(value: String): Either[ParseError, Option[BuildMetaInfo]] = | ||
EitherCompat.map(parse(value, Right.apply))(_.map(BuildMetaInfo)) | ||
|
||
def parse( | ||
value: String | ||
, validator: AlphaNumHyphenGroup => Either[ParseError, AlphaNumHyphenGroup] | ||
): Either[ParseError, Option[Identifier]] = { | ||
val alphaNumHyphens: Either[ParseError, List[AlphaNumHyphenGroup]] = | ||
Option(value) | ||
.map(_.split("\\.")) | ||
.map(_.map(AlphaNumHyphenGroup.parse)) match { | ||
case Some(preRelease) => | ||
preRelease.foldRight[Either[ParseError, List[AlphaNumHyphenGroup]]](Right(List.empty)){ | ||
(x, acc) => | ||
EitherCompat.flatMap(x)(validator) match { | ||
case Right(alp) => | ||
EitherCompat.map(acc)(alps => alp :: alps) | ||
case Left(error) => | ||
Left(error) | ||
} | ||
} | ||
case None => | ||
Right(List.empty) | ||
} | ||
EitherCompat.map(alphaNumHyphens) { | ||
case Nil => | ||
None | ||
case xs => | ||
Some(Identifier(xs)) | ||
} | ||
} | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
package just.semver | ||
|
||
/** | ||
* @author Kevin Lee | ||
* @since 2018-10-21 | ||
*/ | ||
sealed trait AlphaNumHyphen extends Ordered[AlphaNumHyphen] { | ||
|
||
import AlphaNumHyphen._ | ||
|
||
override def compare(that: AlphaNumHyphen): Int = | ||
(this, that) match { | ||
case (Num(thisValue), Num(thatValue)) => | ||
Ordering[Int].compare(thisValue.toInt, thatValue.toInt) | ||
case (Num(_), Alphabet(_)) => | ||
-1 | ||
case (Num(_), Hyphen) => | ||
-1 | ||
case (Alphabet(_), Num(_)) => | ||
1 | ||
case (Alphabet(thisValue), Alphabet(thatValue)) => | ||
thisValue.compareTo(thatValue) | ||
case (Alphabet(_), Hyphen) => | ||
1 | ||
case (Hyphen, Num(_)) => | ||
1 | ||
case (Hyphen, Alphabet(_)) => | ||
-1 | ||
case (Hyphen, Hyphen) => | ||
0 | ||
} | ||
} | ||
|
||
object AlphaNumHyphen { | ||
|
||
final case class Alphabet(value: String) extends AlphaNumHyphen | ||
final case class Num(value: String) extends AlphaNumHyphen | ||
case object Hyphen extends AlphaNumHyphen | ||
|
||
def alphabet(value: String): AlphaNumHyphen = | ||
Alphabet(value) | ||
|
||
def num(value: Int): AlphaNumHyphen = | ||
Num(value.toString) | ||
|
||
def numFromStringUnsafe(value: String): AlphaNumHyphen = | ||
if (value.forall(_.isDigit)) | ||
Num(value) | ||
else | ||
sys.error(s"The Num value cannot contain any non-digit. value: $value") | ||
|
||
def hyphen: AlphaNumHyphen = | ||
Hyphen | ||
|
||
def render(alphaNumHyphen: AlphaNumHyphen): String = alphaNumHyphen match { | ||
case Num(value) => value | ||
case Alphabet(value) => value | ||
case Hyphen => "-" | ||
} | ||
|
||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
package just.semver | ||
|
||
import just.Common.compareElems | ||
|
||
import just.fp.compat.EitherCompat | ||
import just.fp.syntax._ | ||
|
||
import scala.annotation.tailrec | ||
|
||
/** | ||
* @author Kevin Lee | ||
* @since 2018-10-21 | ||
*/ | ||
final case class AlphaNumHyphenGroup(values: List[AlphaNumHyphen]) extends Ordered[AlphaNumHyphenGroup] { | ||
override def compare(that: AlphaNumHyphenGroup): Int = | ||
compareElems(this.values, that.values) | ||
} | ||
|
||
object AlphaNumHyphenGroup { | ||
|
||
import AlphaNumHyphen._ | ||
|
||
def render(alphaNumHyphenGroup: AlphaNumHyphenGroup): String = | ||
alphaNumHyphenGroup.values.map(AlphaNumHyphen.render).mkString | ||
|
||
def parse(value: String): Either[ParseError, AlphaNumHyphenGroup] = { | ||
|
||
@tailrec | ||
def accumulate(cs: List[Char], chars: AlphaNumHyphen, acc: Vector[AlphaNumHyphen]): Either[ParseError, Vector[AlphaNumHyphen]] = | ||
cs match { | ||
case x :: xs => | ||
|
||
if (x.isDigit) { | ||
chars match { | ||
case Num(ns) => | ||
accumulate(xs, Num(ns :+ x), acc) | ||
|
||
case _ => | ||
accumulate(xs, Num(x.toString), acc :+ chars) | ||
} | ||
} else if (x === '-') { | ||
accumulate(xs, Hyphen, acc :+ chars) | ||
} else if (x.isUpper || x.isLower) { | ||
chars match { | ||
case Alphabet(as) => | ||
accumulate(xs, Alphabet(as :+ x), acc) | ||
|
||
case _ => | ||
accumulate(xs, Alphabet(x.toString), acc :+ chars) | ||
} | ||
} else { | ||
Left( | ||
ParseError.invalidAlphaNumHyphenError(x, xs) | ||
) | ||
} | ||
|
||
case Nil => | ||
Right(acc :+ chars) | ||
} | ||
|
||
value.toList match { | ||
case x :: xs => | ||
val result = | ||
if (x.isDigit) { | ||
accumulate(xs, Num(x.toString), Vector.empty) | ||
} else if (x === '-') | ||
accumulate(xs, Hyphen, Vector.empty) | ||
else if (x.isLower || x.isUpper) | ||
accumulate(xs, Alphabet(x.toString), Vector.empty) | ||
else | ||
Left( | ||
ParseError.invalidAlphaNumHyphenError(x, xs) | ||
) | ||
|
||
EitherCompat.map(result)(groups => AlphaNumHyphenGroup(groups.toList)) | ||
|
||
case Nil => | ||
Left(ParseError.emptyAlphaNumHyphenError) | ||
} | ||
|
||
} | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
package just.semver | ||
|
||
/** | ||
* @author Kevin Lee | ||
* @since 2018-10-21 | ||
*/ | ||
sealed trait ParseError | ||
|
||
object ParseError { | ||
|
||
final case class InvalidAlphaNumHyphenError(c: Char, rest: List[Char]) extends ParseError | ||
case object EmptyAlphaNumHyphenError extends ParseError | ||
|
||
final case class LeadingZeroNumError(n: String) extends ParseError | ||
|
||
final case class PreReleaseParseError(parseError: ParseError) extends ParseError | ||
final case class BuildMetadataParseError(parseError: ParseError) extends ParseError | ||
|
||
final case class CombinedParseError(preReleaseError: ParseError, buildMetadataError: ParseError) extends ParseError | ||
|
||
final case class InvalidVersionStringError(value: String) extends ParseError | ||
|
||
@SuppressWarnings(Array("org.wartremover.warts.Recursion")) | ||
def render(parseError: ParseError): String = parseError match { | ||
case InvalidAlphaNumHyphenError(c, rest) => | ||
s"Invalid char for AlphaNumHyphen found. value: ${c.toString} / rest: ${rest.toString}" | ||
|
||
case EmptyAlphaNumHyphenError => | ||
"AlphaNumHyphen cannot be empty but the given value is an empty String." | ||
|
||
case LeadingZeroNumError(n) => | ||
s"Invalid Num value. It should not have any leading zeros. value: $n" | ||
|
||
case PreReleaseParseError(error) => | ||
s"Error in parsing pre-release: ${render(error)}" | ||
|
||
case BuildMetadataParseError(error) => | ||
s"Error in parsing build meta data: ${render(error)}" | ||
|
||
case CombinedParseError(preReleaseError, buildMetadataError) => | ||
s"""Errors: | ||
|[1] ${render(preReleaseError)} | ||
|[2] ${render(buildMetadataError)} | ||
|""".stripMargin | ||
|
||
case InvalidVersionStringError(value) => | ||
s"Invalid SemVer String. value: $value" | ||
} | ||
|
||
def invalidAlphaNumHyphenError(c: Char, rest: List[Char]): ParseError = | ||
InvalidAlphaNumHyphenError(c, rest) | ||
|
||
def emptyAlphaNumHyphenError: ParseError = | ||
EmptyAlphaNumHyphenError | ||
|
||
def leadingZeroNumError(n: String): ParseError = | ||
LeadingZeroNumError(n) | ||
|
||
def preReleaseParseError(parseError: ParseError): ParseError = | ||
PreReleaseParseError(parseError) | ||
|
||
def buildMetadataParseError(parseError: ParseError): ParseError = | ||
BuildMetadataParseError(parseError) | ||
|
||
def combine(preReleaseError: ParseError, buildMetadataError: ParseError): ParseError = | ||
CombinedParseError( | ||
preReleaseParseError(preReleaseError) | ||
, buildMetadataParseError(buildMetadataError) | ||
) | ||
|
||
def invalidVersionStringError(value: String): ParseError = | ||
InvalidVersionStringError(value) | ||
|
||
} |
Oops, something went wrong.