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

rescala.extra.reactor.ReactorBundle.scala Maven / Gradle / Ivy

There is a newer version: 0.35.1
Show newest version
package rescala.extra.reactor

import rescala.core.ReName
import rescala.interface.RescalaInterface

class ReactorBundle[Api <: RescalaInterface](val api: Api) {
  import api._
  class Reactor[T](
      initState: State[ReactorState[T]]
  ) extends Derived with ReadableMacro[T] {

    override type Value = ReactorState[T]

    override protected[rescala] def state: State[ReactorState[T]] = initState
    override protected[rescala] def name: ReName                  = "Custom Reactor"
    override def read(v: ReactorState[T]): T                      = v.currentValue
    override protected[rescala] def commit(base: Value): Value    = base

    /** called if any of the dependencies changed in the current update turn,
      * after all (known) dependencies are updated
      */
    override protected[rescala] def reevaluate(input: ReIn): Rout = {

      input.trackDependencies(Set())

      var progressedNextAction = false

      def processActions[A](currentState: ReactorState[T]): ReactorState[T] = {
        def setAction(value: T, remainingActions: List[ReactorAction[T]]): ReactorState[T] = {
          processActions(currentState.copy(currentValue = value, currentStage = Stage(remainingActions)))
        }

        def nextAction[E](event: Event[E], handler: E => Stage[T]): ReactorState[T] = {
          if (progressedNextAction) {
            return currentState
          }

          val eventValue = input.depend(event)
          eventValue match {
            case None => currentState
            case Some(value) =>
              progressedNextAction = true
              val stages = handler(value)
              processActions(currentState.copy(currentStage = stages))
          }
        }

        def loopAction(loopStage: Stage[T], initialStage: Stage[T]): ReactorState[T] = {
          val resultState = processActions(currentState.copy(currentStage = loopStage))
          if (resultState.currentStage.actions.isEmpty) {
            return processActions(resultState.copy(currentStage = initialStage))
          }

          resultState.copy(currentStage = Stage(List(ReactorAction.LoopAction(resultState.currentStage, initialStage))))
        }

        def untilAction[E](event: Event[E], body: Stage[T], interrupt: E => Stage[T]): ReactorState[T] = {
          val eventValue = input.depend(event)
          eventValue match {
            case None =>
              val resultState = processActions(currentState.copy(currentStage = body))
              resultState.copy(currentStage =
                Stage(List(ReactorAction.UntilAction(event, resultState.currentStage, interrupt)))
              )
            case Some(value) =>
              val stages = interrupt(value)
              processActions(currentState.copy(currentStage = stages))
          }
        }

        def modifyAction(modifier: T => T, currentValue: T, tail: List[ReactorAction[T]]): ReactorState[T] = {
          val modifiedValue = modifier(currentValue)
          setAction(modifiedValue, tail)
        }

        def readAction(builder: T => Stage[T], currentValue: T): ReactorState[T] = {
          val nextStage = builder(currentValue)
          processActions(currentState.copy(currentStage = nextStage))
        }

        currentState.currentStage.actions match {
          case Nil => currentState
          case ReactorAction.SetAction(v) :: tail =>
            setAction(v, tail)
          case ReactorAction.ModifyAction(modifier) :: tail =>
            modifyAction(modifier, currentState.currentValue, tail)
          case ReactorAction.NextAction(event, handler) :: _ =>
            nextAction(event, handler)
          case ReactorAction.ReadAction(builder) :: _ =>
            readAction(builder, currentState.currentValue)
          case ReactorAction.LoopAction(loopStage, initialStage) :: _ =>
            loopAction(loopStage, initialStage)
          case ReactorAction.UntilAction(event, body, interrupt) :: _ =>
            untilAction(event, body, interrupt)
        }
      }

      val resState = processActions(input.before)

      input.withValue(resState)
    }

    override def resource: ReadAs[T] = this

    def now(implicit scheduler: Scheduler): T = scheduler.singleReadValueOnce(this)
  }

  object Reactor {

    /** Creates a new Reactor, which steps through the reactor stages ones.
      *
      * @param initialValue The initial value of the Reactor.
      * @param initialStage The Stage defining the Reactors behaviour.
      * @tparam T The type of the Reactor value.
      * @return The created Reactor.
      */
    def once[T](
        initialValue: T
    )(initialStage: Stage[T]): Reactor[T] = {
      createReactor(initialValue, initialStage)
    }

    /** Creates a new Reactor, which starts from the beginning, once it's finished.
      *
      * @param initialValue The initial value of the Reactor.
      * @param initialStage The Stage defining the Reactors behaviour.
      * @tparam T The type of the Reactor value.
      * @return The created Reactor.
      */
    def loop[T](
        initialValue: T
    )(initialStage: Stage[T]): Reactor[T] = {
      val loopingStage = initialStage.copy(List(ReactorAction.LoopAction(initialStage, initialStage)))
      createReactor(initialValue, loopingStage)
    }

    private def createReactor[T](initialValue: T, initialStage: Stage[T])(implicit ct: CreationTicket): Reactor[T] = {
      ct.create(
        Set(),
        new ReactorState[T](initialValue, initialStage),
        needsReevaluation = true
      ) { (createdState: State[ReactorState[T]]) =>
        new Reactor[T](createdState)
      }
    }
  }

  sealed trait ReactorAction[T]

  object ReactorAction {
    case class SetAction[T](res: T) extends ReactorAction[T]

    case class ModifyAction[T](modifier: T => T) extends ReactorAction[T]

    case class NextAction[T, E](event: Event[E], handler: E => Stage[T])
        extends ReactorAction[T]

