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

monix.reactive.Pipe.scala Maven / Gradle / Ivy

/*
 * Copyright (c) 2014-2016 by its authors. Some rights reserved.
 * See the project homepage at: https://monix.io
 *
 * 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 monix.reactive

import monix.execution.Scheduler
import monix.reactive.observables.ObservableLike
import monix.reactive.observables.ObservableLike.{Operator, Transformer}
import monix.reactive.OverflowStrategy.{Synchronous, Unbounded}
import monix.reactive.Pipe.{LiftedPipe, TransformedPipe}
import monix.reactive.observers.{BufferedSubscriber, Subscriber}
import monix.reactive.subjects._

/** Represents a factory for an input/output channel for
  * broadcasting input to multiple subscribers.
  */
abstract class Pipe[I, +O]
  extends ObservableLike[O, ({type λ[+α] = Pipe[I, α]})#λ] {

  /** Returns an input/output pair that can be used to
    * push input to a single subscriber.
    *
    * This means that the returned observable should be subscribed
    * at most once, otherwise the behavior is undefined.
    *
    * @see [[multicast]] for creating a safe observable that can
    *     be subscribed many times.
    */
  def unicast: (Observer[I], Observable[O])

  /** Returns an input/output pair that can be used to
    * push input to multiple subscribers.
    */
  def multicast(implicit s: Scheduler): (Observer[I], Observable[O]) = {
    val (in,out) = unicast
    val proc = PublishSubject[O]()
    out.unsafeSubscribeFn(Subscriber(proc, s))
    (in,proc)
  }

  /** Returns an input/output pair with an input that can be
    * used synchronously and concurrently (without back-pressure or
    * multi-threading issues) to push signals to multiple subscribers.
    */
  def concurrent(implicit s: Scheduler): (Observer.Sync[I], Observable[O]) =
    concurrent(Unbounded)(s)

  /** Returns an input/output pair with an input that can be
    * used synchronously and concurrently (without back-pressure or
    * multi-threading issues) to push signals to multiple subscribers.
    *
    * @param strategy is the [[OverflowStrategy]] used for the underlying
    *                 multi-producer/single-consumer buffer
    */
  def concurrent(strategy: Synchronous[I])(implicit s: Scheduler): (Observer.Sync[I], Observable[O]) = {
    val (in,out) = multicast(s)
    val buffer = BufferedSubscriber.synchronous[I](Subscriber(in, s), strategy)
    (buffer, out)
  }

  // provides observable-like operators
  override def liftByOperator[B](op: Operator[O, B]): Pipe[I, B] =
    new LiftedPipe(this, op)

  /** Transforms the source using the given transformer function. */
  override def transform[B](transformer: Transformer[O, B]): Pipe[I, B] =
    new TransformedPipe(this, transformer)
}

object Pipe {
  /** Given a [[MulticastStrategy]] returns the corresponding [[Pipe]]. */
  def apply[A](strategy: MulticastStrategy[A]): Pipe[A,A] =
    strategy match {
      case MulticastStrategy.Publish =>
        Pipe.publish[A]
      case MulticastStrategy.Behavior(initial) =>
        Pipe.behavior[A](initial)
      case MulticastStrategy.Async =>
        Pipe.async[A]
      case MulticastStrategy.Replay(initial) =>
        Pipe.replay[A](initial)
      case MulticastStrategy.ReplayLimited(capacity, initial) =>
        Pipe.replayLimited[A](capacity, initial)
    }

  /** Subject recipe for building
    * [[monix.reactive.subjects.PublishSubject PublishSubject]] instances.
    */
  def publish[A]: Pipe[A,A] =
    new Pipe[A,A] {
      def unicast: (Observer[A], Observable[A]) = {
        val p = PublishSubject[A]()
        (p,p)
      }

      override def multicast(implicit s: Scheduler): (Observer[A], Observable[A]) =
        unicast
    }

  /** Subject recipe for building
    * [[monix.reactive.subjects.PublishToOneSubject PublishToOneSubject]]
    * instances.
    */
  def publishToOne[A]: Pipe[A,A] =
    new Pipe[A,A] {
      def unicast: (Observer[A], Observable[A]) = {
        val p = PublishToOneSubject[A]()
        (p,p)
      }
    }

  /** Subject recipe for building
    * [[monix.reactive.subjects.BehaviorSubject BehaviorSubject]]
    * instances.
    */
  def behavior[A](initial: A): Pipe[A,A] =
    new Pipe[A,A] {
      def unicast: (Observer[A], Observable[A]) = {
        val p = BehaviorSubject[A](initial)
        (p,p)
      }

      override def multicast(implicit s: Scheduler): (Observer[A], Observable[A]) =
        unicast
    }

  /** Subject recipe for building
    * [[monix.reactive.subjects.AsyncSubject AsyncSubject]] instances.
    */
  def async[A]: Pipe[A,A] =
    new Pipe[A,A] {
      def unicast: (Observer[A], Observable[A]) = {
        val p = AsyncSubject[A]()
        (p,p)
      }

      override def multicast(implicit s: Scheduler): (Observer[A], Observable[A]) =
        unicast
    }

  /** Subject recipe for building unbounded
    * [[monix.reactive.subjects.ReplaySubject monix.reactive.subjects.]] instances.
    */
  def replay[A]: Pipe[A,A] =
    replay(Seq.empty)

  /** Subject recipe for building unbounded
    * [[monix.reactive.subjects.ReplaySubject ReplaySubject]]
    * instances.
    *
    * @param initial is an initial sequence of elements that will be pushed
    *        to subscribers before any elements emitted by the source.
    */
  def replay[A](initial: Seq[A]): Pipe[A,A] =
    new Pipe[A,A] {
      def unicast: (Observer[A], Observable[A]) = {
        val p = ReplaySubject.create[A](initial)
        (p,p)
      }

      override def multicast(implicit s: Scheduler): (Observer[A], Observable[A]) =
        unicast
    }

  /** Subject recipe for building
    * [[monix.reactive.subjects.ReplaySubject ReplaySubject]] instances
    * with a maximum `capacity` (after which old items start being dropped).
    *
    * @param capacity indicates the minimum capacity of the underlying buffer,
    *        with the implementation being free to increase it.
    */
  def replayLimited[A](capacity: Int): Pipe[A,A] =
    replayLimited(capacity, Seq.empty)

  /** Subject recipe for building
    * [[monix.reactive.subjects.ReplaySubject ReplaySubject]] instances
    * with a maximum `capacity` (after which old items start being dropped).
    *
    * @param capacity indicates the minimum capacity of the underlying buffer,
    *        with the implementation being free to increase it.
    * @param initial is an initial sequence of elements that will be pushed
    *        to subscribers before any elements emitted by the source.
    */
  def replayLimited[A](capacity: Int, initial: Seq[A]): Pipe[A,A] =
    new Pipe[A,A] {
      def unicast: (Observer[A], Observable[A]) = {
        val p = ReplaySubject.createLimited[A](capacity, initial)
        (p,p)
      }

      override def multicast(implicit s: Scheduler): (Observer[A], Observable[A]) =
        unicast
    }

  private final class LiftedPipe[I,+O,+U](self: Pipe[I,O], op: Operator[O, U])
    extends Pipe[I,U] {

    def unicast: (Observer[I], Observable[U]) = {
      val (in,out) = self.unicast
      val outU = out.liftByOperator(op)
      (in, outU)
    }

    override def multicast(implicit s: Scheduler): (Observer[I], Observable[U]) = {
      val (in,out) = self.multicast(s)
      val outU = out.liftByOperator(op)
      (in, outU)
    }
  }

  private final class TransformedPipe[I,+O,+U](self: Pipe[I,O], f: Transformer[O, U])
    extends Pipe[I,U] {

    override def unicast: (Observer[I], Observable[U]) = {
      val (in,out) = self.unicast
      (in, f(out))
    }

    override def multicast(implicit s: Scheduler): (Observer[I], Observable[U]) = {
      val (in,out) = self.multicast(s)
      (in, f(out))
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy