diff options
author | vlad <vlad@driver.xyz> | 2017-10-19 14:42:50 -0700 |
---|---|---|
committer | vlad <vlad@driver.xyz> | 2017-10-19 14:42:50 -0700 |
commit | c13a90f7dc6565e0beddcc6a61609d4e131f55ba (patch) | |
tree | 9d56f512986394f0bb3901ef09abaea809dae667 | |
parent | 2af63e7fb8b15568adaf2d9f0a6b395d6719fd71 (diff) | |
download | driver-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.sbt | 1 | ||||
-rw-r--r-- | src/main/scala/xyz/driver/core/core.scala | 14 | ||||
-rw-r--r-- | src/main/scala/xyz/driver/core/generators.scala | 14 | ||||
-rw-r--r-- | src/main/scala/xyz/driver/core/json.scala | 19 |
4 files changed, 46 insertions, 2 deletions
@@ -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)) |