    case class ReadAction[T](stageBuilder: T => Stage[T]) extends ReactorAction[T]

    case class LoopAction[T](currentStage: Stage[T], initialStage: Stage[T]) extends ReactorAction[T]

    case class UntilAction[T, E](event: Event[E], body: Stage[T], interrupt: E => Stage[T])
        extends ReactorAction[T]
  }

  case class ReactorState[T](currentValue: T, currentStage: Stage[T])

  case class Stage[T](actions: List[ReactorAction[T]] = Nil) {

    private def addAction(newValue: ReactorAction[T]): Stage[T] = {
      copy(actions = actions :+ newValue)
    }

    /** Sets the value of the Reactor.
      *
      * @param newValue The new value of the Reactor.
      * @return A StageBuilder describing the Reactor behaviour.
      */
    def set(newValue: T): Stage[T] = {
      addAction(ReactorAction.SetAction(newValue))
    }

    /** Modifies the value of the Reactor.
      *
      * @param modifier A function that has the old Reactor value as input and returns a new Reactor value.
      * @return A StageBuilder describing the Reactor behaviour.
      */
    def modify(modifier: T => T): Stage[T] = {
      addAction(ReactorAction.ModifyAction(modifier))
    }

    /** Waits until the event is triggered.
      *
      * When the event is triggered the given body is executed in the
      * same transaction.
      *
      * @param event the event to wait for.
      * @param body  the code to execute when the event is triggered.
      * @tparam E the event's type.
      */
    def next[E](event: Event[E])(body: E => Stage[T]): Stage[T] = {
      addAction(ReactorAction.NextAction(event, body))
    }

    /** Waits until the event is triggered.
      *
      * When the event is triggered the given body is executed in the
      * same transaction.
      *
      * @param event the event to wait for.
      * @param body  the code to execute when the event is triggered.
      */
    def next(event: Event[Unit])(body: => Stage[T]): Stage[T] = {
      addAction(ReactorAction.NextAction(event, (_: Unit) => body))
    }

    /** Reads the current reactor value.
      *
      * Executes the body with the current reactor value
      * and expects another [[Stage]] as result.
      *
      * A usage example could be returning different [[Stage]]s
      * depending on the event value.
      *
      * @param body The function building the resulting [[Stage]]
      */
    def read(body: T => Stage[T]): Stage[T] = {
      addAction(ReactorAction.ReadAction(body))
    }

    /** Executes the body in a loop.
      *
      * @param body The [[Stage]] to be executes repeatedly
      */
    def loop(body: => Stage[T]): Stage[T] = {
      addAction(ReactorAction.LoopAction(body, body))
    }

    /** Executes it's body until an event is fired.
      *
      * Until executes the body until the given event is fired.
      * When the event is fired, until executes the interruptHandler.
      *
      * @param event            The event indicating the interrupt.
      * @param body             The [[Stage]] to be executes by default.
      * @param interruptHandler A function taking the interrupt event's value
      *                         and returning a [[Stage]].
      *                         It is executed when the interrupt is fired.
      * @tparam E The type of the event value.
      */
    def until[E](event: Event[E], body: => Stage[T], interruptHandler: E => Stage[T]): Stage[T] = {
      addAction(ReactorAction.UntilAction(event, body, interruptHandler))
    }

    /** Executes it's body until an event is fired.
      *
      * Until executes the body until the given event is fired.
      * When the event is fired, until executes the interruptHandler.
      *
      * @param event            The event indicating the interrupt.
      * @param body             The [[Stage]] to be executes by default.
      * @param interruptHandler A function taking the interrupt event's value
      *                         and returning a [[Stage]].
      *                         It is executed when the interrupt is fired.
      */
    def until(event: Event[Unit], body: => Stage[T], interruptHandler: Stage[T]): Stage[T] = {
      val handler = { (_: Unit) => interruptHandler }
      addAction(ReactorAction.UntilAction(event, body, handler))
    }

    /** Executes it's body until an event is fired.
      *
      * Until executes the body until the given event is fired.
      *
      * @param event The event indicating the interrupt.
      * @param body  The [[Stage]] to be executes by default.
      * @tparam E The type of the event value.
      */
    def until(event: Event[Any], body: => Stage[T]): Stage[T] = {
      val interrupHandler = { (_: Any) => Stage[T]() }
      addAction(ReactorAction.UntilAction(event, body, interrupHandler))
    }
  }

  object S {
    def set[T](newValue: T): Stage[T] = {
      Stage().set(newValue)
    }

    def modify[T](modifier: T => T): Stage[T] = {
      Stage().modify(modifier)
    }

    def next[T, E](event: Event[E])(body: E => Stage[T]): Stage[T] = {
      Stage().next(event)(body)
    }

    def next[T](event: Event[Unit])(body: => Stage[T]): Stage[T] = {
      Stage().next(event)(body)
    }

    def read[T](body: T => Stage[T]): Stage[T] = {
      Stage().read(body)
    }

    def loop[T](body: => Stage[T]): Stage[T] = {
      Stage().loop(body)
    }

    def until[T, E](event: Event[E], body: => Stage[T], interruptHandler: E => Stage[T]): Stage[T] = {
      Stage().until(event, body, interruptHandler)
    }

    def until[T](event: Event[Unit], body: => Stage[T], interruptHandler: Stage[T]): Stage[T] = {
      Stage().until(event, body, interruptHandler)
    }

    def until[T](event: Event[Any], body: => Stage[T]): Stage[T] = {
      Stage().until(event, body)
    }

    def end[T]: Stage[T] = {
      Stage()
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy