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

korolev.Dux.scala Maven / Gradle / Ivy

The newest version!
package korolev

import java.util.concurrent.ConcurrentLinkedQueue
import scala.concurrent.{ExecutionContext, Future, Promise}

trait Dux[State] {
  def state: State
  def subscribe[U](f: State => U): Dux.Unsubscribe
  def onDestroy[U](f: () => U): Dux.Unsubscribe
  def destroy(): Unit
  def apply(action: Dux.Transition[State]): Future[Unit]
}

object Dux {

  type Unsubscribe = () => Unit
  type Transition[State] = PartialFunction[State, State]

  def apply[State](initialState: State)(implicit ec: ExecutionContext): Dux[State] = {

    new Dux[State] {

      val queue = new ConcurrentLinkedQueue[(Promise[Unit], Transition[State])]

      @volatile var _state = initialState
      @volatile var subscribers = List.empty[State => _]
      @volatile var onDestroyListeners = List.empty[() => _]
      @volatile var inProgress = false

      def apply(transition: Transition[State]): Future[Unit] = {
        def executeNext(): Unit = {
          val (promise, transition) = queue.poll()
          try {
            transition.lift(_state) match {
              case Some(newState) =>
                _state = newState
                subscribers.foreach(f => f(newState))
                promise.success(())
              case None =>
                promise.failure(new Exception("Transition don't fit this state"))
            }
          } catch {
            case e: Throwable => promise.failure(e)
          } finally {
            if (queue.isEmpty) inProgress = false
            else executeNext()
          }
        }
        val promise = Promise[Unit]()
        queue.add(promise -> transition)
        if (!inProgress) {
          inProgress = true
          executeNext()
        }
        promise.future
      }

      def destroy(): Unit = {
        for (listener <- onDestroyListeners)
          listener()
      }

      def state = _state

      def onDestroy[U](f: () => U): Unsubscribe = {
        onDestroyListeners = f :: onDestroyListeners
        () => onDestroyListeners = onDestroyListeners.filter(_ != f)
      }

      def subscribe[U](f: State => U): Dux.Unsubscribe = {
        subscribers = f :: subscribers
        () => subscribers = subscribers.filter(_ != f)
      }
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy