All Downloads are FREE. Search and download functionalities are using the official Maven repository.

teststate.dsl.OptionAssertions.scala Maven / Gradle / Ivy

There is a newer version: 3.1.0
Show newest version
package teststate.dsl

import japgolly.microlibs.name_fn.Name.Implicits._
import japgolly.microlibs.name_fn._
import teststate.typeclass._

object OptionAssertions {

  sealed abstract class Contains {
    def name(subject: => String, queryName: => String): Name
    def apply[A, B](source: Option[A], query: B)(implicit ev: A <:< B, eb: Equal[B], sb: Display[B]): Option[Contains.Failure[B]]
    protected final def found[A, B](source: Option[A], query: B)(implicit ev: A <:< B, eb: Equal[B]) =
      source.exists(a => eb.equal(query, a))
  }

  object Contains {
    def name(expect: Boolean, subject: => String, queryName: => String): Name =
      Name.lazily(apply(expect).name(subject, queryName))

    def apply(positive: Boolean): Contains =
      if (positive) Pos else Neg

    object Pos extends Contains {
      override def name(subject: => String, queryName: => String): Name =
        s"$subject should contain $queryName."
      override def apply[A, B](source: Option[A], query: B)(implicit ev: A <:< B, eb: Equal[B], sb: Display[B]): Option[Missing[B]] =
        if (found(source, query))
          None
        else
          Some(Missing(source.map(ev), query))
    }

    object Neg extends Contains {
      override def name(subject: => String, queryName: => String): Name =
        s"$subject shouldn't contain $queryName."
      override def apply[A, B](source: Option[A], query: B)(implicit ev: A <:< B, eb: Equal[B], sb: Display[B]): Option[Present[B]] =
        if (found(source, query))
          Some(Present(query))
        else
          None
    }

    sealed trait Failure[+A] extends HasErrorString with Product with Serializable
    final case class Missing[A](source: Option[A], query: A)(implicit d: Display[A]) extends Failure[A] {
      override def errorString =
        source match {
          case None => s"Option was empty, expected to contain ${d(query)}."
          case Some(a) => s"Contained ${d(a)}, expected ${d(query)}."
        }
    }
    final case class Present[A](query: A)(implicit d: Display[A]) extends Failure[Nothing] {
      override def errorString = s"Shouldn't contain ${d(query)}."
    }
  }

  // ===================================================================================================================

  sealed abstract class Forall {
    def name(source: => String, criteria: => String): Name
    def apply[A](as: Option[A])(f: A => Boolean)(implicit d: Display[A]): Option[Forall.Failure[A]]
  }

  object Forall {
    def apply(positive: Boolean): Forall =
      if (positive) Pos else Neg

    object Pos extends Forall {
      override def name(source: => String, criteria: => String): Name =
        s"$source should be empty or $criteria."
      override def apply[A](oa: Option[A])(f: A => Boolean)(implicit d: Display[A]): Option[Contained[A]] =
        oa match {
          case None    => None
          case Some(a) => if (f(a)) None else Some(Contained(a))
        }
    }

    object Neg extends Forall {
      override def name(source: => String, criteria: => String): Name =
        s"$source should be defined and not $criteria."
      override def apply[A](oa: Option[A])(f: A => Boolean)(implicit d: Display[A]): Option[Failure[A]] =
        oa match {
          case Some(a) => if (f(a)) Some(Contained(a)) else None
          case None    => Some(Empty)
        }
    }

    sealed trait Failure[+A] extends HasErrorString with Product with Serializable
    final case class Contained[+A](value: A)(implicit d: Display[A]) extends Failure[A] {
      override def errorString = s"Contained ${d(value)}."
    }
    case object Empty extends Failure[Nothing] {
      override def errorString = "Option was empty."
    }
  }

  // ===================================================================================================================

  sealed abstract class Exists {
    def name(source: => String, criteria: => String): Name
    def apply[A](as: Option[A])(f: A => Boolean)(implicit d: Display[A]): Option[Exists.Failure[A]]
  }

  object Exists {
    def apply(positive: Boolean): Exists =
      if (positive) Pos else Neg

    import Forall.{Contained, Empty}
    type Failure[+A] = Forall.Failure[A]

    object Pos extends Exists {
      override def name(source: => String, criteria: => String): Name =
        s"$source should be defined and $criteria."
      override def apply[A](oa: Option[A])(f: A => Boolean)(implicit d: Display[A]): Option[Failure[A]] =
        oa match {
          case None    => Some(Empty)
          case Some(a) => if (f(a)) None else Some(Contained(a))
        }
    }

    object Neg extends Exists {
      override def name(source: => String, criteria: => String): Name =
        s"$source should be empty or not $criteria."
      override def apply[A](oa: Option[A])(f: A => Boolean)(implicit d: Display[A]): Option[Contained[A]] =
        oa match {
          case Some(a) => if (f(a)) Some(Contained(a)) else None
          case None    => None
        }
    }
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy