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

urwerk.source.internal.FluxSourceOps.scala Maven / Gradle / Ivy

The newest version!
package urwerk.source.internal

import java.util.concurrent.Flow
import java.util.function.{BiConsumer, BiFunction}

import org.reactivestreams.FlowAdapters
import org.reactivestreams.Publisher

import reactor.adapter.JdkFlowAdapter
import reactor.core.publisher.Flux
import reactor.core.publisher.FluxCreate
import reactor.core.publisher.Mono
import reactor.core.scheduler.Schedulers

import scala.concurrent.ExecutionContext
import scala.jdk.FunctionConverters.*
import scala.jdk.CollectionConverters.*

import urwerk.source.reactor.FluxConverters.*
import urwerk.source.OptionSource
import urwerk.source.SingletonSource
import urwerk.source.Source
import urwerk.source.Signal
import urwerk.source.internal.given
import urwerk.source.BufferOverflowStrategy

private abstract class FluxSourceOps[+A](val flux: Flux[_ <: A]):
  type  S[+ _]

  protected def wrap[B](flux: Flux[? <: B]): S[B]

  def cache: S[A] = wrap(flux.cache())

  def concat[B](implicit evidence: Source[A] <:< Source[Source[B]]): Source[B] =
    FluxSource.wrap(
      Flux.concat(
        flux.asInstanceOf[Flux[Source[B]]]
          .map(_.toFlux)))

  def concatDelayError[B](implicit evidence: Source[A] <:< Source[Source[B]]): Source[B] =
    FluxSource.wrap(
      Flux.concatDelayError(
        flux.asInstanceOf[Flux[Source[B]]]
          .map(_.toFlux)))

  def concat[A1 >: A](other: Source[A1]): Source[A1] =
    FluxSource.wrap(
      flux.asInstanceOf[Flux[A1]]
        .concatWith(other.toFlux))

  // def dematerialize[B](implicit evidence: Source[A] <:< Source[Signal[B]]): Source[B] =
  //   takeWhile{
  //     case Signal.Next(value) => true
  //     case Signal.Complete => false
  //     case Signal.Error(ex) =>
  //       throw ex
  //   }
  //   .map{_.asInstanceOf[Signal.Next[B]].value}

  def distinct: S[A] = wrap(flux.distinct)

  def doOnComplete(op: => Unit): S[A] =
    wrap(flux.doOnComplete(() => op))

  def doOnError(op: Throwable => Unit): S[A] =
    wrap(
      flux.doOnError(op(_)))

  def doOnNext(op: A => Unit): S[A] =
    wrap(
      flux.doOnNext(op(_)))

  def flatMap[B](op: A => Source[B]): Source[B] =
    FluxSource.wrap(
      flux.flatMap(
        op(_).toFlux))

  def flatMap[B](op: A => S[B]): S[B] =
    wrap(
      flux.flatMap(elem =>
        unwrap(op(elem).asInstanceOf[Source[B]])))

  def flatMap[B](concurrency: Int)(op: A => Source[B]): Source[B] =
    FluxSource.wrap(
      flux.flatMap(op(_).toFlux,
      concurrency))

  def flatMap[B](concurrency: Int, prefetch: Int)(op: A => Source[B]): Source[B] =
    FluxSource.wrap(
      flux.flatMap(op(_).toFlux,
      concurrency,
      prefetch))

  def foldLeft[B](start: B)(op: (B, A) => B): urwerk.source.SingletonSource[B] =
    FluxSingleton.wrap(
      flux.reduce(start,
        op(_, _)).flux)

  def head: urwerk.source.SingletonSource[A] =
    FluxSingleton.wrap(
      flux
        .next()
        .single().flux())

  def headOption: OptionSource[A] =
    FluxOptional.wrap(
      flux
        .next().flux())

  def last: SingletonSource[A] =
    FluxSingleton.wrap(
      flux
        .last().flux())

  def lastOption: OptionSource[A] =
    FluxOptional.wrap(
      flux
        .last()
        .onErrorResume(
          classOf[NoSuchElementException],
          _ => Mono.empty)
        .flux())

  def map[B](op: A => B): S[B] =
    wrap(flux.map(op(_)))

  def materialize: S[Signal[A]] =
    wrap(
      flux.materialize.map(signal => FluxSignal(signal)))

  def merge[A1 >: A](that: Source[A1]): Source[A1] =
    FluxSource.wrap((
      Flux.merge(flux, unwrap(that))))

  def merge[B](implicit evidence: Source[A] <:< Source[Source[B]]): Source[B] =
    FluxSource.wrap(
      Flux.merge(
        flux.asInstanceOf[Flux[Source[B]]]
          .map(_.toFlux)))

  def mergeDelayError[A1 >: A](prefetch: Int, that: Source[A1]): Source[A1] =
    FluxSource.wrap(
      Flux.mergeDelayError(prefetch, flux, unwrap(that)))

  def mkString(start: String, sep: String, end: String): SingletonSource[String] =
    foldLeft(StringBuilder(start))((builder, elem) =>
        builder.append(elem.toString)
          .append(sep))
      .map(_.dropRight(sep.size)
        .append(end)
        .toString)

  def onBackpressureBuffer(capacity: Int, overflowStrategy: BufferOverflowStrategy): S[A] =
    wrap(
      flux.onBackpressureBuffer(capacity, overflowStrategy.asJava))

  def onErrorContinue(op: (Throwable, Any) => Unit): Source[A] =
    FluxSource.wrap(
      flux.onErrorContinue(op.asJava))

  def onErrorMap(op: Throwable => Throwable): S[A] =
    wrap(
      flux.onErrorMap(op.asJava))

  def onErrorResume[A1 >: A](op: Throwable => Source[A1]): Source[A1] =
    FluxSource.wrap(
      flux.asInstanceOf[Flux[A1]]
        .onErrorResume{(e) => op(e).toFlux})

  def onErrorResume[A1 >: A](op: Throwable => S[A1]): S[A1] =
    wrap(
      flux.asInstanceOf[Flux[A1]]
              .onErrorResume{(e) => unwrap(op(e).asInstanceOf[Source[A1]])})

  def publishOn(ec: ExecutionContext): S[A] =
    wrap(
      flux.publishOn(Schedulers.fromExecutor(ec.toExecutor)))

  def reduce[A1 >: A](op: (A1, A) => A1): OptionSource[A1] =
    def reduceOp[B1 <: A]: BiFunction[B1, B1, B1] = (v1, v2) =>
      op(v1, v2).asInstanceOf[B1]

    FluxOptional.wrap(flux.reduce(reduceOp).flux)

  def scan[B](start: B)(op: (B, A) => B): S[B] =
    wrap(
      flux.scan(start, op.asJavaBiFunction))

  def scanWith[B](start: => B)(op: (B, A) => B): S[B] =
    wrap(
      flux.scanWith(()=> start, op.asJavaBiFunction))

  def subscribe(): AutoCloseable = {
    val disposable = flux.subscribe()
    () => {
      disposable.dispose()
    }
  }

  def subscribe[A1 >: A](subscriber: Flow.Subscriber[A1]): Unit = {
    flux.subscribe(
      FlowAdapters.toSubscriber(subscriber))
  }

  def subscribe(onNext: A => Unit, onError: Throwable => Unit, onComplete: => Unit ): AutoCloseable = {
    val disposable = flux.subscribe(onNext(_), onError(_), () => onComplete)
    () => {
      disposable.dispose()
    }
  }

  def subscribeOn(ec: ExecutionContext): S[A] =
    wrap(
      flux.subscribeOn(Schedulers.fromExecutor(ec.toExecutor)))

  def subscribeOn(ec: ExecutionContext, requestOnSeparateThread: Boolean): S[A] =
    wrap(
      flux.subscribeOn(Schedulers.fromExecutor(ec.toExecutor), requestOnSeparateThread))


  def takeUntil(predicate: A => Boolean): S[A] =
    wrap(
      flux.takeUntil(predicate.asJava))

  def takeWhile(predicate: A => Boolean): S[A] =
    wrap(
      flux.takeWhile(predicate.asJava))

  def toPublisher[A1 >: A]: Flow.Publisher[A1] =
    JdkFlowAdapter.publisherToFlowPublisher(flux.asInstanceOf[Flux[A1]])

  def toSeq: SingletonSource[Seq[A]] =
    FluxSingleton.wrap(flux
      .collectList
      .flux.map(_.asScala.toSeq))




© 2015 - 2024 Weber Informatics LLC | Privacy Policy