You're right. Do it with flatMap
instead.
import Combine
enum Cut<Value> {
case output(Value)
case end(Bool)
var isEnd: Bool {
if case .end(_) = self { return true }
return false
}
var output: Value? {
if case .output(let output) = self { return output }
return nil
}
}
extension Publisher {
func cut(after predicate: @escaping (Output) -> Bool) -> AnyPublisher<Output, Failure> {
return self.flatMap { (input: Output) -> AnyPublisher<Cut<Output>, Failure> in
if predicate(input) {
return [.output(input), .end(true)].publisher.setFailureType(to: Failure.self).eraseToAnyPublisher()
} else {
return [.output(input)].publisher.setFailureType(to: Failure.self).eraseToAnyPublisher()
}
}
.prefix { !$0.isEnd }
.compactMap { $0.output }
.eraseToAnyPublisher()
}
}
let subject = PassthroughSubject<String, Never>()
let ticket = subject.cut(after: { $0.hasPrefix("end-") })
.sink(
receiveCompletion: { print("completion: \($0)") },
receiveValue: { print($0) }
)
subject.send("hello")
subject.send("world")
subject.send("end-transmission")
But there's a bug that makes it crash if the Cut.end
case has no associated value. Hence the dummy Bool
value.