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

teststate.core.checks.scala Maven / Gradle / Ivy

The newest version!
package teststate.core

import acyclic.file
import teststate.data._
import teststate.typeclass._
import teststate.{core => ^}
import Conditional.Implicits._
import NamedOps.ToOps._
import Profunctor.ToOps._
import Display.ToOps._

final case class Point[-I, E](name: NameFn[I], test: I => Tri[E, Unit])

object Point {
  implicit val pointInstance: Profunctor[Point] =
    new Profunctor[Point] {
      override def dimap[A, B, C, D](p: Point[A, B])(g: C => A, f: B => D): Point[C, D] =
        Point(p.name cmap g, c => p.test(g(c)) mapE f)
    }

  implicit def pointInstanceConditional[I, E]: Conditional[Point[I, E], I] =
    Conditional((p, f) => Point(p.name, p.test when f))

  implicit def pointInstanceNamedOps[I, E]: NamedOps[Point[I, E], I] =
    NamedOps((p, f) => p.copy(name = f(p.name)))

  implicit def pointInstanceDisplay[I, E]: Display[Point[I, E]] =
    Display(_.name(None).value)
}

// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

sealed abstract class Around[-I, E]
object Around {

  sealed abstract class When
  case object Before extends When
  case object After  extends When

  final case class Point[-I, E](point: ^.Point[I, E], when: When) extends Around[I, E]

  // This is a wrapper around DeltaA because when DeltaA extends Around directly, scalac bugs cause it to sometimes be
  // omitted from pattern-matching exhaustiveness analysis.
  final case class Delta[-I, E](delta: DeltaA[I, E]) extends Around[I, E]

  type DeltaAux[-I, E, AA] = DeltaA[I, E] {type A = AA}

  sealed abstract class DeltaA[-I, E] {
    type A
    val name: NameFn[BeforeAfter[I]]
    val before: I => Tri[E, A]
    val test: (I, A) => Option[E]

    final def aux: DeltaAux[I, E, A] = this
  }

  def DeltaA[I, E, AA](_name: NameFn[BeforeAfter[I]], _before: I => Tri[E, AA], _test: (I, AA) => Option[E]): DeltaAux[I, E, AA] =
    new DeltaA[I, E] {
      override type A     = AA
      override val name   = _name
      override val before = _before
      override val test   = _test
    }

  implicit val deltaAInstance: Profunctor[DeltaA] =
    new Profunctor[DeltaA] {
      override def dimap[A, B, C, D](d: DeltaA[A, B])(g: C => A, f: B => D): DeltaA[C, D] = {
        val b = d.before
        val t = d.test
        DeltaA[C, D, d.A](
          d.name cmap (_ map g),
          c => b(g(c)) mapE f,
          (c, a) => t(g(c), a) map f)
      }
    }

  implicit def deltaAInstanceConditional[I, E]: Conditional[DeltaA[I, E], I] =
    Conditional((d, f) => DeltaA(d.name, d.before when f, d.test))

  implicit def deltaAInstanceNamedOps[I, E]: NamedOps[DeltaA[I, E], BeforeAfter[I]] =
    NamedOps((d, f) => DeltaA(f(d.name), d.before, d.test))

  implicit def deltaAInstanceDisplay[I, E]: Display[DeltaA[I, E]] =
    Display(_.name(None).value)

  implicit val aroundInstance: Profunctor[Around] =
    new Profunctor[Around] {
      override def dimap[A, B, C, D](around: Around[A, B])(g: C => A, f: B => D): Around[C, D] =
        around match {
          case Point(p, w) => Point(p.dimap(g, f), w)
          case Delta(d)    => Delta(d.dimap(g, f))
        }
    }

  implicit def aroundInstanceConditional[I, E]: Conditional[Around[I, E], I] =
    Conditional((m, f) => m match {
      case Point(p, w) => Point(p when f, w)
      case Delta(d)    => Delta(d when f)
    })

  implicit def aroundInstanceNamedOps[I, E]: NamedOps[Around[I, E], BeforeAfter[I]] =
    NamedOps((a, f) => a match {
      case Point(p, Before) => Point(p renameBy f.thruBefore, Before)
      case Point(p, After ) => Point(p renameBy f.thruAfter , After)
      case Delta(d)         => Delta(d renameBy f)
    })

  implicit def aroundInstanceDisplay[I, E]: Display[Around[I, E]] =
    Display {
      case Point(p, _) => p.display
      case Delta(d)    => d.display
    }
}

// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

sealed abstract class Invariant[-I, E]
object Invariant {
  final case class Point[-I, E](point: ^.Point        [I, E]) extends Invariant[I, E]
  final case class Delta[-I, E](delta: ^.Around.DeltaA[I, E]) extends Invariant[I, E]

  implicit val invariantInstanceProfunctor: Profunctor[Invariant] =
    new Profunctor[Invariant] {
      override def dimap[A, B, C, D](a: Invariant[A, B])(g: C => A, f: B => D): Invariant[C, D] =
        a match {
          case Point(x) => Point(x.dimap(g, f))
          case Delta(x) => Delta(x.dimap(g, f))
        }
    }

  implicit def invariantInstanceConditional[I, E]: Conditional[Invariant[I, E], I] =
    Conditional((m, f) => m match {
      case Point(x) => Point(x when f)
      case Delta(x) => Delta(x when f)
    })

  implicit def invariantInstanceNamedOps[I, E]: NamedOps[Invariant[I, E], I] =
    NamedOps((i, f) => i match {
      case Point(x) => Point(x renameBy f)
      case Delta(x) =>
        // Hmmmm......
        Delta(x renameBy (n => f(n.cmap(BeforeAfter.same)).cmap(_.before)))
    })

  implicit def invariantInstanceDisplay[I, E]: Display[Invariant[I, E]] =
    Display {
      case Point(x) => x.display
      case Delta(x) => x.display
    }
}





© 2015 - 2025 Weber Informatics LLC | Privacy Policy