Skip to content

Instantly share code, notes, and snippets.

@missingfaktor
Last active July 21, 2018 23:27
Show Gist options
  • Select an option

  • Save missingfaktor/d6acbd4eeb4f120d989240a2c96701a0 to your computer and use it in GitHub Desktop.

Select an option

Save missingfaktor/d6acbd4eeb4f120d989240a2c96701a0 to your computer and use it in GitHub Desktop.
import shapeless.Lub
sealed trait EarlyExitableEither[+F, +D, +C] extends Product with Serializable {
import EarlyExitableEither._
final def map[C1](f: C => C1): EarlyExitableEither[F, D, C1] = {
this match {
case Continue(value) => Continue(f(value))
case Done(value) => Done(value)
case Fail(error) => Fail(error)
}
}
final def flatMap[C1, D1 >: D, F1 >: F](f: C => EarlyExitableEither[F1, D1, C1]): EarlyExitableEither[F1, D1, C1] = {
this match {
case Continue(value) => f(value)
case Done(value) => Done(value)
case Fail(error) => Fail(error)
}
}
final def toEither[V](implicit lub: Lub[C, D, V]): Either[F, V] = {
this match {
case Continue(value) => Right(lub.left(value))
case Done(value) => Right(lub.right(value))
case Fail(error) => Left(error)
}
}
final def orDone[D1 >: D](doneValue: => D1): EarlyExitableEither[Nothing, D1, C] = {
this match {
case Continue(value) => Continue(value)
case Done(value) => Done(value)
case Fail(_) => Done(doneValue)
}
}
}
object EarlyExitableEither {
private final case class Continue[+C](value: C) extends EarlyExitableEither[Nothing, Nothing, C]
private final case class Done[+D](value: D) extends EarlyExitableEither[Nothing, D, Nothing]
private final case class Fail[+F](error: F) extends EarlyExitableEither[F, Nothing, Nothing]
def continue: EarlyExitableEither[Nothing, Nothing, Unit] = Continue(())
def continue[C](value: C): EarlyExitableEither[Nothing, Nothing, C] = Continue(value)
def done: EarlyExitableEither[Nothing, Unit, Nothing] = Done(())
def done[D](value: D): EarlyExitableEither[Nothing, D, Nothing] = Done(value)
def fail[F](error: F): EarlyExitableEither[F, Nothing, Nothing] = Fail(error)
def from[F, C](either: Either[F, C]): EarlyExitableEither[F, Nothing, C] = either.fold(Fail(_), Continue(_))
def doneIf[D1](condition: Boolean, value: => D1): EarlyExitableEither[Nothing, D1, Unit] = {
if (condition) {
Done(value)
} else {
Continue(())
}
}
}
sealed trait AutomationRecommendations extends Product with Serializable
object AutomationRecommendations {
final case class WithLabels(
automation: Map[LanguageExternalLabel, List[TemplateRecommendation]]
) extends AutomationRecommendations
final case class WithLanguages(
automation: Map[DGStandardizedLanguage, List[TemplateRecommendation]]
) extends AutomationRecommendations
case object Empty extends AutomationRecommendations
def empty: AutomationRecommendations = Empty
implicit val decoder: Decoder[AutomationRecommendations] = Decoder.instance { c =>
val E3 = EarlyExitableEither
val result: EarlyExitableEither[DecodingFailure, AutomationRecommendations, AutomationRecommendations] = {
for {
_ <- E3.doneIf(c.focus.forall(_.isNull), empty)
automation <- E3.from(c.downField("automation").as[Json]).orDone(empty)
obj <- E3.from(automation.as[JsonObject])
_ <- E3.doneIf(obj.isEmpty, empty)
recommendations <- E3.from {
automation.as[Map[DGStandardizedLanguage, List[TemplateRecommendation]]].map(WithLanguages) orElse
automation.as[Map[LanguageExternalLabel, List[TemplateRecommendation]]].map(WithLabels)
}
} yield recommendations
}
result.toEither
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment