Repository: scala-steward-org/scala-steward
Pull request #2013 (2021-03-18 16:53:35)

Use cats.parse.SemVer for our SemVer.parse

codecov[bot]: # [Codecov](https://codecov.io/gh/scala-steward-org/scala-steward/pull/2013?src=pr&el=h1) Report
> Merging [#2013](https://codecov.io/gh/scala-steward-org/scala-steward/pull/2013?src=pr&el=desc) (9f83408) into [master](https://codecov.io/gh/scala-steward-org/scala-steward/commit/d89e000889e470010a810643322ebf9f42fe2a71?el=desc) (d89e000) will **decrease** coverage by `0.09%`.
> The diff coverage is `100.00%`.

[![Impacted file tree graph](https://codecov.io/gh/scala-steward-org/scala-steward/pull/2013/graphs/tree.svg?width=650&height=150&src=pr&token=udzMsc1Dne)](https://codecov.io/gh/scala-steward-org/scala-steward/pull/2013?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master    #2013      +/-   ##
==========================================
- Coverage   78.69%   78.59%   -0.10%     
==========================================
  Files         131      130       -1     
  Lines        2262     2252      -10     
  Branches       66       57       -9     
==========================================
- Hits         1780     1770      -10     
  Misses        482      482              
```


| [Impacted Files](https://codecov.io/gh/scala-steward-org/scala-steward/pull/2013?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [...main/scala/org/scalasteward/core/data/SemVer.scala](https://codecov.io/gh/scala-steward-org/scala-steward/pull/2013/diff?src=pr&el=tree#diff-bW9kdWxlcy9jb3JlL3NyYy9tYWluL3NjYWxhL29yZy9zY2FsYXN0ZXdhcmQvY29yZS9kYXRhL1NlbVZlci5zY2FsYQ==) | `100.00% <100.00%> (ø)` | |
| [...main/scala/org/scalasteward/core/util/string.scala](https://codecov.io/gh/scala-steward-org/scala-steward/pull/2013/diff?src=pr&el=tree#diff-bW9kdWxlcy9jb3JlL3NyYy9tYWluL3NjYWxhL29yZy9zY2FsYXN0ZXdhcmQvY29yZS91dGlsL3N0cmluZy5zY2FsYQ==) | `96.00% <0.00%> (-4.00%)` | :arrow_down: |
| [...la-2.12/org/scalasteward/mill/plugin/package.scala](https://codecov.io/gh/scala-steward-org/scala-steward/pull/2013/diff?src=pr&el=tree#diff-bW9kdWxlcy9taWxsLXBsdWdpbi9zcmMvbWFpbi9zY2FsYS0yLjEyL29yZy9zY2FsYXN0ZXdhcmQvbWlsbC9wbHVnaW4vcGFja2FnZS5zY2FsYQ==) | | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/scala-steward-org/scala-steward/pull/2013?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/scala-steward-org/scala-steward/pull/2013?src=pr&el=footer). Last update [d89e000...9f83408](https://codecov.io/gh/scala-steward-org/scala-steward/pull/2013?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/build.sbt b/build.sbt
index d0a65946..5f57e012 100644
--- a/build.sbt
+++ b/build.sbt
@@ -104,7 +104,6 @@ lazy val core = myCrossProject("core")
       Dependencies.log4catsSlf4j,
       Dependencies.monocleCore,
       Dependencies.refined,
-      Dependencies.refinedCats,
       Dependencies.scalacacheCaffeine,
       Dependencies.scalacacheCatsEffect,
       Dependencies.logbackClassic % Runtime,
diff --git a/modules/core/src/main/scala/org/scalasteward/core/data/SemVer.scala b/modules/core/src/main/scala/org/scalasteward/core/data/SemVer.scala
index e63528c3..6bcddc99 100644
--- a/modules/core/src/main/scala/org/scalasteward/core/data/SemVer.scala
+++ b/modules/core/src/main/scala/org/scalasteward/core/data/SemVer.scala
@@ -17,43 +17,21 @@
 package org.scalasteward.core.data
 
 import cats.syntax.all._
-import eu.timepit.refined.cats.refTypeEq
-import eu.timepit.refined.types.numeric.NonNegBigInt
-import eu.timepit.refined.types.string.NonEmptyString
 import org.scalasteward.core.data.SemVer.Change._
-import org.scalasteward.core.util.string.parseNonNegBigInt
 
 final case class SemVer(
-    major: NonNegBigInt,
-    minor: NonNegBigInt,
-    patch: NonNegBigInt,
-    preRelease: Option[NonEmptyString],
-    buildMetadata: Option[NonEmptyString]
-) {
-  def render: String =
-    s"$major.$minor.$patch" + preRelease.fold("")("-" + _) + buildMetadata.fold("")("+" + _)
-}
+    major: String,
+    minor: String,
+    patch: String,
+    preRelease: Option[String] = None,
+    buildMetadata: Option[String] = None
+)
 
 object SemVer {
-  def parse(s: String): Option[SemVer] = {
-    def parseIdentifier(s: String): Option[NonEmptyString] =
-      Option(s).map(_.drop(1)).flatMap(NonEmptyString.unapply)
-
-    val pattern = raw"""(\d+)\.(\d+)\.(\d+)(\-[^\+]+)?(\+.+)?""".r
-    s match {
-      case pattern(majorStr, minorStr, patchStr, preReleaseStr, buildMetadataStr) =>
-        for {
-          major <- parseNonNegBigInt(majorStr)
-          minor <- parseNonNegBigInt(minorStr)
-          patch <- parseNonNegBigInt(patchStr)
-          preRelease = parseIdentifier(preReleaseStr)
-          buildMetadata = parseIdentifier(buildMetadataStr)
-          semVer = SemVer(major, minor, patch, preRelease, buildMetadata)
-          if semVer.render === s
-        } yield semVer
-      case _ => None
+  def parse(s: String): Option[SemVer] =
+    cats.parse.SemVer.semver.parseAll(s).toOption.map { v =>
+      SemVer(v.core.major, v.core.minor, v.core.patch, v.preRelease, v.buildMetadata)
     }
-  }
 
   sealed abstract class Change(val render: String)
   object Change {
diff --git a/modules/core/src/main/scala/org/scalasteward/core/util/string.scala b/modules/core/src/main/scala/org/scalasteward/core/util/string.scala
index c8f50533..5b73b389 100644
--- a/modules/core/src/main/scala/org/scalasteward/core/util/string.scala
+++ b/modules/core/src/main/scala/org/scalasteward/core/util/string.scala
@@ -21,12 +21,11 @@ import cats.syntax.all._
 import eu.timepit.refined.api.Refined
 import eu.timepit.refined.collection.MinSize
 import eu.timepit.refined.refineV
-import eu.timepit.refined.types.numeric.NonNegBigInt
 import org.scalasteward.core.util.Change.{Changed, Unchanged}
-import scala.util.Try
-import scala.util.matching.Regex
 import shapeless.Witness
 
+import scala.util.matching.Regex
+
 object string {
   type MinLengthString[N] = String Refined MinSize[N]
 
@@ -95,9 +94,6 @@ object string {
     s"$line $s $line"
   }
 
-  def parseNonNegBigInt(s: String): Option[NonNegBigInt] =
-    Try(BigInt(s)).toOption.flatMap(NonNegBigInt.unapply)
-
   /** Splits a string between lower and upper case characters.
     *
     * @example {{{
diff --git a/project/Dependencies.scala b/project/Dependencies.scala
index d74fef7f..6f9a7968 100644
--- a/project/Dependencies.scala
+++ b/project/Dependencies.scala
@@ -42,7 +42,6 @@ object Dependencies {
   val munit = "org.scalameta" %% "munit" % "0.7.22"
   val munitScalacheck = "org.scalameta" %% "munit-scalacheck" % munit.revision
   val refined = "eu.timepit" %% "refined" % "0.9.21"
-  val refinedCats = "eu.timepit" %% "refined-cats" % refined.revision
   val refinedScalacheck = "eu.timepit" %% "refined-scalacheck" % refined.revision
   val scalacacheCaffeine = "com.github.cb372" %% "scalacache-caffeine" % "0.28.0"
   val scalacacheCatsEffect =


Test patch:
diff --git a/modules/core/src/test/scala/org/scalasteward/core/data/SemVerTest.scala b/modules/core/src/test/scala/org/scalasteward/core/data/SemVerTest.scala
index 0bf05339..0dfc7a0e 100644
--- a/modules/core/src/test/scala/org/scalasteward/core/data/SemVerTest.scala
+++ b/modules/core/src/test/scala/org/scalasteward/core/data/SemVerTest.scala
@@ -1,26 +1,18 @@
 package org.scalasteward.core.data
 
-import eu.timepit.refined.types.numeric.NonNegBigInt
-import eu.timepit.refined.types.string.NonEmptyString
 import munit.FunSuite
 import org.scalasteward.core.data.SemVer.Change
 
 class SemVerTest extends FunSuite {
-  implicit val toNonNegBigInt: Int => NonNegBigInt =
-    i => NonNegBigInt.unsafeFrom(BigInt(i))
-
-  implicit val toNonEmptyString: String => NonEmptyString =
-    NonEmptyString.unsafeFrom
-
   test("parse: simple examples") {
-    assertEquals(SemVer.parse("1.2.3"), Some(SemVer(1, 2, 3, None, None)))
-    assertEquals(SemVer.parse("0.0.0"), Some(SemVer(0, 0, 0, None, None)))
-    assertEquals(SemVer.parse("123.456.789"), Some(SemVer(123, 456, 789, None, None)))
+    assertEquals(SemVer.parse("1.2.3"), Some(SemVer("1", "2", "3")))
+    assertEquals(SemVer.parse("0.0.0"), Some(SemVer("0", "0", "0")))
+    assertEquals(SemVer.parse("123.456.789"), Some(SemVer("123", "456", "789")))
   }
 
   test("parse: with pre-release identifier") {
-    assertEquals(SemVer.parse("1.0.0-SNAP5"), Some(SemVer(1, 0, 0, Some("SNAP5"), None)))
-    assertEquals(SemVer.parse("9.10.100-0.3.7"), Some(SemVer(9, 10, 100, Some("0.3.7"), None)))
+    assertEquals(SemVer.parse("1.0.0-SNAP5"), Some(SemVer("1", "0", "0", Some("SNAP5"))))
+    assertEquals(SemVer.parse("9.10.100-0.3.7"), Some(SemVer("9", "10", "100", Some("0.3.7"))))
   }
 
   test("parse: empty pre-release identifier") {
@@ -30,11 +22,11 @@ class SemVerTest extends FunSuite {
   test("parse: with build metadata") {
     assertEquals(
       SemVer.parse("1.0.0+20130313144700"),
-      Some(SemVer(1, 0, 0, None, Some("20130313144700")))
+      Some(SemVer("1", "0", "0", None, Some("20130313144700")))
     )
     assertEquals(
       SemVer.parse("1.0.0-beta+exp.sha.5114f85"),
-      Some(SemVer(1, 0, 0, Some("beta"), Some("exp.sha.5114f85")))
+      Some(SemVer("1", "0", "0", Some("beta"), Some("exp.sha.5114f85")))
     )
   }
 
@@ -56,38 +48,23 @@ class SemVerTest extends FunSuite {
   }
 
   test("getChange") {
-    assertEquals(
-      SemVer.getChange(SemVer(1, 3, 4, None, None), SemVer(2, 1, 2, None, None)),
-      Some(Change.Major)
-    )
-    assertEquals(
-      SemVer.getChange(SemVer(2, 3, 4, None, None), SemVer(2, 5, 2, None, None)),
-      Some(Change.Minor)
-    )
-    assertEquals(
-      SemVer.getChange(
-        SemVer(2, 3, 4, Some("SNAP1"), None),
-        SemVer(2, 3, 4, Some("SNAP2"), None)
+    List(
+      (SemVer("1", "3", "4"), SemVer("2", "1", "2"), Some(Change.Major)),
+      (SemVer("2", "3", "4"), SemVer("2", "5", "2"), Some(Change.Minor)),
+      (
+        SemVer("2", "3", "4", Some("SNAP1")),
+        SemVer("2", "3", "4", Some("SNAP2")),
+        Some(Change.PreRelease)
       ),
-      Some(Change.PreRelease)
-    )
-    assertEquals(
-      SemVer.getChange(
-        SemVer(2, 3, 4, Some("M1"), Some("1")),
-        SemVer(2, 3, 4, Some("M1"), Some("2"))
-      ),
-      Some(Change.BuildMetadata)
-    )
-    assertEquals(
-      SemVer.getChange(
-        SemVer(2, 3, 4, Some("M1"), None),
-        SemVer(2, 3, 4, Some("M1"), None)
+      (
+        SemVer("2", "3", "4", Some("M1"), Some("1")),
+        SemVer("2", "3", "4", Some("M1"), Some("2")),
+        Some(Change.BuildMetadata)
       ),
-      None
-    )
-    assertEquals(
-      SemVer.getChange(SemVer(0, 20, 0, Some("M4"), None), SemVer(0, 20, 3, None, None)),
-      Some(Change.PreRelease)
-    )
+      (SemVer("2", "3", "4", Some("M1")), SemVer("2", "3", "4", Some("M1")), None),
+      (SemVer("0", "20", "0", Some("M4")), SemVer("0", "20", "3"), Some(Change.PreRelease))
+    ).foreach { case (from, to, result) =>
+      assertEquals(SemVer.getChange(from, to), result)
+    }
   }
 }