eu.timepit.refined.collection.scala Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of refined_sjs1_2.12 Show documentation
Show all versions of refined_sjs1_2.12 Show documentation
Simple refinement types for Scala
package eu.timepit.refined
import eu.timepit.refined.api.Inference.==>
import eu.timepit.refined.api.{Inference, Result, Validate}
import eu.timepit.refined.boolean.Not
import eu.timepit.refined.collection._
import eu.timepit.refined.generic.Equal
import eu.timepit.refined.internal.Resources
import eu.timepit.refined.numeric.{GreaterEqual, Interval}
import shapeless.Witness
import shapeless.nat.{_0, _1}
/** Module for collection predicates. */
object collection extends CollectionInference {
/**
* Predicate that counts the number of elements in an `Iterable`
* which satisfy the predicate `PA` and passes the result to the numeric
* predicate `PC`.
*/
final case class Count[PA, PC](pa: PA, pc: PC)
/** Predicate that checks if an `Iterable` is empty. */
final case class Empty()
/**
* Predicate that checks if the predicate `P` holds for all elements of an
* `Iterable`.
*/
final case class Forall[P](p: P)
/**
* Predicate that checks if the predicate `P` holds for the first element
* of an `Iterable`.
*/
final case class Head[P](p: P)
/**
* Predicate that checks if the predicate `P` holds for the element at
* index `N` of a sequence.
*/
final case class Index[N, P](n: N, p: P)
/**
* Predicate that checks if the predicate `P` holds for all but the last
* element of an `Iterable`.
*/
final case class Init[P](p: P)
/**
* Predicate that checks if the predicate `P` holds for the last element
* of an `Iterable`.
*/
final case class Last[P](p: P)
/**
* Predicate that checks if the size of an `Iterable` satisfies the
* predicate `P`.
*/
final case class Size[P](p: P)
/**
* Predicate that checks if the predicate `P` holds for all but the first
* element of an `Iterable`.
*/
final case class Tail[P](p: P)
/**
* Predicate that checks if an `Iterable` contains a value
* equal to `U`.
*/
type Contains[U] = Exists[Equal[U]]
/**
* Predicate that checks if the predicate `P` holds for some elements of an
* `Iterable`.
*/
type Exists[P] = Not[Forall[Not[P]]]
/**
* Predicate that checks if the size of an `Iterable` is greater than
* or equal to `N`.
*/
type MinSize[N] = Size[GreaterEqual[N]]
/**
* Predicate that checks if the size of an `Iterable` is less than
* or equal to `N`.
*/
type MaxSize[N] = Size[Interval.Closed[_0, N]]
/** Predicate that checks if an `Iterable` is not empty. */
type NonEmpty = Not[Empty]
object Count {
implicit def countValidate[A, PA, RA, PC, RC, T](implicit
va: Validate.Aux[A, PA, RA],
vc: Validate.Aux[Int, PC, RC],
ev: T => Iterable[A]
): Validate.Aux[T, Count[PA, PC], Count[List[va.Res], vc.Res]] =
new Validate[T, Count[PA, PC]] {
override type R = Count[List[va.Res], vc.Res]
override def validate(t: T): Res = {
val ra = ev(t).toList.map(va.validate)
val rc = vc.validate(ra.count(_.isPassed))
rc.as(Count(ra, rc))
}
override def showExpr(t: T): String =
vc.showExpr(count(t))
override def showResult(t: T, r: Res): String = {
val c = count(t)
val expr = ev(t).map(va.showExpr).mkString("count(", ", ", ")")
Resources.predicateTakingResultDetail(s"$expr = $c", r, vc.showResult(c, r.detail.pc))
}
private def count(t: T): Int =
ev(t).count(va.isValid)
}
}
object Empty {
implicit def emptyValidate[T](implicit ev: T => Iterable[_]): Validate.Plain[T, Empty] =
Validate.fromPredicate(t => ev(t).isEmpty, t => s"isEmpty($t)", Empty())
}
object Forall {
implicit def forallValidate[A, P, R, T[a] <: Iterable[a]](implicit
v: Validate.Aux[A, P, R]
): Validate.Aux[T[A], Forall[P], Forall[List[v.Res]]] =
new Validate[T[A], Forall[P]] {
override type R = Forall[List[v.Res]]
override def validate(t: T[A]): Res = {
val rt = t.toList.map(v.validate)
Result.fromBoolean(rt.forall(_.isPassed), Forall(rt))
}
override def showExpr(t: T[A]): String =
t.map(v.showExpr).mkString("(", " && ", ")")
}
implicit def forallValidateView[A, P, R, T](implicit
v: Validate.Aux[A, P, R],
ev: T => Iterable[A]
): Validate.Aux[T, Forall[P], Forall[List[v.Res]]] =
forallValidate[A, P, R, Iterable].contramap(ev)
}
object Head {
implicit def headValidate[A, P, R, T[a] <: Iterable[a]](implicit
v: Validate.Aux[A, P, R]
): Validate.Aux[T[A], Head[P], Head[Option[v.Res]]] =
new Validate[T[A], Head[P]] {
override type R = Head[Option[v.Res]]
override def validate(t: T[A]): Res = {
val ra = t.headOption.map(v.validate)
Result.fromBoolean(ra.fold(false)(_.isPassed), Head(ra))
}
override def showExpr(t: T[A]): String =
optElemShowExpr(t.headOption, v.showExpr)
override def showResult(t: T[A], r: Res): String =
optElemShowResult(t.headOption, r.detail.p, (a: A) => s"head($t) = $a", v.showResult)
}
implicit def headValidateView[A, P, R, T](implicit
v: Validate.Aux[A, P, R],
ev: T => Iterable[A]
): Validate.Aux[T, Head[P], Head[Option[v.Res]]] =
headValidate[A, P, R, Iterable].contramap(ev)
}
object Index {
implicit def indexValidate[A, P, R, N <: Int, T](implicit
v: Validate.Aux[A, P, R],
ev: T => PartialFunction[Int, A],
wn: Witness.Aux[N]
): Validate.Aux[T, Index[N, P], Index[N, Option[v.Res]]] =
new Validate[T, Index[N, P]] {
override type R = Index[N, Option[v.Res]]
override def validate(t: T): Res = {
val ra = ev(t).lift(wn.value).map(v.validate)
Result.fromBoolean(ra.fold(false)(_.isPassed), Index(wn.value, ra))
}
override def showExpr(t: T): String =
optElemShowExpr(ev(t).lift(wn.value), v.showExpr)
override def showResult(t: T, r: Res): String =
optElemShowResult(
ev(t).lift(wn.value),
r.detail.p,
(a: A) => s"index($t, ${wn.value}) = $a",
v.showResult
)
}
}
object Init {
implicit def initValidate[A, P, R, T[a] <: Iterable[a]](implicit
v: Validate.Aux[A, P, R]
): Validate.Aux[T[A], Init[P], Init[List[v.Res]]] =
new Validate[T[A], Init[P]] {
override type R = Init[List[v.Res]]
override def validate(t: T[A]): Res = {
val ra = t.toList.dropRight(1).map(v.validate)
Result.fromBoolean(ra.forall(_.isPassed), Init(ra))
}
override def showExpr(t: T[A]): String =
t.toList.dropRight(1).map(v.showExpr).mkString("(", " && ", ")")
}
implicit def initValidateView[A, P, R, T](implicit
v: Validate.Aux[A, P, R],
ev: T => Iterable[A]
): Validate.Aux[T, Init[P], Init[List[v.Res]]] =
initValidate[A, P, R, Iterable].contramap(ev)
}
object Last {
implicit def lastValidate[A, P, R, T[a] <: Iterable[a]](implicit
v: Validate.Aux[A, P, R]
): Validate.Aux[T[A], Last[P], Last[Option[v.Res]]] =
new Validate[T[A], Last[P]] {
override type R = Last[Option[v.Res]]
override def validate(t: T[A]): Res = {
val ra = t.lastOption.map(v.validate)
Result.fromBoolean(ra.fold(false)(_.isPassed), Last(ra))
}
override def showExpr(t: T[A]): String =
optElemShowExpr(t.lastOption, v.showExpr)
override def showResult(t: T[A], r: Res): String =
optElemShowResult(t.lastOption, r.detail.p, (a: A) => s"last($t) = $a", v.showResult)
}
implicit def lastValidateView[A, P, R, T](implicit
v: Validate.Aux[A, P, R],
ev: T => Iterable[A]
): Validate.Aux[T, Last[P], Last[Option[v.Res]]] =
lastValidate[A, P, R, Iterable].contramap(ev)
}
object Size {
implicit def sizeValidate[T, P, RP](implicit
v: Validate.Aux[Int, P, RP],
ev: T => Iterable[_]
): Validate.Aux[T, Size[P], Size[v.Res]] =
new Validate[T, Size[P]] {
override type R = Size[v.Res]
override def validate(t: T): Res = {
val r = v.validate(ev(t).size)
r.as(Size(r))
}
override def showExpr(t: T): String =
v.showExpr(ev(t).size)
override def showResult(t: T, r: Res): String = {
val size = ev(t).size
val nested = v.showResult(size, r.detail.p)
Resources.predicateTakingResultDetail(s"size($t) = $size", r, nested)
}
}
}
object Tail {
implicit def tailValidate[A, P, R, T[a] <: Iterable[a]](implicit
v: Validate.Aux[A, P, R]
): Validate.Aux[T[A], Tail[P], Tail[List[v.Res]]] =
new Validate[T[A], Tail[P]] {
override type R = Tail[List[v.Res]]
override def validate(t: T[A]): Res = {
val ra = t.toList.drop(1).map(v.validate)
Result.fromBoolean(ra.forall(_.isPassed), Tail(ra))
}
override def showExpr(t: T[A]): String =
t.toList.drop(1).map(v.showExpr).mkString("(", " && ", ")")
}
implicit def tailValidateView[A, P, R, T](implicit
v: Validate.Aux[A, P, R],
ev: T => Iterable[A]
): Validate.Aux[T, Tail[P], Tail[List[v.Res]]] =
tailValidate[A, P, R, Iterable].contramap(ev)
}
private def optElemShowExpr[A](elem: Option[A], f: A => String): String =
elem.fold(Resources.showExprEmptyCollection)(f)
private def optElemShowResult[A, R](
elem: Option[A],
res: Option[Result[R]],
f: A => String,
g: (A, Result[R]) => String
): String =
(elem, res) match {
case (Some(a), Some(r)) =>
Resources.predicateTakingResultDetail(f(a), r, g(a, r))
case _ => Resources.showResultEmptyCollection
}
}
private[refined] trait CollectionInference {
implicit def existsInference[A, B](implicit p1: A ==> B): Exists[A] ==> Exists[B] =
p1.adapt("existsInference(%s)")
implicit def existsNonEmptyInference[P]: Exists[P] ==> NonEmpty =
Inference.alwaysValid("existsNonEmptyInference")
implicit def headInference[A, B](implicit p1: A ==> B): Head[A] ==> Head[B] =
p1.adapt("headInference(%s)")
implicit def headExistsInference[P]: Head[P] ==> Exists[P] =
Inference.alwaysValid("headExistsInference")
implicit def indexInference[N, A, B](implicit p1: A ==> B): Index[N, A] ==> Index[N, B] =
p1.adapt("indexInference(%s)")
implicit def indexExistsInference[N, P]: Index[N, P] ==> Exists[P] =
Inference.alwaysValid("indexExistsInference")
implicit def lastInference[A, B](implicit p1: A ==> B): Last[A] ==> Last[B] =
p1.adapt("lastInference(%s)")
implicit def lastExistsInference[P]: Last[P] ==> Exists[P] =
Inference.alwaysValid("lastExistsInference")
implicit def sizeInference[A, B](implicit p1: A ==> B): Size[A] ==> Size[B] =
p1.adapt("sizeInference(%s)")
implicit def sizeGreaterEqual1NonEmptyInference[A](implicit
p1: A ==> GreaterEqual[_1]
): Size[A] ==> NonEmpty =
p1.adapt("sizeGreaterEqual1NonEmptyInference(%s)")
}