Skip to content

Commit

Permalink
Merge pull request #456 from umbreak/shapeless_typeable
Browse files Browse the repository at this point in the history
Added support to generate a Typeable for a refined type
fthomas authored Mar 26, 2018

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
2 parents 984de6f + d825a2e commit 0f9d287
Showing 4 changed files with 98 additions and 2 deletions.
26 changes: 24 additions & 2 deletions build.sbt
Original file line number Diff line number Diff line change
@@ -27,7 +27,15 @@ val scalaCheckDep =
Def.setting("org.scalacheck" %%% "scalacheck" % scalaCheckVersion)

val allSubprojects =
Seq("cats", "core", "eval", "jsonpath", "pureconfig", "scalacheck", "scalaz", "scodec")
Seq("cats",
"core",
"eval",
"jsonpath",
"pureconfig",
"scalacheck",
"scalaz",
"scodec",
"shapeless")
val allSubprojectsJVM = allSubprojects.map(_ + "JVM")
val allSubprojectsJS = {
val jvmOnlySubprojects = Seq("jsonpath", "pureconfig")
@@ -58,7 +66,9 @@ lazy val root = project
scalazJVM,
scalazJS,
scodecJVM,
scodecJS
scodecJS,
shapelessJVM,
shapelessJS
)
.settings(commonSettings)
.settings(noPublishSettings)
@@ -220,6 +230,18 @@ lazy val scodec = crossProject(JSPlatform, JVMPlatform)
lazy val scodecJVM = scodec.jvm
lazy val scodecJS = scodec.js

lazy val shapeless = crossProject(JSPlatform, JVMPlatform)
.configureCross(moduleCrossConfig("shapeless"))
.dependsOn(core % "compile->compile;test->test")
.settings(
initialCommands += s"""
import $rootPkg.shapeless._
"""
)

lazy val shapelessJVM = shapeless.jvm
lazy val shapelessJS = shapeless.js

/// settings

lazy val commonSettings = Def.settings(
1 change: 1 addition & 0 deletions latestVersion.sbt
Original file line number Diff line number Diff line change
@@ -3,6 +3,7 @@ latestVersion in ThisBuild := "0.8.7"
latestVersionInSeries in ThisBuild := Some("0.8.7")

unreleasedModules in ThisBuild := Set(
"refined-shapeless"
// Example:
// "refined-eval"
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package eu.timepit.refined.shapeless

import _root_.shapeless.Typeable
import eu.timepit.refined.api.{RefType, Validate}
import scala.util.Right

package object typeable {

/**
* `Typeable` instance for refined types.
*/
implicit def refTypeTypeable[F[_, _], T, P](implicit rt: RefType[F],
V: Validate[T, P],
T: Typeable[T],
P: Typeable[P]): Typeable[F[T, P]] =
new Typeable[F[T, P]] {
override def cast(t: Any): Option[F[T, P]] =
T.cast(t)
.flatMap(casted =>
rt.refine[P](casted) match {
case Right(v) => Some(v)
case _ => None
})
override def describe: String = s"Refined[${T.describe}, ${P.describe}]"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package eu.timepit.refined.shapeless.typeable

import eu.timepit.refined.W
import eu.timepit.refined.api.{Refined, RefinedTypeOps}
import eu.timepit.refined.string.MatchesRegex
import eu.timepit.refined.types.numeric.PosInt
import org.scalacheck.Prop._
import org.scalacheck.Properties
import shapeless.Typeable

class TypeableSpec extends Properties("shapeless") {

property("Typeable cast success") = secure {
val value: PosInt = PosInt.unsafeFrom(5)
typeableCast[PosInt](5) ?= Some(value)
}

property("Typeable cast fail") = secure {
typeableCast[PosInt](0) ?= None
}

property("Typeable describe") = secure {
typeableDescribe[PosInt] ?= "Refined[Int, Greater[_0]]"
}

property("Typeable cast success string regex") = secure {
type Word = String Refined MatchesRegex[W.`"[a-zA-Z]*"`.T]
object Word extends RefinedTypeOps[Word, String]
val value: Word = Word.unsafeFrom("AlloweD")
typeableCast[Word]("AlloweD") ?= Some(value)
}

property("Typeable cast fail string regex") = secure {
type Word = String Refined MatchesRegex[W.`"[a-zA-Z]*"`.T]
typeableCast[Word]("Not Allowed") ?= None
}

property("Typeable string regex describe") = secure {
type Word = String Refined MatchesRegex[W.`"[a-zA-Z]*"`.T]
typeableDescribe[Word] ?= """Refined[String, MatchesRegex[String([a-zA-Z]*)]]"""
}

private def typeableDescribe[T](implicit T: Typeable[T]): String = T.describe

private def typeableCast[T](value: Any)(implicit T: Typeable[T]): Option[T] = T.cast(value)

}

0 comments on commit 0f9d287

Please sign in to comment.