// Copyright (c) Facebook, Inc. and its affiliates. // // 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. #pragma once #include #include #include #include #include #include "yarpl/Disposable.h" #include "yarpl/Refcounted.h" #include "yarpl/flowable/Subscriber.h" #include "yarpl/utils/credits.h" namespace yarpl { class TimeoutException; namespace detail { class TimeoutExceptionGenerator; } namespace flowable { template class Flowable; namespace details { template struct IsFlowable : std::false_type {}; template struct IsFlowable>> : std::true_type { using ElemType = R; }; } // namespace details template class Flowable : public yarpl::enable_get_ref { public: virtual ~Flowable() = default; virtual void subscribe(std::shared_ptr>) = 0; /** * Subscribe overload that accepts lambdas. */ template < typename Next, typename = typename std::enable_if< folly::is_invocable&, T>::value>::type> std::unique_ptr subscribe( Next&& next, int64_t batch = credits::kNoFlowControl) { auto subscriber = details::LambdaSubscriber::create(std::forward(next), batch); subscribe(subscriber); return std::make_unique>( std::move(subscriber)); } /** * Subscribe overload that accepts lambdas. * * Takes an optional batch size for request_n. Default is no flow control. */ template < typename Next, typename Error, typename = typename std::enable_if< folly::is_invocable&, T>::value && folly::is_invocable&, folly::exception_wrapper>:: value>::type> std::unique_ptr subscribe( Next&& next, Error&& e, int64_t batch = credits::kNoFlowControl) { auto subscriber = details::LambdaSubscriber::create( std::forward(next), std::forward(e), batch); subscribe(subscriber); return std::make_unique>( std::move(subscriber)); } /** * Subscribe overload that accepts lambdas. * * Takes an optional batch size for request_n. Default is no flow control. */ template < typename Next, typename Error, typename Complete, typename = typename std::enable_if< folly::is_invocable&, T>::value && folly::is_invocable&, folly::exception_wrapper>:: value && folly::is_invocable&>::value>::type> std::unique_ptr subscribe( Next&& next, Error&& e, Complete&& complete, int64_t batch = credits::kNoFlowControl) { auto subscriber = details::LambdaSubscriber::create( std::forward(next), std::forward(e), std::forward(complete), batch); subscribe(subscriber); return std::make_unique>( std::move(subscriber)); } void subscribe() { subscribe(Subscriber::create()); } // // creator methods: // // Creates Flowable which completes the subscriber right after it subscribes static std::shared_ptr> empty(); // Creates Flowable which will never terminate the subscriber static std::shared_ptr> never(); // Create Flowable which will imediatelly terminate the subscriber upon // subscription with the provided error static std::shared_ptr> error(folly::exception_wrapper ex); template static std::shared_ptr> error(Ex&) { static_assert( std::is_lvalue_reference::value, "use variant of error() method accepting also exception_ptr"); } template static std::shared_ptr> error(Ex& ex, std::exception_ptr ptr) { return Flowable::error(folly::exception_wrapper(std::move(ptr), ex)); } static std::shared_ptr> just(T value) { auto lambda = [value = std::move(value)]( Subscriber& subscriber, int64_t requested) mutable { DCHECK_GT(requested, 0); subscriber.onNext(std::move(value)); subscriber.onComplete(); }; return Flowable::create(std::move(lambda)); } static std::shared_ptr> justN(std::initializer_list list) { auto lambda = [v = std::vector(std::move(list)), i = size_t{0}]( Subscriber& subscriber, int64_t requested) mutable { while (i < v.size() && requested-- > 0) { subscriber.onNext(v[i++]); } if (i == v.size()) { // TODO T27302402: Even though having two subscriptions exist // concurrently for Emitters is not possible still. At least it possible // to resubscribe and consume the same values again. i = 0; subscriber.onComplete(); } }; return Flowable::create(std::move(lambda)); } // this will generate a flowable which can be subscribed to only once static std::shared_ptr> justOnce(T value) { auto lambda = [value = std::move(value), used = false]( Subscriber& subscriber, int64_t) mutable { if (used) { subscriber.onError( std::runtime_error("justOnce value was already used")); return; } used = true; // # requested should be > 0. Ignoring the actual parameter. subscriber.onNext(std::move(value)); subscriber.onComplete(); }; return Flowable::create(std::move(lambda)); } template static std::shared_ptr> fromGenerator(TGenerator&& generator); /** * The Defer operator waits until a subscriber subscribes to it, and then it * generates a Flowabe with a FlowableFactory function. It * does this afresh for each subscriber, so although each subscriber may * think it is subscribing to the same Flowable, in fact each subscriber * gets its own individual sequence. */ template < typename FlowableFactory, typename = typename std::enable_if>, std::decay_t&>::value>::type> static std::shared_ptr> defer(FlowableFactory&&); template < typename Function, typename ErrorFunction = folly::Function, typename R = typename folly::invoke_result_t, typename = typename std::enable_if&, folly::exception_wrapper&&>::value>::type> std::shared_ptr> map( Function&& function, ErrorFunction&& errormapFunc = [](folly::exception_wrapper&& ew) { return std::move(ew); }); template < typename Function, typename R = typename details::IsFlowable< typename folly::invoke_result_t>::ElemType> std::shared_ptr> flatMap(Function&& func); template std::shared_ptr> filter(Function&& function); template < typename Function, typename R = typename folly::invoke_result_t> std::shared_ptr> reduce(Function&& function); std::shared_ptr> take(int64_t); std::shared_ptr> skip(int64_t); std::shared_ptr> ignoreElements(); /* * To instruct a Flowable to do its work on a particular Executor. * the onSubscribe, request and cancel methods will be scheduled on the * provided executor */ std::shared_ptr> subscribeOn(folly::Executor&); std::shared_ptr> observeOn(folly::Executor&); std::shared_ptr> observeOn(folly::Executor::KeepAlive<>); std::shared_ptr> concatWith(std::shared_ptr>); template std::shared_ptr> concatWith( std::shared_ptr> first, Args... args) { return concatWith(first)->concatWith(args...); } template static std::shared_ptr> concat( std::shared_ptr> first, Args... args) { return first->concatWith(args...); } template using enableWrapRef = typename std::enable_if::value, Q>::type; // Combines multiple Flowables so that they act like a // single Flowable. The items // emitted by the merged Flowables may interlieve. template enableWrapRef merge() { return this->flatMap([](auto f) { return std::move(f); }); } // function is invoked when onComplete occurs. template < typename Function, typename = typename std::enable_if< folly::is_invocable&>::value>::type> std::shared_ptr> doOnSubscribe(Function&& function); // function is invoked when onNext occurs. template < typename Function, typename = typename std::enable_if< folly::is_invocable&, const T&>::value>::type> std::shared_ptr> doOnNext(Function&& function); // function is invoked when onError occurs. template < typename Function, typename = typename std::enable_if&, folly::exception_wrapper&>::value>::type> std::shared_ptr> doOnError(Function&& function); // function is invoked when onComplete occurs. template < typename Function, typename = typename std::enable_if< folly::is_invocable&>::value>::type> std::shared_ptr> doOnComplete(Function&& function); // function is invoked when either onComplete or onError occurs. template < typename Function, typename = typename std::enable_if< folly::is_invocable&>::value>::type> std::shared_ptr> doOnTerminate(Function&& function); // the function is invoked for each of onNext, onCompleted, onError template < typename Function, typename = typename std::enable_if< folly::is_invocable&>::value>::type> std::shared_ptr> doOnEach(Function&& function); // function is invoked when request(n) is called. template < typename Function, typename = typename std::enable_if< folly::is_invocable&, int64_t>::value>::type> std::shared_ptr> doOnRequest(Function&& function); // function is invoked when cancel is called. template < typename Function, typename = typename std::enable_if< folly::is_invocable&>::value>::type> std::shared_ptr> doOnCancel(Function&& function); // the callbacks will be invoked of each of the signals template < typename OnNextFunc, typename OnCompleteFunc, typename = typename std::enable_if< folly::is_invocable&, const T&>::value>:: type, typename = typename std::enable_if< folly::is_invocable&>::value>::type> std::shared_ptr> doOn( OnNextFunc&& onNext, OnCompleteFunc&& onComplete); // the callbacks will be invoked of each of the signals template < typename OnNextFunc, typename OnCompleteFunc, typename OnErrorFunc, typename = typename std::enable_if< folly::is_invocable&, const T&>::value>:: type, typename = typename std::enable_if< folly::is_invocable&>::value>::type, typename = typename std::enable_if&, folly::exception_wrapper&>::value>::type> std::shared_ptr> doOn(OnNextFunc&& onNext, OnCompleteFunc&& onComplete, OnErrorFunc&& onError); template < typename ExceptionGenerator = yarpl::detail::TimeoutExceptionGenerator> std::shared_ptr> timeout( folly::EventBase& timerEvb, std::chrono::milliseconds timeout, std::chrono::milliseconds initTimeout, ExceptionGenerator&& exnGen = ExceptionGenerator()); template < typename Emitter, typename = typename std::enable_if&, Subscriber&, int64_t>::value>::type> static std::shared_ptr> create(Emitter&& emitter); template < typename OnSubscribe, typename = typename std::enable_if>>::value>::type> // TODO(lehecka): enable this warning once mobile code is clear // [[deprecated( // "Flowable::fromPublisher is deprecated: Use PublishProcessor or " // "contact rsocket team if you can't figure out what to replace it " // "with")]] static std::shared_ptr> fromPublisher(OnSubscribe&& function); }; } // namespace flowable } // namespace yarpl #include "yarpl/flowable/DeferFlowable.h" #include "yarpl/flowable/EmitterFlowable.h" #include "yarpl/flowable/FlowableOperator.h" namespace yarpl { namespace flowable { template template std::shared_ptr> Flowable::create(Emitter&& emitter) { return std::make_shared>>( std::forward(emitter)); } template std::shared_ptr> Flowable::empty() { class EmptyFlowable : public Flowable { void subscribe(std::shared_ptr> subscriber) override { subscriber->onSubscribe(Subscription::create()); // does not wait for request(n) to complete subscriber->onComplete(); } }; return std::make_shared(); } template std::shared_ptr> Flowable::never() { class NeverFlowable : public Flowable { void subscribe(std::shared_ptr> subscriber) override { subscriber->onSubscribe(Subscription::create()); } }; return std::make_shared(); } template std::shared_ptr> Flowable::error(folly::exception_wrapper ex) { class ErrorFlowable : public Flowable { void subscribe(std::shared_ptr> subscriber) override { subscriber->onSubscribe(Subscription::create()); // does not wait for request(n) to error subscriber->onError(ex_); } folly::exception_wrapper ex_; public: explicit ErrorFlowable(folly::exception_wrapper ew) : ex_(std::move(ew)) {} }; return std::make_shared(std::move(ex)); } namespace internal { template std::shared_ptr> flowableFromSubscriber(OnSubscribe&& function) { return std::make_shared>>( std::forward(function)); } } // namespace internal // TODO(lehecka): remove template template std::shared_ptr> Flowable::fromPublisher( OnSubscribe&& function) { return internal::flowableFromSubscriber( std::forward(function)); } template template std::shared_ptr> Flowable::fromGenerator( TGenerator&& generator) { auto lambda = [generator = std::forward(generator)]( Subscriber& subscriber, int64_t requested) mutable { try { while (requested-- > 0) { subscriber.onNext(generator()); } } catch (const std::exception& ex) { subscriber.onError( folly::exception_wrapper(std::current_exception(), ex)); } catch (...) { subscriber.onError(std::runtime_error( "Flowable::fromGenerator() threw from Subscriber:onNext()")); } }; return Flowable::create(std::move(lambda)); } // namespace flowable template template std::shared_ptr> Flowable::defer(FlowableFactory&& factory) { return std::make_shared< details::DeferFlowable>>( std::forward(factory)); } template template std::shared_ptr> Flowable::map( Function&& function, ErrorFunction&& errorFunction) { return std::make_shared< MapOperator, std::decay_t>>( this->ref_from_this(this), std::forward(function), std::forward(errorFunction)); } template template std::shared_ptr> Flowable::filter(Function&& function) { return std::make_shared>>( this->ref_from_this(this), std::forward(function)); } template template std::shared_ptr> Flowable::reduce(Function&& function) { return std::make_shared>>( this->ref_from_this(this), std::forward(function)); } template std::shared_ptr> Flowable::take(int64_t limit) { return std::make_shared>(this->ref_from_this(this), limit); } template std::shared_ptr> Flowable::skip(int64_t offset) { return std::make_shared>(this->ref_from_this(this), offset); } template std::shared_ptr> Flowable::ignoreElements() { return std::make_shared>(this->ref_from_this(this)); } template std::shared_ptr> Flowable::subscribeOn( folly::Executor& executor) { return std::make_shared>( this->ref_from_this(this), executor); } template std::shared_ptr> Flowable::observeOn(folly::Executor& executor) { return observeOn(folly::getKeepAliveToken(executor)); } template std::shared_ptr> Flowable::observeOn( folly::Executor::KeepAlive<> executor) { return std::make_shared>( this->ref_from_this(this), std::move(executor)); } template template std::shared_ptr> Flowable::flatMap(Function&& function) { return std::make_shared>( this->ref_from_this(this), std::forward(function)); } template std::shared_ptr> Flowable::concatWith( std::shared_ptr> next) { return std::make_shared>( this->ref_from_this(this), std::move(next)); } template template std::shared_ptr> Flowable::doOnSubscribe(Function&& function) { return details::createDoOperator( ref_from_this(this), std::forward(function), [](const T&) {}, [](const auto&) {}, [] {}, [](const auto&) {}, // onRequest [] {}); // onCancel } template template std::shared_ptr> Flowable::doOnNext(Function&& function) { return details::createDoOperator( ref_from_this(this), [] {}, std::forward(function), [](const auto&) {}, [] {}, [](const auto&) {}, // onRequest [] {}); // onCancel } template template std::shared_ptr> Flowable::doOnError(Function&& function) { return details::createDoOperator( ref_from_this(this), [] {}, [](const T&) {}, std::forward(function), [] {}, [](const auto&) {}, // onRequest [] {}); // onCancel } template template std::shared_ptr> Flowable::doOnComplete(Function&& function) { return details::createDoOperator( ref_from_this(this), [] {}, [](const T&) {}, [](const auto&) {}, std::forward(function), [](const auto&) {}, // onRequest [] {}); // onCancel } template template std::shared_ptr> Flowable::doOnTerminate(Function&& function) { auto sharedFunction = std::make_shared>( std::forward(function)); return details::createDoOperator( ref_from_this(this), [] {}, [](const T&) {}, [sharedFunction](const auto&) { (*sharedFunction)(); }, [sharedFunction]() { (*sharedFunction)(); }, [](const auto&) {}, // onRequest [] {}); // onCancel } template template std::shared_ptr> Flowable::doOnEach(Function&& function) { auto sharedFunction = std::make_shared>( std::forward(function)); return details::createDoOperator( ref_from_this(this), [] {}, [sharedFunction](const T&) { (*sharedFunction)(); }, [sharedFunction](const auto&) { (*sharedFunction)(); }, [sharedFunction]() { (*sharedFunction)(); }, [](const auto&) {}, // onRequest [] {}); // onCancel } template template std::shared_ptr> Flowable::doOn( OnNextFunc&& onNext, OnCompleteFunc&& onComplete) { return details::createDoOperator( ref_from_this(this), [] {}, std::forward(onNext), [](const auto&) {}, std::forward(onComplete), [](const auto&) {}, // onRequest [] {}); // onCancel } template template < typename OnNextFunc, typename OnCompleteFunc, typename OnErrorFunc, typename, typename, typename> std::shared_ptr> Flowable::doOn( OnNextFunc&& onNext, OnCompleteFunc&& onComplete, OnErrorFunc&& onError) { return details::createDoOperator( ref_from_this(this), [] {}, std::forward(onNext), std::forward(onError), std::forward(onComplete), [](const auto&) {}, // onRequest [] {}); // onCancel } template template std::shared_ptr> Flowable::doOnRequest(Function&& function) { return details::createDoOperator( ref_from_this(this), [] {}, // onSubscribe [](const auto&) {}, // onNext [](const auto&) {}, // onError [] {}, // onComplete std::forward(function), // onRequest [] {}); // onCancel } template template std::shared_ptr> Flowable::doOnCancel(Function&& function) { return details::createDoOperator( ref_from_this(this), [] {}, // onSubscribe [](const auto&) {}, // onNext [](const auto&) {}, // onError [] {}, // onComplete [](const auto&) {}, // onRequest std::forward(function)); // onCancel } template template std::shared_ptr> Flowable::timeout( folly::EventBase& timerEvb, std::chrono::milliseconds starvationTimeout, std::chrono::milliseconds initTimeout, ExceptionGenerator&& exnGen) { return std::make_shared>( ref_from_this(this), timerEvb, starvationTimeout, initTimeout, std::forward(exnGen)); } } // namespace flowable } // namespace yarpl