aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorvlad <vlad@driver.xyz>2017-10-19 14:42:50 -0700
committervlad <vlad@driver.xyz>2017-10-19 14:42:50 -0700
commitc13a90f7dc6565e0beddcc6a61609d4e131f55ba (patch)
tree9d56f512986394f0bb3901ef09abaea809dae667
parent2af63e7fb8b15568adaf2d9f0a6b395d6719fd71 (diff)
downloaddriver-core-c13a90f7dc6565e0beddcc6a61609d4e131f55ba.tar.gz
driver-core-c13a90f7dc6565e0beddcc6a61609d4e131f55ba.tar.bz2
driver-core-c13a90f7dc6565e0beddcc6a61609d4e131f55ba.zip
[RFC] Using "Refined" library (https://github.com/fthomas/refined) to allow defining entities with more precise types
-rw-r--r--build.sbt1
-rw-r--r--src/main/scala/xyz/driver/core/core.scala14
-rw-r--r--src/main/scala/xyz/driver/core/generators.scala14
-rw-r--r--src/main/scala/xyz/driver/core/json.scala19
4 files changed, 46 insertions, 2 deletions
diff --git a/build.sbt b/build.sbt
index 2770f58..775da1b 100644
--- a/build.sbt
+++ b/build.sbt
@@ -22,5 +22,6 @@ lazy val core = (project in file("."))
"com.typesafe.slick" %% "slick" % "3.2.1",
"com.typesafe" % "config" % "1.2.1",
"com.typesafe.scala-logging" %% "scala-logging" % "3.5.0",
+ "eu.timepit" %% "refined" % "0.8.4",
"ch.qos.logback" % "logback-classic" % "1.1.11"
))
diff --git a/src/main/scala/xyz/driver/core/core.scala b/src/main/scala/xyz/driver/core/core.scala
index 4747574..c405962 100644
--- a/src/main/scala/xyz/driver/core/core.scala
+++ b/src/main/scala/xyz/driver/core/core.scala
@@ -1,6 +1,8 @@
package xyz.driver
import scalaz.{Equal, Monad, OptionT}
+import eu.timepit.refined.api.Refined
+import eu.timepit.refined.collection.NonEmpty
package object core {
@@ -83,6 +85,18 @@ package core {
implicit def nameOrdering[T]: Ordering[Name[T]] = Ordering.by(_.value)
}
+ final case class NonEmptyName[+Tag](value: String Refined NonEmpty) {
+ @inline def length: Int = value.value.length
+ override def toString: String = value.value
+ }
+
+ object NonEmptyName {
+ implicit def nonEmptyNameEqual[T]: Equal[NonEmptyName[T]] =
+ Equal.equal[NonEmptyName[T]](_.value.value == _.value.value)
+
+ implicit def nonEmptyNameOrdering[T]: Ordering[NonEmptyName[T]] = Ordering.by(_.value.value)
+ }
+
final case class Revision[T](id: String)
object Revision {
diff --git a/src/main/scala/xyz/driver/core/generators.scala b/src/main/scala/xyz/driver/core/generators.scala
index 9242fd9..f3913e5 100644
--- a/src/main/scala/xyz/driver/core/generators.scala
+++ b/src/main/scala/xyz/driver/core/generators.scala
@@ -1,12 +1,16 @@
package xyz.driver.core
import java.math.MathContext
+import java.util.UUID
import xyz.driver.core.time.{Time, TimeRange}
import xyz.driver.core.date.Date
import scala.reflect.ClassTag
import scala.util.Random
+import eu.timepit.refined.refineV
+import eu.timepit.refined.api.Refined
+import eu.timepit.refined.collection._
object generators {
@@ -35,13 +39,19 @@ object generators {
def nextName[T](maxLength: Int = DefaultMaxLength): Name[T] = Name[T](nextString(maxLength))
- def nextUuid() = java.util.UUID.randomUUID
+ def nextUuid(): UUID = java.util.UUID.randomUUID
- def nextRevision[T]() = Revision[T](nextUuid().toString)
+ def nextRevision[T](): Revision[T] = Revision[T](nextUuid().toString)
def nextString(maxLength: Int = DefaultMaxLength): String =
(oneOf[Char](StringLetters) +: arrayOf(oneOf[Char](StringLetters), maxLength - 1)).mkString
+ def nextNonEmptyString(maxLength: Int = DefaultMaxLength): String Refined NonEmpty = {
+ refineV[NonEmpty](
+ (oneOf[Char](StringLetters) +: arrayOf(oneOf[Char](StringLetters), maxLength - 1)).mkString
+ ).right.get
+ }
+
def nextOption[T](value: => T): Option[T] = if (nextBoolean()) Option(value) else None
def nextPair[L, R](left: => L, right: => R): (L, R) = (left, right)
diff --git a/src/main/scala/xyz/driver/core/json.scala b/src/main/scala/xyz/driver/core/json.scala
index 6e780ed..c14424d 100644
--- a/src/main/scala/xyz/driver/core/json.scala
+++ b/src/main/scala/xyz/driver/core/json.scala
@@ -14,6 +14,8 @@ import xyz.driver.core.auth.AuthCredentials
import xyz.driver.core.date.{Date, Month}
import xyz.driver.core.domain.{Email, PhoneNumber}
import xyz.driver.core.time.Time
+import eu.timepit.refined.refineV
+import eu.timepit.refined.api.{Refined, Validate}
object json {
import DefaultJsonProtocol._
@@ -213,6 +215,23 @@ object json {
}
}
+ /**
+ * Provides the JsonFormat for the Refined types provided by the Refined library.
+ *
+ * @see https://github.com/fthomas/refined
+ */
+ implicit def refinedJsonFormat[T, Predicate](implicit valueFormat: JsonFormat[T],
+ validate: Validate[T, Predicate]): JsonFormat[Refined[T, Predicate]] =
+ new JsonFormat[Refined[T, Predicate]] {
+ def write(x: T Refined Predicate): JsValue = valueFormat.write(x.value)
+ def read(value: JsValue): T Refined Predicate = {
+ refineV[Predicate](valueFormat.read(value))(validate) match {
+ case Right(refinedValue) => refinedValue
+ case Left(refinementError) => deserializationError(refinementError)
+ }
+ }
+ }
+
val jsValueToStringMarshaller: Marshaller[JsValue, String] =
Marshaller.strict[JsValue, String](value => Marshalling.Opaque[String](() => value.compactPrint))