feat: Add dimap operator to Flow. (#942)
This commit is contained in:
parent
057fe066e8
commit
cf70478201
9 changed files with 190 additions and 8 deletions
36
docs/src/main/paradox/stream/operators/Flow/dimap.md
Normal file
36
docs/src/main/paradox/stream/operators/Flow/dimap.md
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
# dimap
|
||||||
|
|
||||||
|
Transform this Flow by applying a function `f` to each *incoming* upstream element before it is passed to the Flow, and a function `g` to each *outgoing* downstream element.
|
||||||
|
|
||||||
|
@ref[Simple operators](../index.md#simple-operators)
|
||||||
|
|
||||||
|
## Signature
|
||||||
|
|
||||||
|
@apidoc[Flow.dimap](Flow) { scala="#dimap%5BIn2%2C%20Out2%5D(f%3A%20In2%20%3D%3E%20In)(g%3A%20Out%20%3D%3E%20Out2)%3Aorg.apache.pekko.stream.scaladsl.Flow%5BIn2%2COut2%2CMat%5D" java="#dimap(org.apache.pekko.japi.function.Function,org.apache.pekko.japi.function.Function)" }
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
Transform this Flow by applying a function `f` to each *incoming* upstream element before it is passed to the Flow,
|
||||||
|
and a function `g` to each *outgoing* downstream element.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
Scala
|
||||||
|
: @@snip [DiMap.scala](/docs/src/test/scala/docs/stream/operators/flow/DiMap.scala) { #imports #dimap }
|
||||||
|
|
||||||
|
Java
|
||||||
|
: @@snip [DiMap.java](/docs/src/test/java/jdocs/stream/operators/flow/DiMap.java) { #imports #dimap }
|
||||||
|
|
||||||
|
## Reactive Streams semantics
|
||||||
|
|
||||||
|
@@@div { .callout }
|
||||||
|
|
||||||
|
**emits** when the mapping function `g` returns an element
|
||||||
|
|
||||||
|
**backpressures** '''Backpressures when''' original flow backpressures
|
||||||
|
|
||||||
|
**completes** when original flow completes
|
||||||
|
|
||||||
|
**cancels** when original flow cancels
|
||||||
|
|
||||||
|
@@@
|
||||||
|
|
@ -148,6 +148,7 @@ depending on being backpressured by downstream or not.
|
||||||
|Flow|<a name="completionstageflow"></a>@ref[completionStageFlow](Flow/completionStageFlow.md)|Streams the elements through the given future flow once it successfully completes.|
|
|Flow|<a name="completionstageflow"></a>@ref[completionStageFlow](Flow/completionStageFlow.md)|Streams the elements through the given future flow once it successfully completes.|
|
||||||
|Flow|<a name="contramap"></a>@ref[contramap](Flow/contramap.md)|Transform this Flow by applying a function to each *incoming* upstream element before it is passed to the Flow.|
|
|Flow|<a name="contramap"></a>@ref[contramap](Flow/contramap.md)|Transform this Flow by applying a function to each *incoming* upstream element before it is passed to the Flow.|
|
||||||
|Source/Flow|<a name="detach"></a>@ref[detach](Source-or-Flow/detach.md)|Detach upstream demand from downstream demand without detaching the stream rates.|
|
|Source/Flow|<a name="detach"></a>@ref[detach](Source-or-Flow/detach.md)|Detach upstream demand from downstream demand without detaching the stream rates.|
|
||||||
|
|Flow|<a name="dimap"></a>@ref[dimap](Flow/dimap.md)|Transform this Flow by applying a function `f` to each *incoming* upstream element before it is passed to the Flow, and a function `g` to each *outgoing* downstream element.|
|
||||||
|Source/Flow|<a name="drop"></a>@ref[drop](Source-or-Flow/drop.md)|Drop `n` elements and then pass any subsequent element downstream.|
|
|Source/Flow|<a name="drop"></a>@ref[drop](Source-or-Flow/drop.md)|Drop `n` elements and then pass any subsequent element downstream.|
|
||||||
|Source/Flow|<a name="dropwhile"></a>@ref[dropWhile](Source-or-Flow/dropWhile.md)|Drop elements as long as a predicate function return true for the element|
|
|Source/Flow|<a name="dropwhile"></a>@ref[dropWhile](Source-or-Flow/dropWhile.md)|Drop elements as long as a predicate function return true for the element|
|
||||||
|Source/Flow|<a name="filter"></a>@ref[filter](Source-or-Flow/filter.md)|Filter the incoming elements using a predicate.|
|
|Source/Flow|<a name="filter"></a>@ref[filter](Source-or-Flow/filter.md)|Filter the incoming elements using a predicate.|
|
||||||
|
|
@ -429,6 +430,7 @@ For more background see the @ref[Error Handling in Streams](../stream-error.md)
|
||||||
* [delay](Source-or-Flow/delay.md)
|
* [delay](Source-or-Flow/delay.md)
|
||||||
* [delayWith](Source-or-Flow/delayWith.md)
|
* [delayWith](Source-or-Flow/delayWith.md)
|
||||||
* [detach](Source-or-Flow/detach.md)
|
* [detach](Source-or-Flow/detach.md)
|
||||||
|
* [dimap](Flow/dimap.md)
|
||||||
* [divertTo](Source-or-Flow/divertTo.md)
|
* [divertTo](Source-or-Flow/divertTo.md)
|
||||||
* [drop](Source-or-Flow/drop.md)
|
* [drop](Source-or-Flow/drop.md)
|
||||||
* [dropWhile](Source-or-Flow/dropWhile.md)
|
* [dropWhile](Source-or-Flow/dropWhile.md)
|
||||||
|
|
|
||||||
45
docs/src/test/java/jdocs/stream/operators/flow/DiMap.java
Normal file
45
docs/src/test/java/jdocs/stream/operators/flow/DiMap.java
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You 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 jdocs.stream.operators.flow;
|
||||||
|
|
||||||
|
// #imports
|
||||||
|
import org.apache.pekko.NotUsed;
|
||||||
|
import org.apache.pekko.actor.ActorSystem;
|
||||||
|
import org.apache.pekko.stream.javadsl.Flow;
|
||||||
|
import org.apache.pekko.stream.javadsl.Source;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
// #imports
|
||||||
|
|
||||||
|
public class DiMap {
|
||||||
|
private static final ActorSystem system = null;
|
||||||
|
|
||||||
|
private void demoDiMapUsage() {
|
||||||
|
// #dimap
|
||||||
|
final Source<String, NotUsed> source = Source.from(Arrays.asList("1", "2", "3"));
|
||||||
|
final Flow<Integer, Integer, NotUsed> flow = Flow.<Integer>create().map(elem -> elem * 2);
|
||||||
|
source
|
||||||
|
.via(flow.dimap(Integer::parseInt, String::valueOf))
|
||||||
|
.runForeach(System.out::println, system);
|
||||||
|
// expected prints:
|
||||||
|
// 2
|
||||||
|
// 4
|
||||||
|
// 6
|
||||||
|
// #dimap
|
||||||
|
}
|
||||||
|
}
|
||||||
42
docs/src/test/scala/docs/stream/operators/flow/DiMap.scala
Normal file
42
docs/src/test/scala/docs/stream/operators/flow/DiMap.scala
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You 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 docs.stream.operators.flow
|
||||||
|
|
||||||
|
//#imports
|
||||||
|
import org.apache.pekko
|
||||||
|
import pekko.NotUsed
|
||||||
|
import pekko.actor.ActorSystem
|
||||||
|
import pekko.stream.scaladsl._
|
||||||
|
//#imports
|
||||||
|
|
||||||
|
object DiMap {
|
||||||
|
implicit val system: ActorSystem = null
|
||||||
|
|
||||||
|
def demoDiMap(): Unit = {
|
||||||
|
// #dimap
|
||||||
|
val source = Source(List("1", "2", "3"))
|
||||||
|
val flow: Flow[Int, Int, NotUsed] = Flow[Int].map(_ * 2)
|
||||||
|
val newFlow: Flow[String, String, NotUsed] = flow.dimap(Integer.parseInt)(_.toString)
|
||||||
|
source.via(newFlow).runForeach(println)
|
||||||
|
// expected prints:
|
||||||
|
// 2
|
||||||
|
// 4
|
||||||
|
// 6
|
||||||
|
// #dimap
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -134,6 +134,20 @@ public class FlowTest extends StreamTest {
|
||||||
.expectComplete();
|
.expectComplete();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void mustBeAbleToUseDiMap() {
|
||||||
|
final Source<String, NotUsed> source = Source.from(Arrays.asList("1", "2", "3"));
|
||||||
|
final Flow<Integer, Integer, NotUsed> flow = Flow.<Integer>create().map(elem -> elem * 2);
|
||||||
|
source
|
||||||
|
.via(flow.dimap(Integer::valueOf, String::valueOf))
|
||||||
|
.runWith(TestSink.create(system), system)
|
||||||
|
.request(3)
|
||||||
|
.expectNext("2")
|
||||||
|
.expectNext("4")
|
||||||
|
.expectNext("6")
|
||||||
|
.expectComplete();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void mustBeAbleToUseDropWhile() throws Exception {
|
public void mustBeAbleToUseDropWhile() throws Exception {
|
||||||
final TestKit probe = new TestKit(system);
|
final TestKit probe = new TestKit(system);
|
||||||
|
|
|
||||||
|
|
@ -215,6 +215,14 @@ class FlowSpec extends StreamSpec(ConfigFactory.parseString("pekko.actor.debug.r
|
||||||
sub.expectComplete()
|
sub.expectComplete()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
"perform dimap operation" in {
|
||||||
|
val flow = Flow[Int].dimap(Integer.parseInt)(_ * 2)
|
||||||
|
val sub = Source(List("1", "2", "3")).via(flow).runWith(TestSink())
|
||||||
|
sub.request(3)
|
||||||
|
sub.expectNextN(List(2, 4, 6))
|
||||||
|
sub.expectComplete()
|
||||||
|
}
|
||||||
|
|
||||||
"perform transformation operation" in {
|
"perform transformation operation" in {
|
||||||
val flow = Flow[Int].map(i => { testActor ! i.toString; i.toString })
|
val flow = Flow[Int].map(i => { testActor ! i.toString; i.toString })
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,8 @@ import pekko.stream.Attributes._
|
||||||
val fused = name("fused")
|
val fused = name("fused")
|
||||||
val materializedValueSource = name("matValueSource")
|
val materializedValueSource = name("matValueSource")
|
||||||
val map = name("map")
|
val map = name("map")
|
||||||
|
val contramap = name("contramap")
|
||||||
|
val dimap = name("dimap")
|
||||||
val log = name("log")
|
val log = name("log")
|
||||||
val filter = name("filter")
|
val filter = name("filter")
|
||||||
val filterNot = name("filterNot")
|
val filterNot = name("filterNot")
|
||||||
|
|
|
||||||
|
|
@ -25,8 +25,6 @@ import scala.collection.immutable
|
||||||
import scala.concurrent.duration.FiniteDuration
|
import scala.concurrent.duration.FiniteDuration
|
||||||
import scala.reflect.ClassTag
|
import scala.reflect.ClassTag
|
||||||
|
|
||||||
import org.reactivestreams.Processor
|
|
||||||
|
|
||||||
import org.apache.pekko
|
import org.apache.pekko
|
||||||
import pekko.Done
|
import pekko.Done
|
||||||
import pekko.NotUsed
|
import pekko.NotUsed
|
||||||
|
|
@ -47,6 +45,8 @@ import pekko.util.OptionConverters._
|
||||||
import pekko.util.Timeout
|
import pekko.util.Timeout
|
||||||
import pekko.util.unused
|
import pekko.util.unused
|
||||||
|
|
||||||
|
import org.reactivestreams.Processor
|
||||||
|
|
||||||
object Flow {
|
object Flow {
|
||||||
|
|
||||||
/** Create a `Flow` which can process elements of type `T`. */
|
/** Create a `Flow` which can process elements of type `T`. */
|
||||||
|
|
@ -544,6 +544,22 @@ final class Flow[In, Out, Mat](delegate: scaladsl.Flow[In, Out, Mat]) extends Gr
|
||||||
def contramap[In2](f: function.Function[In2, In]): javadsl.Flow[In2, Out, Mat] =
|
def contramap[In2](f: function.Function[In2, In]): javadsl.Flow[In2, Out, Mat] =
|
||||||
new Flow(delegate.contramap(elem => f(elem)))
|
new Flow(delegate.contramap(elem => f(elem)))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transform this Flow by applying a function `f` to each *incoming* upstream element before
|
||||||
|
* it is passed to the [[Flow]], and a function `g` to each *outgoing* downstream element.
|
||||||
|
*
|
||||||
|
* '''Emits when''' the mapping function `g` returns an element
|
||||||
|
*
|
||||||
|
* '''Backpressures when''' original [[Flow]] backpressures
|
||||||
|
*
|
||||||
|
* '''Completes when''' original [[Flow]] completes
|
||||||
|
*
|
||||||
|
* '''Cancels when''' original [[Flow]] cancels
|
||||||
|
* @since 1.1.0
|
||||||
|
*/
|
||||||
|
def dimap[In2, Out2](f: function.Function[In2, In], g: function.Function[Out, Out2]): javadsl.Flow[In2, Out2, Mat] =
|
||||||
|
new Flow(delegate.dimap(f(_))(g(_)))
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Join this [[Flow]] to another [[Flow]], by cross connecting the inputs and outputs, creating a [[RunnableGraph]].
|
* Join this [[Flow]] to another [[Flow]], by cross connecting the inputs and outputs, creating a [[RunnableGraph]].
|
||||||
* {{{
|
* {{{
|
||||||
|
|
|
||||||
|
|
@ -21,11 +21,6 @@ import scala.concurrent.Future
|
||||||
import scala.concurrent.duration.FiniteDuration
|
import scala.concurrent.duration.FiniteDuration
|
||||||
import scala.reflect.ClassTag
|
import scala.reflect.ClassTag
|
||||||
|
|
||||||
import org.reactivestreams.Processor
|
|
||||||
import org.reactivestreams.Publisher
|
|
||||||
import org.reactivestreams.Subscriber
|
|
||||||
import org.reactivestreams.Subscription
|
|
||||||
|
|
||||||
import org.apache.pekko
|
import org.apache.pekko
|
||||||
import pekko.Done
|
import pekko.Done
|
||||||
import pekko.NotUsed
|
import pekko.NotUsed
|
||||||
|
|
@ -48,12 +43,18 @@ import pekko.stream.impl.TraversalBuilder
|
||||||
import pekko.stream.impl.fusing
|
import pekko.stream.impl.fusing
|
||||||
import pekko.stream.impl.fusing._
|
import pekko.stream.impl.fusing._
|
||||||
import pekko.stream.impl.fusing.FlattenMerge
|
import pekko.stream.impl.fusing.FlattenMerge
|
||||||
|
import pekko.stream.impl.Stages.DefaultAttributes
|
||||||
import pekko.stream.stage._
|
import pekko.stream.stage._
|
||||||
import pekko.util.ConstantFun
|
import pekko.util.ConstantFun
|
||||||
import pekko.util.OptionVal
|
import pekko.util.OptionVal
|
||||||
import pekko.util.Timeout
|
import pekko.util.Timeout
|
||||||
import pekko.util.ccompat._
|
import pekko.util.ccompat._
|
||||||
|
|
||||||
|
import org.reactivestreams.Processor
|
||||||
|
import org.reactivestreams.Publisher
|
||||||
|
import org.reactivestreams.Subscriber
|
||||||
|
import org.reactivestreams.Subscription
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A `Flow` is a set of stream processing steps that has one open input and one open output.
|
* A `Flow` is a set of stream processing steps that has one open input and one open output.
|
||||||
*/
|
*/
|
||||||
|
|
@ -182,7 +183,23 @@ final class Flow[-In, +Out, +Mat](
|
||||||
* @since 1.1.0
|
* @since 1.1.0
|
||||||
*/
|
*/
|
||||||
def contramap[In2](f: In2 => In): Flow[In2, Out, Mat] =
|
def contramap[In2](f: In2 => In): Flow[In2, Out, Mat] =
|
||||||
Flow.fromFunction(f).viaMat(this)(Keep.right)
|
Flow.fromFunction(f).viaMat(this)(Keep.right).withAttributes(DefaultAttributes.contramap)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transform this Flow by applying a function `f` to each *incoming* upstream element before
|
||||||
|
* it is passed to the [[Flow]], and a function `g` to each *outgoing* downstream element.
|
||||||
|
*
|
||||||
|
* '''Emits when''' the mapping function `g` returns an element
|
||||||
|
*
|
||||||
|
* '''Backpressures when''' original [[Flow]] backpressures
|
||||||
|
*
|
||||||
|
* '''Completes when''' original [[Flow]] completes
|
||||||
|
*
|
||||||
|
* '''Cancels when''' original [[Flow]] cancels
|
||||||
|
* @since 1.1.0
|
||||||
|
*/
|
||||||
|
def dimap[In2, Out2](f: In2 => In)(g: Out => Out2): Flow[In2, Out2, Mat] =
|
||||||
|
Flow.fromFunction(f).viaMat(this)(Keep.right).map(g).withAttributes(DefaultAttributes.dimap)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Join this [[Flow]] to another [[Flow]], by cross connecting the inputs and outputs, creating a [[RunnableGraph]].
|
* Join this [[Flow]] to another [[Flow]], by cross connecting the inputs and outputs, creating a [[RunnableGraph]].
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue