package com.nulabinc.backlog.r2b.interpreters import cats.free.Free import cats.~> import com.nulabinc.backlog.r2b.dsl.BacklogDSL.BacklogProgram import com.nulabinc.backlog.r2b.interpreters.AppDSL.AppProgram import com.nulabinc.backlog.r2b.interpreters.ConsoleDSL.ConsoleProgram import com.nulabinc.backlog.r2b.interpreters.backlog.Backlog4jInterpreter import monix.eval.Task sealed trait AppADT[+A] case class Pure[A](a: A) extends AppADT[A] case class Backlog[A](prg: BacklogProgram[A]) extends AppADT[A] case class Console[A](prg: ConsoleProgram[A]) extends AppADT[A] case class Exit(exitCode: Int) extends AppADT[Unit] object AppDSL { type AppProgram[A] = Free[AppADT, A] def pure[A](a: A): AppProgram[A] = Free.liftF(Pure(a)) def backlog[A](prg: BacklogProgram[A]): AppProgram[A] = Free.liftF[AppADT, A](Backlog(prg)) def console[A](prg: ConsoleProgram[A]): AppProgram[A] = Free.liftF(Console(prg)) def exit(reason: String, exitCode: Int): AppProgram[Unit] = { for { _ <- console(ConsoleDSL.print(reason)) _ <- Free.liftF(Exit(exitCode)) } yield () } } case class AppInterpreter(backlogInterpreter: Backlog4jInterpreter, consoleInterpreter: ConsoleInterpreter) extends (AppADT ~> Task) { def run[A](prg: AppProgram[A]): Task[A] = prg.foldMap(this) override def apply[A](fa: AppADT[A]): Task[A] = fa match { case Pure(a) => Task(a) case Backlog(prg) => backlogInterpreter.run(prg) case Console(prg) => prg.foldMap(consoleInterpreter) case Exit(statusCode) => sys.exit(statusCode) } }