From 01dff4fb2e7063e29a47fe65a7109d5942b00c25 Mon Sep 17 00:00:00 2001 From: "Frank S. Thomas" Date: Sun, 14 Jan 2018 21:05:46 +0100 Subject: [PATCH 1/4] WIP1 --- .../scala/eu/timepit/refined/api/Show.scala | 37 +++++++++++++++++++ .../main/scala/eu/timepit/refined/char.scala | 9 ++++- 2 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 modules/core/shared/src/main/scala/eu/timepit/refined/api/Show.scala diff --git a/modules/core/shared/src/main/scala/eu/timepit/refined/api/Show.scala b/modules/core/shared/src/main/scala/eu/timepit/refined/api/Show.scala new file mode 100644 index 000000000..fb90b8ca7 --- /dev/null +++ b/modules/core/shared/src/main/scala/eu/timepit/refined/api/Show.scala @@ -0,0 +1,37 @@ +package eu.timepit.refined.api + +trait Show[T, P] { + + type R + + final type Res = Result[R] + + // t: Int = 1 => "1" + def show(t: T): String + + // x > 0 + def withPlaceholder(x: String): String + + // x > 0 where x = 1 + def showExpr(t: T): String + + // Predicate failed: x > 0 where x = 1 + def showResult[R](t: T, result: Result[R]) +} + +object Show extends LowPriorityShowInstances {} + +trait LowPriorityShowInstances { + implicit def showFromValidate[T, P](implicit v: Validate[T, P]): Show[T, P] = + new Show[T, P] { + override type R = v.R + + override def show(t: T): String = "" + + override def withPlaceholder(x: String): String = "" + + override def showExpr(t: T): String = "" + + override def showResult[R](t: T, result: Result[R]): Unit = "" + } +} diff --git a/modules/core/shared/src/main/scala/eu/timepit/refined/char.scala b/modules/core/shared/src/main/scala/eu/timepit/refined/char.scala index 78c21f420..aa8b7501f 100644 --- a/modules/core/shared/src/main/scala/eu/timepit/refined/char.scala +++ b/modules/core/shared/src/main/scala/eu/timepit/refined/char.scala @@ -1,6 +1,6 @@ package eu.timepit.refined -import eu.timepit.refined.api.Validate +import eu.timepit.refined.api.{Result, Show, Validate} import eu.timepit.refined.boolean.Or import eu.timepit.refined.char._ @@ -31,6 +31,13 @@ private[refined] trait CharValidate { implicit def digitValidate: Validate.Plain[Char, Digit] = Validate.fromPredicate(_.isDigit, t => s"isDigit('$t')", Digit()) + implicit def digitShow: Show[Char, Digit] = + new Show[Char, Digit] { + override def showExpr(t: Char): String = s"isDigit('$t')" + + override def showResult[R](t: Char, result: Result[R]): Unit = ??? + } + implicit def letterValidate: Validate.Plain[Char, Letter] = Validate.fromPredicate(_.isLetter, t => s"isLetter('$t')", Letter()) From f6d3f3f2b7d4dc9c05bd624551de3d6280c389b9 Mon Sep 17 00:00:00 2001 From: "Frank S. Thomas" Date: Sun, 14 Jan 2018 21:43:33 +0100 Subject: [PATCH 2/4] WIP2 --- .../scala/eu/timepit/refined/api/Show.scala | 19 ++++++++++++------- .../main/scala/eu/timepit/refined/char.scala | 9 +-------- .../internal/RefinePartiallyApplied.scala | 9 ++++++++- .../scala/eu/timepit/refined/numeric.scala | 17 ++++++++++++++++- 4 files changed, 37 insertions(+), 17 deletions(-) diff --git a/modules/core/shared/src/main/scala/eu/timepit/refined/api/Show.scala b/modules/core/shared/src/main/scala/eu/timepit/refined/api/Show.scala index fb90b8ca7..6ba1397fb 100644 --- a/modules/core/shared/src/main/scala/eu/timepit/refined/api/Show.scala +++ b/modules/core/shared/src/main/scala/eu/timepit/refined/api/Show.scala @@ -16,22 +16,27 @@ trait Show[T, P] { def showExpr(t: T): String // Predicate failed: x > 0 where x = 1 - def showResult[R](t: T, result: Result[R]) + def showResult(t: T, r: Res): String } -object Show extends LowPriorityShowInstances {} +object Show extends LowPriorityShowInstances { + + type Aux[T, P, R0] = Show[T, P] { type R = R0 } + + def apply[T, P](implicit s: Show[T, P]): Aux[T, P, s.R] = s +} trait LowPriorityShowInstances { - implicit def showFromValidate[T, P](implicit v: Validate[T, P]): Show[T, P] = + implicit def showFromValidate[T, P, R0](implicit v: Validate.Aux[T, P, R0]): Show.Aux[T, P, R0] = new Show[T, P] { - override type R = v.R + override type R = R0 - override def show(t: T): String = "" + override def show(t: T): String = t.toString override def withPlaceholder(x: String): String = "" - override def showExpr(t: T): String = "" + override def showExpr(t: T): String = v.showExpr(t) - override def showResult[R](t: T, result: Result[R]): Unit = "" + override def showResult(t: T, r: Res): String = v.showResult(t, r) } } diff --git a/modules/core/shared/src/main/scala/eu/timepit/refined/char.scala b/modules/core/shared/src/main/scala/eu/timepit/refined/char.scala index aa8b7501f..78c21f420 100644 --- a/modules/core/shared/src/main/scala/eu/timepit/refined/char.scala +++ b/modules/core/shared/src/main/scala/eu/timepit/refined/char.scala @@ -1,6 +1,6 @@ package eu.timepit.refined -import eu.timepit.refined.api.{Result, Show, Validate} +import eu.timepit.refined.api.Validate import eu.timepit.refined.boolean.Or import eu.timepit.refined.char._ @@ -31,13 +31,6 @@ private[refined] trait CharValidate { implicit def digitValidate: Validate.Plain[Char, Digit] = Validate.fromPredicate(_.isDigit, t => s"isDigit('$t')", Digit()) - implicit def digitShow: Show[Char, Digit] = - new Show[Char, Digit] { - override def showExpr(t: Char): String = s"isDigit('$t')" - - override def showResult[R](t: Char, result: Result[R]): Unit = ??? - } - implicit def letterValidate: Validate.Plain[Char, Letter] = Validate.fromPredicate(_.isLetter, t => s"isLetter('$t')", Letter()) diff --git a/modules/core/shared/src/main/scala/eu/timepit/refined/internal/RefinePartiallyApplied.scala b/modules/core/shared/src/main/scala/eu/timepit/refined/internal/RefinePartiallyApplied.scala index 5558d7002..79432a562 100644 --- a/modules/core/shared/src/main/scala/eu/timepit/refined/internal/RefinePartiallyApplied.scala +++ b/modules/core/shared/src/main/scala/eu/timepit/refined/internal/RefinePartiallyApplied.scala @@ -1,6 +1,6 @@ package eu.timepit.refined.internal -import eu.timepit.refined.api.{RefType, Validate} +import eu.timepit.refined.api.{RefType, Show, Validate} /** * Helper class that allows the type `T` to be inferred from calls like @@ -17,6 +17,13 @@ final class RefinePartiallyApplied[F[_, _], P](rt: RefType[F]) { else Left(v.showResult(t, res)) } + def apply2[T, R](t: T)(implicit v: Validate.Aux[T, P, R], + s: Show.Aux[T, P, R]): Either[String, F[T, P]] = { + val res = v.validate(t) + if (res.isPassed) Right(rt.unsafeWrap(t)) + else Left(s.showResult(t, res)) + } + def unsafeFrom[T](t: T)(implicit v: Validate[T, P]): F[T, P] = apply(t).fold(err => throw new IllegalArgumentException(err), identity) } diff --git a/modules/core/shared/src/main/scala/eu/timepit/refined/numeric.scala b/modules/core/shared/src/main/scala/eu/timepit/refined/numeric.scala index 410cdb63c..41d350ef5 100644 --- a/modules/core/shared/src/main/scala/eu/timepit/refined/numeric.scala +++ b/modules/core/shared/src/main/scala/eu/timepit/refined/numeric.scala @@ -1,6 +1,6 @@ package eu.timepit.refined -import eu.timepit.refined.api.{Inference, Validate} +import eu.timepit.refined.api.{Inference, Result, Show, Validate} import eu.timepit.refined.api.Inference.==> import eu.timepit.refined.boolean.{And, Not} import eu.timepit.refined.numeric._ @@ -161,6 +161,21 @@ private[refined] trait NumericValidate { t => s"($t % ${tn()} == ${to()})", Modulo(wn.value, wo.value) ) + + implicit def greaterEqualShowWit[N <: Nat, T]( + implicit nt: Numeric[T]): Show.Aux[T, GreaterEqual[N], Not[Result[Less[N]]]] = + new Show[T, GreaterEqual[N]] { + override type R = Not[Result[Less[N]]] + + override def show(t: T): String = "" + + override def withPlaceholder(x: String): String = "" + + override def showExpr(t: T): String = "" + + override def showResult(t: T, r: Res): String = + s"Predicate $t blabla failed" + } } private[refined] trait NumericInference { From d8b9f363e372baccf5203c8bd3c248a4973eace2 Mon Sep 17 00:00:00 2001 From: "Frank S. Thomas" Date: Mon, 15 Jan 2018 21:13:31 +0100 Subject: [PATCH 3/4] Show instances for GreaterEqual and LessEqual --- .../scala/eu/timepit/refined/api/Show.scala | 27 +++++++++---------- .../scala/eu/timepit/refined/numeric.scala | 24 ++++++++--------- 2 files changed, 24 insertions(+), 27 deletions(-) diff --git a/modules/core/shared/src/main/scala/eu/timepit/refined/api/Show.scala b/modules/core/shared/src/main/scala/eu/timepit/refined/api/Show.scala index 6ba1397fb..e01501a6b 100644 --- a/modules/core/shared/src/main/scala/eu/timepit/refined/api/Show.scala +++ b/modules/core/shared/src/main/scala/eu/timepit/refined/api/Show.scala @@ -1,22 +1,17 @@ package eu.timepit.refined.api -trait Show[T, P] { +import eu.timepit.refined.internal.Resources + +trait Show[T, P] extends Serializable { type R final type Res = Result[R] - // t: Int = 1 => "1" - def show(t: T): String - - // x > 0 - def withPlaceholder(x: String): String - - // x > 0 where x = 1 def showExpr(t: T): String - // Predicate failed: x > 0 where x = 1 - def showResult(t: T, r: Res): String + def showResult(t: T, r: Res): String = + Resources.predicateResultDetailDot(r, showExpr(t)) } object Show extends LowPriorityShowInstances { @@ -24,17 +19,21 @@ object Show extends LowPriorityShowInstances { type Aux[T, P, R0] = Show[T, P] { type R = R0 } def apply[T, P](implicit s: Show[T, P]): Aux[T, P, s.R] = s + + def instance[T, P, R0](showExprF: T => String): Aux[T, P, R0] = + new Show[T, P] { + override type R = R0 + + override def showExpr(t: T): String = showExprF(t) + } } trait LowPriorityShowInstances { + implicit def showFromValidate[T, P, R0](implicit v: Validate.Aux[T, P, R0]): Show.Aux[T, P, R0] = new Show[T, P] { override type R = R0 - override def show(t: T): String = t.toString - - override def withPlaceholder(x: String): String = "" - override def showExpr(t: T): String = v.showExpr(t) override def showResult(t: T, r: Res): String = v.showResult(t, r) diff --git a/modules/core/shared/src/main/scala/eu/timepit/refined/numeric.scala b/modules/core/shared/src/main/scala/eu/timepit/refined/numeric.scala index 41d350ef5..225a1c4e4 100644 --- a/modules/core/shared/src/main/scala/eu/timepit/refined/numeric.scala +++ b/modules/core/shared/src/main/scala/eu/timepit/refined/numeric.scala @@ -29,7 +29,7 @@ import shapeless.ops.nat.ToInt * * Note: `[[generic.Equal]]` can also be used for numeric types. */ -object numeric extends NumericValidate with NumericInference { +object numeric extends NumericValidate with NumericShow with NumericInference { /** Predicate that checks if a numeric value is less than `N`. */ final case class Less[N](n: N) @@ -161,21 +161,19 @@ private[refined] trait NumericValidate { t => s"($t % ${tn()} == ${to()})", Modulo(wn.value, wo.value) ) +} - implicit def greaterEqualShowWit[N <: Nat, T]( - implicit nt: Numeric[T]): Show.Aux[T, GreaterEqual[N], Not[Result[Less[N]]]] = - new Show[T, GreaterEqual[N]] { - override type R = Not[Result[Less[N]]] - - override def show(t: T): String = "" - - override def withPlaceholder(x: String): String = "" +private[refined] trait NumericShow { - override def showExpr(t: T): String = "" + implicit def lessEqualShowNat[N <: Nat, T]( + implicit tn: ToInt[N] + ): Show.Aux[T, LessEqual[N], Not[Result[Greater[N]]]] = + Show.instance(t => s"($t <= ${tn()})") - override def showResult(t: T, r: Res): String = - s"Predicate $t blabla failed" - } + implicit def greaterEqualShowNat[N <: Nat, T]( + implicit tn: ToInt[N] + ): Show.Aux[T, GreaterEqual[N], Not[Result[Less[N]]]] = + Show.instance(t => s"($t >= ${tn()})") } private[refined] trait NumericInference { From a6a173b5dd867911ae55e0f54d7eb10910d80833 Mon Sep 17 00:00:00 2001 From: "Frank S. Thomas" Date: Mon, 15 Jan 2018 21:26:34 +0100 Subject: [PATCH 4/4] Add Show[String, NonEmpty] instance --- .../scala/eu/timepit/refined/string.scala | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/modules/core/shared/src/main/scala/eu/timepit/refined/string.scala b/modules/core/shared/src/main/scala/eu/timepit/refined/string.scala index d4baa7a32..d7f72338a 100644 --- a/modules/core/shared/src/main/scala/eu/timepit/refined/string.scala +++ b/modules/core/shared/src/main/scala/eu/timepit/refined/string.scala @@ -1,7 +1,9 @@ package eu.timepit.refined -import eu.timepit.refined.api.{Inference, Validate} +import eu.timepit.refined.api.{Inference, Result, Show, Validate} import eu.timepit.refined.api.Inference.==> +import eu.timepit.refined.boolean.Not +import eu.timepit.refined.collection.{Empty, NonEmpty} import eu.timepit.refined.string._ import shapeless.Witness @@ -10,7 +12,7 @@ import shapeless.Witness * in `[[collection]]` also work for `String`s by treating them as sequences * of `Char`s. */ -object string extends StringValidate with StringInference { +object string extends StringValidate with StringShow with StringInference { /** Predicate that checks if a `String` ends with the suffix `S`. */ final case class EndsWith[S](s: S) @@ -132,6 +134,19 @@ private[refined] trait StringValidate { .fromPartial(javax.xml.xpath.XPathFactory.newInstance().newXPath().compile, "XPath", XPath()) } +private[refined] trait StringShow { + + implicit def nonEmptyShow: Show.Aux[String, NonEmpty, Not[Result[Empty]]] = + new Show[String, NonEmpty] { + override type R = Not[Result[Empty]] + + override def showExpr(t: String): String = s"isEmpty($t)" + + override def showResult(t: String, r: Res): String = + "Cannot create a NonEmptyString with the empty string" + } +} + private[refined] trait StringInference { implicit def endsWithInference[A <: String, B <: String](