// 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 "yarpl/Refcounted.h" #include "yarpl/observable/Observer.h" #include "yarpl/observable/Subscription.h" #include "yarpl/Common.h" #include "yarpl/Flowable.h" #include "yarpl/flowable/Flowable_FromObservable.h" namespace yarpl { namespace observable { template class Observable : public yarpl::enable_get_ref { public: static std::shared_ptr> empty() { auto lambda = [](std::shared_ptr> observer) { observer->onComplete(); }; return Observable::create(std::move(lambda)); } static std::shared_ptr> error(folly::exception_wrapper ex) { auto lambda = [ex = std::move(ex)](std::shared_ptr> observer) mutable { observer->onError(std::move(ex)); }; return Observable::create(std::move(lambda)); } 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) { auto lambda = [ew = folly::exception_wrapper(std::move(ptr), ex)]( std::shared_ptr> observer) mutable { observer->onError(std::move(ew)); }; return Observable::create(std::move(lambda)); } static std::shared_ptr> just(T value) { auto lambda = [value = std::move(value)](std::shared_ptr> observer) { observer->onNext(value); observer->onComplete(); }; return Observable::create(std::move(lambda)); } /** * The Defer operator waits until an observer subscribes to it, and then it * generates an Observable with an ObservableFactory function. It * does this afresh for each subscriber, so although each subscriber may * think it is subscribing to the same Observable, in fact each subscriber * gets its own individual sequence. */ template < typename ObservableFactory, typename = typename std::enable_if>, std::decay_t&>::value>::type> static std::shared_ptr> defer(ObservableFactory&&); static std::shared_ptr> justN(std::initializer_list list) { auto lambda = [v = std::vector(std::move(list))]( std::shared_ptr> observer) { for (auto const& elem : v) { observer->onNext(elem); } observer->onComplete(); }; return Observable::create(std::move(lambda)); } // this will generate an observable which can be subscribed to only once static std::shared_ptr> justOnce(T value) { auto lambda = [value = std::move(value), used = false]( std::shared_ptr> observer) mutable { if (used) { observer->onError( std::runtime_error("justOnce value was already used")); return; } used = true; observer->onNext(std::move(value)); observer->onComplete(); }; return Observable::create(std::move(lambda)); } template static std::shared_ptr> create(OnSubscribe&&); template static std::shared_ptr> createEx(OnSubscribe&&); virtual std::shared_ptr 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::shared_ptr subscribe(Next&& next) { return subscribe(Observer::create(std::forward(next))); } /** * Subscribe overload that accepts lambdas. */ template < typename Next, typename Error, typename = typename std::enable_if< folly::is_invocable&, T>::value && folly::is_invocable&, folly::exception_wrapper>:: value>::type> std::shared_ptr subscribe(Next&& next, Error&& error) { return subscribe(Observer::create( std::forward(next), std::forward(error))); } /** * Subscribe overload that accepts lambdas. */ 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::shared_ptr subscribe(Next&& next, Error&& error, Complete&& complete) { return subscribe(Observer::create( std::forward(next), std::forward(error), std::forward(complete))); } std::shared_ptr subscribe() { return subscribe(Observer::create()); } template < typename Function, typename R = typename folly::invoke_result_t> std::shared_ptr> map(Function&& function); 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(); std::shared_ptr> subscribeOn(folly::Executor&); 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...); } // 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); // 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); // 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); /** * Convert from Observable to Flowable with a given BackpressureStrategy. */ auto toFlowable(BackpressureStrategy strategy); /** * Convert from Observable to Flowable with a given BackpressureStrategy. */ auto toFlowable(std::shared_ptr> strategy); }; } // namespace observable } // namespace yarpl #include "yarpl/observable/DeferObservable.h" #include "yarpl/observable/ObservableOperator.h" namespace yarpl { namespace observable { template template std::shared_ptr> Observable::create(OnSubscribe&& function) { static_assert( folly::is_invocable>>::value, "OnSubscribe must have type `void(std::shared_ptr>)`"); return createEx([func = std::forward(function)]( std::shared_ptr> observer, std::shared_ptr) mutable { func(std::move(observer)); }); } template template std::shared_ptr> Observable::createEx(OnSubscribe&& function) { static_assert( folly::is_invocable< OnSubscribe&&, std::shared_ptr>, std::shared_ptr>::value, "OnSubscribe must have type " "`void(std::shared_ptr>, std::shared_ptr)`"); return std::make_shared>>( std::forward(function)); } template template std::shared_ptr> Observable::defer( ObservableFactory&& factory) { return std::make_shared< details::DeferObservable>>( std::forward(factory)); } template template std::shared_ptr> Observable::map(Function&& function) { return std::make_shared>>( this->ref_from_this(this), std::forward(function)); } template template std::shared_ptr> Observable::filter(Function&& function) { return std::make_shared>>( this->ref_from_this(this), std::forward(function)); } template template std::shared_ptr> Observable::reduce(Function&& function) { return std::make_shared>>( this->ref_from_this(this), std::forward(function)); } template std::shared_ptr> Observable::take(int64_t limit) { return std::make_shared>(this->ref_from_this(this), limit); } template std::shared_ptr> Observable::skip(int64_t offset) { return std::make_shared>(this->ref_from_this(this), offset); } template std::shared_ptr> Observable::ignoreElements() { return std::make_shared>(this->ref_from_this(this)); } template std::shared_ptr> Observable::subscribeOn( folly::Executor& executor) { return std::make_shared>( this->ref_from_this(this), executor); } template template std::shared_ptr> Observable::doOnSubscribe( Function&& function) { return details::createDoOperator( ref_from_this(this), std::forward(function), [](const T&) {}, [](const auto&) {}, [] {}, [] {}); // onCancel } template std::shared_ptr> Observable::concatWith( std::shared_ptr> next) { return std::make_shared>( this->ref_from_this(this), std::move(next)); } template template std::shared_ptr> Observable::doOnNext(Function&& function) { return details::createDoOperator( ref_from_this(this), [] {}, std::forward(function), [](const auto&) {}, [] {}, [] {}); // onCancel } template template std::shared_ptr> Observable::doOnError(Function&& function) { return details::createDoOperator( ref_from_this(this), [] {}, [](const T&) {}, std::forward(function), [] {}, [] {}); // onCancel } template template std::shared_ptr> Observable::doOnComplete( Function&& function) { return details::createDoOperator( ref_from_this(this), [] {}, [](const T&) {}, [](const auto&) {}, std::forward(function), [] {}); // onCancel } template template std::shared_ptr> Observable::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)(); }, [] {}); // onCancel } template template std::shared_ptr> Observable::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)(); }, [] {}); // onCancel } template template std::shared_ptr> Observable::doOn( OnNextFunc&& onNext, OnCompleteFunc&& onComplete) { return details::createDoOperator( ref_from_this(this), [] {}, std::forward(onNext), [](const auto&) {}, std::forward(onComplete), [] {}); // onCancel } template template < typename OnNextFunc, typename OnCompleteFunc, typename OnErrorFunc, typename, typename, typename> std::shared_ptr> Observable::doOn( OnNextFunc&& onNext, OnCompleteFunc&& onComplete, OnErrorFunc&& onError) { return details::createDoOperator( ref_from_this(this), [] {}, std::forward(onNext), std::forward(onError), std::forward(onComplete), [] {}); // onCancel } template template std::shared_ptr> Observable::doOnCancel(Function&& function) { return details::createDoOperator( ref_from_this(this), [] {}, // onSubscribe [](const auto&) {}, // onNext [](const auto&) {}, // onError [] {}, // onComplete std::forward(function)); // onCancel } template auto Observable::toFlowable(BackpressureStrategy strategy) { switch (strategy) { case BackpressureStrategy::DROP: return toFlowable(IBackpressureStrategy::drop()); case BackpressureStrategy::ERROR: return toFlowable(IBackpressureStrategy::error()); case BackpressureStrategy::BUFFER: return toFlowable(IBackpressureStrategy::buffer()); case BackpressureStrategy::LATEST: return toFlowable(IBackpressureStrategy::latest()); case BackpressureStrategy::MISSING: return toFlowable(IBackpressureStrategy::missing()); default: CHECK(false); // unknown value for strategy } } template auto Observable::toFlowable( std::shared_ptr> strategy) { return yarpl::flowable::internal::flowableFromSubscriber( [thisObservable = this->ref_from_this(this), strategy = std::move(strategy)]( std::shared_ptr> subscriber) { strategy->init(std::move(thisObservable), std::move(subscriber)); }); } } // namespace observable } // namespace yarpl