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

monifu.reactive.internals.package.scala Maven / Gradle / Ivy

There is a newer version: 1.2
Show newest version
/*
 * Copyright (c) 2014-2015 by its authors. Some rights reserved.
 * See the project homepage at: http://www.monifu.org
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package monifu.reactive

import language.implicitConversions
import monifu.concurrent.{Cancelable, Scheduler}
import monifu.reactive.Ack.{Cancel, Continue}
import scala.concurrent.{ExecutionContext, Future, Promise}
import scala.util.control.NonFatal
import scala.util.{Success, Failure, Try}


package object internals {
  /**
   * Internal extensions to Future[Ack] used in the implementation of Observable.
   */
  private[monifu] implicit class FutureAckExtensions(val source: Future[Ack]) extends AnyVal {
    /**
     * If the result of this `Future` is a `Continue`, then completes
     * the stream with an `onComplete`.
     *
     * Note that it doesn't do exception handling - if the future is a failure
     * then the behavior is undefined.
     */
    def onContinueSignalComplete[T](observer: Observer[T])(implicit s: Scheduler): Unit =
      source match {
        case sync if sync.isCompleted =>
          sync.value.get match {
            case Continue.IsSuccess =>
              observer.onComplete()
            case _ =>
              () // do nothing
          }

        case async =>
          async.onComplete {
            case Continue.IsSuccess =>
              observer.onComplete()
            case _ =>
              () // do nothing
          }
      }

    /**
     * If the result of this `Future` is a `Continue`, then completes
     * the stream with an `onError`.
     *
     * Note that it doesn't do exception handling - if the future is a failure
     * then the behavior is undefined.
     */
    def onContinueSignalError[T](observer: Observer[T], ex: Throwable)(implicit s: Scheduler): Unit =
      source match {
        case sync if sync.isCompleted =>
          sync.value.get match {
            case Continue.IsSuccess =>
              observer.onError(ex)
            case _ =>
              () // do nothing
          }
        case async =>
          async.onComplete {
            case Continue.IsSuccess =>
              observer.onError(ex)
            case _ =>
              () // do nothing
          }
      }

    /**
     * On Continue, triggers the execution of the given callback.
     */
    def onContinue(callback: => Unit)(implicit s: Scheduler): Unit =
      source match {
        case sync if sync.isCompleted =>
          if (sync == Continue || (sync != Cancel && sync.value.get == Continue.IsSuccess))
            try callback catch {
              case NonFatal(ex) =>
                s.reportFailure(ex)
            }

        case async =>
          async.onComplete {
            case Continue.IsSuccess =>
              try callback catch {
                case NonFatal(ex) =>
                  s.reportFailure(ex)
              }
            case _ =>
              () // do nothing
          }
      }

    /**
     * On Continue, triggers the execution of the given runnable.
     */
    def onContinue(r: Runnable)(implicit s: Scheduler): Unit = {
      source match {
        case sync if sync.isCompleted =>
          sync.value.get match {
            case Continue.IsSuccess =>
              try r.run() catch {
                case NonFatal(ex) =>
                  s.reportFailure(ex)
              }
            case _ =>
              () // do nothing
          }

        case async =>
          async.onComplete {
            case Continue.IsSuccess =>
              try r.run() catch {
                case NonFatal(ex) =>
                  s.reportFailure(ex)
              }
            case _ =>
              () // do nothing
          }
      }
    }

    /**
     * An implementation of `flatMap` that executes
     * synchronously if the source is already completed.
     */
    def fastFlatMap[R](f: Ack => Future[R])(implicit ec: ExecutionContext): Future[R] =
      source.value match {
        case Some(sync) =>
          sync match {
            case Success(ack) =>
              try f(ack) catch {
                case NonFatal(ex) =>
                  Future.failed(ex)
              }
            case Failure(_) =>
              source.asInstanceOf[Future[R]]
          }
        case None =>
          source.flatMap(f)
      }

    /**
     * Helper that triggers [[Cancelable.cancel]] on the given reference,
     * in case our source is a [[Cancel]] or a failure.
     */
    def ifCanceledDoCancel(cancelable: Cancelable)(implicit s: Scheduler): Future[Ack] = {
      source match {
        case sync if sync.isCompleted =>
          sync.value.get match {
            case Cancel.IsSuccess | Failure(_) =>
              try {
                cancelable.cancel()
                sync
              }
              catch {
                case NonFatal(ex) =>
                  Future.failed(ex)
              }

            case _ =>
              sync
          }

        case async =>
          val p = Promise[Ack]()
          async.onComplete {
            case result @ (Cancel.IsSuccess | Failure(_)) =>
              try {
                cancelable.cancel()
                p.complete(result)
              }
              catch {
                case NonFatal(ex) =>
                  p.failure(ex)
              }
            case other =>
              p.complete(other)
          }
          p.future
      }
    }

    // -----

    def onContinueStreamOnNext[T](observer: Observer[T], nextElem: T)(implicit s: Scheduler) =
      source match {
        case sync if sync.isCompleted =>
          if (sync == Continue || sync.value.get == Continue.IsSuccess)
            observer.onNext(nextElem)
          else
            Cancel
        case async =>
          async.flatMap {
            case Continue =>
              observer.onNext(nextElem)
            case Cancel =>
              Cancel
          }
      }

    def mapToContinue(implicit s: Scheduler): Future[Continue] =
      source match {
        case sync if sync.isCompleted && sync.value.get.isSuccess =>
          Continue

        case async =>
          async.map(_ => Continue)
      }

    def onCancelStreamOnNext[T](observer: Observer[T], nextElem: T)(implicit s: Scheduler) =
      source match {
        case sync if sync.isCompleted =>
          if (sync == Continue || sync.value.get == Continue.IsSuccess)
            Continue
          else
            observer.onNext(nextElem)

        case async =>
          async.flatMap {
            case Continue => Continue
            case Cancel =>
              observer.onNext(nextElem)
          }
      }

    def onContinueCompleteWith[T](observer: Observer[T], lastElem: T)(implicit s: Scheduler): Unit =
      source match {
        case sync if sync.isCompleted =>
          if (sync == Continue || ((sync != Cancel) && sync.value.get == Continue.IsSuccess)) {
            try {
              observer.onNext(lastElem)
              observer.onComplete()
            }
            catch {
              case NonFatal(err) =>
                observer.onError(err)
            }
          }
        case async =>
          async.onSuccess {
            case Continue =>
              try {
                observer.onNext(lastElem)
                observer.onComplete()
              }
              catch {
                case NonFatal(err) =>
                  observer.onError(err)
              }
          }
      }

    /**
     * On Cancel, triggers Continue on the given Promise.
     */
    def onCancelContinue(p: Promise[Ack])(implicit s: Scheduler): Future[Ack] = {
      source match {
        case Continue => // do nothing
        case Cancel => p.success(Continue)

        case sync if sync.isCompleted && sync.value.get.isSuccess =>
          sync.value.get.get match {
            case Continue => // do nothing
            case Cancel => p.success(Continue)
          }

        case async =>
          async.onComplete {
            case Continue.IsSuccess => // nothing
            case Cancel.IsSuccess => p.success(Continue)
            case Failure(ex) => p.failure(ex)
            case other =>
              // branch not necessary, but Scala's compiler emits warnings if missing
              s.reportFailure(new MatchError(other.toString))
          }
      }
      source
    }

    /**
     * On Cancel, try to trigger Cancel on the given Promise.
     */
    def ifCancelTryCanceling(p: Promise[Ack])(implicit s: Scheduler): Future[Ack] = {
      source match {
        case Continue => // do nothing
        case Cancel => p.trySuccess(Cancel)

        case sync if sync.isCompleted =>
          sync.value.get match {
            case Continue.IsSuccess => // do nothing
            case Cancel.IsSuccess => p.trySuccess(Cancel)
            case Failure(ex) => p.tryFailure(ex)
            case other =>
              // branch not necessary, but Scala's compiler emits warnings if missing
              s.reportFailure(new MatchError(other.toString))
          }

        case async =>
          async.onComplete {
            case Continue.IsSuccess => // nothing
            case Cancel.IsSuccess => p.trySuccess(Cancel)
            case Failure(ex) => p.tryFailure(ex)
            case other =>
              // branch not necessary, but Scala's compiler emits warnings if missing
              s.reportFailure(new MatchError(other.toString))
          }
      }
      source
    }

    /**
     * On Cancel, try to trigger Cancel on the given Promise.
     */
    def ifCanceledDoCancel(p: Promise[Ack])(implicit s: Scheduler): Future[Ack] = {
      source match {
        case Continue => // do nothing
        case Cancel => p.success(Cancel)

        case sync if sync.isCompleted =>
          sync.value.get match {
            case Continue.IsSuccess => // do nothing
            case Cancel.IsSuccess => p.success(Cancel)
            case Failure(ex) => p.failure(ex)
            case other =>
              // branch not necessary, but Scala's compiler emits warnings if missing
              s.reportFailure(new MatchError(other.toString))
          }

        case async =>
          async.onComplete {
            case Continue.IsSuccess => // nothing
            case Cancel.IsSuccess => p.success(Cancel)
            case Failure(ex) => p.failure(ex)
            case other =>
              // branch not necessary, but Scala's compiler emits warnings if missing
              s.reportFailure(new MatchError(other.toString))
          }
      }
      source
    }

    /**
     * Unsafe version of `onComplete` that triggers execution synchronously
     * in case the source is already completed.
     */
    def onCompleteNow(f: Try[Ack] => Unit)(implicit s: Scheduler): Future[Ack] =
      source match {
        case sync if sync.isCompleted =>
          try f(sync.value.get) catch {
            case NonFatal(ex) =>
              s.reportFailure(ex)
          }
          source
        case async =>
          source.onComplete(f)
          source
      }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy