Skip to content

Commit

Permalink
Restructured files
Browse files Browse the repository at this point in the history
  • Loading branch information
kevin-lee committed Oct 19, 2019
1 parent f8a776f commit 21d1a99
Show file tree
Hide file tree
Showing 13 changed files with 822 additions and 753 deletions.
73 changes: 73 additions & 0 deletions src/main/scala/just/semver/AdditionalInfo.scala
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))
}
}
}
61 changes: 61 additions & 0 deletions src/main/scala/just/semver/AlphaNumHyphen.scala
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 => "-"
}

}
82 changes: 82 additions & 0 deletions src/main/scala/just/semver/AlphaNumHyphenGroup.scala
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)
}

}
}
74 changes: 74 additions & 0 deletions src/main/scala/just/semver/ParseError.scala
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)

}
Loading

0 comments on commit 21d1a99

Please sign in to comment.