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

morphling.scalacheck.ToGen.scala Maven / Gradle / Ivy

The newest version!
package morphling.scalacheck

import cats.*
import cats.data.EitherK
import cats.free.*
import morphling.*
import morphling.Schema.Schema
import morphling.annotated.Schema.AnnotatedSchema
import mouse.option.*
import org.scalacheck.Gen
import simulacrum_.typeclass

@typeclass
trait ToGen[S[_]] {
  def toGen: S ~> Gen
}

object ToGen {
  implicit class ToGenOps[S[_], A](s: S[A]) {
    def gen(implicit TG: ToGen[S]): Gen[A] = TG.toGen(s)
  }

  implicit def schemaToGen[P[_]: ToGen]: ToGen[Schema[P, *]] = new ToGen[Schema[P, *]] {
    override val toGen: Schema[P, *] ~> Gen = new (Schema[P, *] ~> Gen) {
      override def apply[I](schema: Schema[P, I]): Gen[I] =
        HFix.cataNT[SchemaF[P, *[_], *], Gen](genAlg).apply(schema)
    }
  }

  implicit def annSchemaToGen[P[_]: ToGen, A[_]: *[_] ~> λ[T => Endo[Gen[T]]]]: ToGen[AnnotatedSchema[P, A, *]] =
    new ToGen[AnnotatedSchema[P, A, *]] {
      override val toGen: AnnotatedSchema[P, A, *] ~> Gen = new (AnnotatedSchema[P, A, *] ~> Gen) {
        override def apply[I](schema: AnnotatedSchema[P, A, I]): Gen[I] =
          HFix.cataNT[HEnvT[A, SchemaF[P, *[_], *], *[_], *], Gen](annGenAlg).apply(schema)
      }
    }

  def genAlg[P[_]: ToGen]: HAlgebra[SchemaF[P, *[_], *], Gen] =
    new HAlgebra[SchemaF[P, *[_], *], Gen] {
      def apply[I](schema: SchemaF[P, Gen, I]): Gen[I] = schema match {
        case s: PrimSchema[P, Gen, I] => ToGen[P].toGen(s.prim)
        case s: OneOfSchema[P, Gen, I] =>
          val altGens = s.alts.map { case Alt(_, b, p) => b.map(p.upcast) }
          altGens.tail.headOption.cata(
            th => Gen.oneOf(altGens.head, th, altGens.tail.tail*),
            altGens.head
          )

        case s: RecordSchema[P, Gen, I]  => recordGen[P, I](s.props)
        case s: IsoSchema[P, Gen, i0, I] => s.base.map(s.eqv.get(_))
      }
    }

  def annGenAlg[P[_]: ToGen, Ann[_]](implicit
      interpret: Ann ~> λ[T => Endo[Gen[T]]]
  ): HAlgebra[HEnvT[Ann, SchemaF[P, *[_], *], *[_], *], Gen] =
    new HAlgebra[HEnvT[Ann, SchemaF[P, *[_], *], *[_], *], Gen] {
      override def apply[I](schema: HEnvT[Ann, SchemaF[P, *[_], *], Gen, I]): Gen[I] =
        interpret.apply(schema.ask).apply(genAlg[P].apply(schema.fa))
    }

  def recordGen[P[_]: ToGen, I](rb: FreeApplicative[PropSchema[I, Gen, *], I]): Gen[I] = {
    implicit val djap: Applicative[Gen] = new Applicative[Gen] {
      override def pure[T](x: T): Gen[T] = Gen.const(x)

      override def ap[T, U](ff: Gen[T => U])(fa: Gen[T]): Gen[U] =
        fa.flatMap(a => ff.map(_(a)))
    }

    rb.foldMap(
      new (PropSchema[I, Gen, *] ~> Gen) {
        def apply[B](ps: PropSchema[I, Gen, B]): Gen[B] = ps match {
          case Required(_, base, _, _)  => base
          case opt: Optional[I, Gen, i] => Gen.option(opt.base)
          case Constant(_, value, _)    => Gen.const(value)
          case abs: Absent[I, Gen, i]   => Gen.const(Option.empty[i])
        }
      }
    )
  }

  implicit def eitherKGen[P[_]: ToGen, Q[_]: ToGen]: ToGen[EitherK[P, Q, *]] = new ToGen[EitherK[P, Q, *]] {
    override val toGen: EitherK[P, Q, *] ~> Gen = new (EitherK[P, Q, *] ~> Gen) {
      override def apply[A](fa: EitherK[P, Q, A]): Gen[A] = fa.run.fold(
        ToGen[P].toGen(_),
        ToGen[Q].toGen(_),
      )
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy