Add map and tryMap operator specializations for Map and TryMap

This commit is contained in:
Sergej Jaskiewicz
2019-07-03 19:42:56 +03:00
committed by Sergej Jaskiewicz
parent 94fe896784
commit 2c38417545
3 changed files with 156 additions and 18 deletions
-14
View File
@@ -3687,20 +3687,6 @@ extension Publishers.Last : Equatable where Upstream : Equatable {
public static func == (lhs: Publishers.Last<Upstream>, rhs: Publishers.Last<Upstream>) -> Bool
}
extension Publishers.Map {
public func map<T>(_ transform: @escaping (Output) -> T) -> Publishers.Map<Upstream, T>
public func tryMap<T>(_ transform: @escaping (Output) throws -> T) -> Publishers.TryMap<Upstream, T>
}
extension Publishers.TryMap {
public func map<T>(_ transform: @escaping (Output) -> T) -> Publishers.TryMap<Upstream, T>
public func tryMap<T>(_ transform: @escaping (Output) throws -> T) -> Publishers.TryMap<Upstream, T>
}
extension Publishers.Sequence {
public func allSatisfy(_ predicate: (Publishers.Sequence<Elements, Failure>.Output) -> Bool) -> Publishers.Once<Bool, Failure>
@@ -66,22 +66,46 @@ extension Publishers {
}
extension Publishers.Map {
public func receive<SubscriberType: Subscriber>(subscriber: SubscriberType)
where Output == SubscriberType.Input, SubscriberType.Failure == Upstream.Failure
public func receive<Downstream: Subscriber>(subscriber: Downstream)
where Output == Downstream.Input, Downstream.Failure == Upstream.Failure
{
let inner = Inner(downstream: subscriber, transform: catching(transform))
upstream.receive(subscriber: inner)
}
public func map<Result>(
_ transform: @escaping (Output) -> Result
) -> Publishers.Map<Upstream, Result> {
return .init(upstream: upstream) { transform(self.transform($0)) }
}
public func tryMap<Result>(
_ transform: @escaping (Output) throws -> Result
) -> Publishers.TryMap<Upstream, Result> {
return .init(upstream: upstream) { try transform(self.transform($0)) }
}
}
extension Publishers.TryMap {
public func receive<SubscriberType: Subscriber>(subscriber: SubscriberType)
where Output == SubscriberType.Input, SubscriberType.Failure == Error
public func receive<Downstream: Subscriber>(subscriber: Downstream)
where Output == Downstream.Input, Downstream.Failure == Error
{
let inner = Inner(downstream: subscriber, transform: catching(transform))
upstream.receive(subscriber: inner)
}
public func map<Result>(
_ transform: @escaping (Output) -> Result
) -> Publishers.TryMap<Upstream, Result> {
return .init(upstream: upstream) { try transform(self.transform($0)) }
}
public func tryMap<Result>(
_ transform: @escaping (Output) throws -> Result
) -> Publishers.TryMap<Upstream, Result> {
return .init(upstream: upstream) { try transform(self.transform($0)) }
}
}
private class _Map<Upstream: Publisher, Downstream: Subscriber>
@@ -28,6 +28,13 @@ final class MapTests: XCTestCase {
("testCancel", testCancel),
("testCancelAlreadyCancelled", testCancelAlreadyCancelled),
("testLifecycle", testLifecycle),
("testMapOperatorSpecializationForMap", testMapOperatorSpecializationForMap),
("testTryMapOperatorSpecializationForMap",
testTryMapOperatorSpecializationForMap),
("testMapOperatorSpecializationForTryMap",
testMapOperatorSpecializationForTryMap),
("testTryMapOperatorSpecializationForTryMap",
testTryMapOperatorSpecializationForTryMap)
]
func testEmpty() {
@@ -281,4 +288,125 @@ final class MapTests: XCTestCase {
try XCTUnwrap(subscription).cancel()
XCTAssertEqual(deinitCounter, 0)
}
func testMapOperatorSpecializationForMap() {
let tracking = TrackingSubscriberBase<Int, Never>(
receiveSubscription: { $0.request(.unlimited) }
)
let publisher = PassthroughSubject<Int, Never>()
let map1 = publisher.map { $0 * 2 }
let map2 = map1.map { $0 + 1 }
map2.subscribe(tracking)
publisher.send(2)
publisher.send(3)
publisher.send(5)
XCTAssert(map1.upstream === map2.upstream)
XCTAssertEqual(tracking.history, [.subscription(Subscriptions.empty),
.value(5),
.value(7),
.value(11)])
}
func testTryMapOperatorSpecializationForMap() {
let tracking = TrackingSubscriberBase<Int, Error>(
receiveSubscription: { $0.request(.unlimited) }
)
let publisher = PassthroughSubject<Int, Never>()
let map1 = publisher.map { $0 * 2 }
let tryMap2 = map1.tryMap { input -> Int in
if input == 12 { throw TestingError.oops }
return input + 1
}
tryMap2.subscribe(tracking)
publisher.send(2)
publisher.send(3)
publisher.send(5)
XCTAssert(map1.upstream === tryMap2.upstream)
XCTAssertEqual(tracking.history, [.subscription(Subscriptions.empty),
.value(5),
.value(7),
.value(11)])
publisher.send(6)
XCTAssertEqual(tracking.history, [.subscription(Subscriptions.empty),
.value(5),
.value(7),
.value(11),
.completion(.failure(TestingError.oops))])
}
func testMapOperatorSpecializationForTryMap() {
let tracking = TrackingSubscriberBase<Int, Error>(
receiveSubscription: { $0.request(.unlimited) }
)
let publisher = PassthroughSubject<Int, Never>()
let tryMap1 = publisher.tryMap { input -> Int in
if input == 6 { throw TestingError.oops }
return input * 2
}
let tryMap2 = tryMap1.map { $0 + 1 }
tryMap2.subscribe(tracking)
publisher.send(2)
publisher.send(3)
publisher.send(5)
XCTAssert(tryMap1.upstream === tryMap2.upstream)
XCTAssertEqual(tracking.history, [.subscription(Subscriptions.empty),
.value(5),
.value(7),
.value(11)])
publisher.send(6)
XCTAssertEqual(tracking.history, [.subscription(Subscriptions.empty),
.value(5),
.value(7),
.value(11),
.completion(.failure(TestingError.oops))])
}
func testTryMapOperatorSpecializationForTryMap() {
let tracking = TrackingSubscriberBase<Int, Error>(
receiveSubscription: { $0.request(.unlimited) }
)
let publisher = PassthroughSubject<Int, Never>()
let tryMap1 = publisher.tryMap { input -> Int in
if input == 6 { throw TestingError.oops }
return input * 2
}
let tryMap2 = tryMap1.tryMap { $0 + 1 }
tryMap2.subscribe(tracking)
publisher.send(2)
publisher.send(3)
publisher.send(5)
XCTAssert(tryMap1.upstream === tryMap2.upstream)
XCTAssertEqual(tracking.history, [.subscription(Subscriptions.empty),
.value(5),
.value(7),
.value(11)])
publisher.send(6)
XCTAssertEqual(tracking.history, [.subscription(Subscriptions.empty),
.value(5),
.value(7),
.value(11),
.completion(.failure(TestingError.oops))])
}
}