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

izumi.functional.bio.impl.SchedulerImpl.scala Maven / Gradle / Ivy

There is a newer version: 1.2.16
Show newest version
package izumi.functional.bio.impl

import izumi.functional.bio.Clock1.ClockAccuracy
import izumi.functional.bio.{Clock2, F, Temporal2}
import izumi.functional.bio.__VersionSpecificDurationConvertersCompat.toFiniteDuration
import izumi.functional.bio.retry.RetryPolicy.{ControllerDecision, RetryFunction}
import izumi.functional.bio.retry.{RetryPolicy, Scheduler2}

import java.time.ZonedDateTime
import java.time.temporal.ChronoUnit
import scala.concurrent.duration.FiniteDuration

open class SchedulerImpl[F[+_, +_]: Temporal2](implicit clock: Clock2[F]) extends Scheduler2[F] {

  override def repeat[E, A, B](eff: F[E, A])(policy: RetryPolicy[F, A, B]): F[E, A] = {
    eff.flatMap(out => loop(out, policy.action)(F.pure(_))(eff.flatMap))
  }

  override def retry[E, S, A](eff: F[E, A])(policy: RetryPolicy[F, E, S]): F[E, A] = {
    retryOrElse(eff)(policy)(F.fail(_))
  }

  override def retryOrElse[E, E2, S, A, A1 >: A](eff: F[E, A])(policy: RetryPolicy[F, E, S])(orElse: E => F[E2, A1]): F[E2, A1] = {
    eff.catchAll(err => loop(err, policy.action)(orElse)(eff.catchAll))
  }

  protected[this] def loop[E, S, A, B](
    in: A,
    makeDecision: RetryFunction[F, A, S],
  )(stopper: A => F[E, B]
  )(repeater: (A => F[E, B]) => F[E, B]
  ): F[E, B] = {
    (for {
      now <- F.clock.nowZoned(ClockAccuracy.MILLIS)
      dec <- makeDecision(now, in)
      next = dec match {
        case ControllerDecision.Repeat(_, interval, action) =>
          val sleepTime = toFiniteDuration(java.time.Duration.between(now, interval))
          F.sleep(sleepTime) *> repeater(loop(_, action)(stopper)(repeater))
        case ControllerDecision.Stop(_) =>
          stopper(in)
      }
    } yield next).flatten
  }

  override def retryOrElseUntil[E, A, E2](r: F[E, A])(duration: FiniteDuration, orElse: E => F[E2, A]): F[E2, A] = {
    def loop(maxTime: ZonedDateTime): F[E2, A] = {
      F.redeem[E, A, E2, A](r)(
        err = error =>
          F.flatMap[E2, ZonedDateTime, A](
            clock.nowZoned(ClockAccuracy.MILLIS)
          )(now => if (now.isBefore(maxTime)) loop(maxTime) else orElse(error)),
        succ = F.pure(_),
      )
    }

    F.flatMap[E2, ZonedDateTime, A](
      clock.nowZoned(ClockAccuracy.MILLIS)
    )(now => loop(maxTime = now.plus(duration.toMillis, ChronoUnit.MILLIS)))
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy