/* * Copyright 2022-present ScyllaDB */ /* * SPDX-License-Identifier: LicenseRef-ScyllaDB-Source-Available-1.0 */ #pragma once #include #include #include "utils/result.hh" namespace utils { // Converts a result into either a ready or an exceptional future. // Supports only results which has an exception_container as their error type. template requires ExceptionContainerResult seastar::future result_into_future(R&& res) { if (res) { if constexpr (std::is_void_v) { return seastar::make_ready_future<>(); } else { return seastar::make_ready_future(std::move(res).assume_value()); } } else { return std::move(res).assume_error().template into_exception_future(); } } // Converts a non-result future to return a successful result. // It _does not_ convert exceptional futures to failed results. template requires ExceptionContainerResult seastar::future then_ok_result(seastar::future&& f) { using T = typename R::value_type; if constexpr (std::is_void_v) { return f.then([] { return seastar::make_ready_future(bo::success()); }); } else { return f.then([] (T&& t) { return seastar::make_ready_future(bo::success(std::move(t))); }); } } // Takes a result, discards the inner value and returns result<>. template rebind_result result_discard_value(R&& res) { if (res) { return bo::success(); } else { return std::move(res).as_failure(); } } namespace internal { template struct result_wrapped_call_traits { }; template struct result_wrapped_call_traits { static_assert(ExceptionContainerResult); using return_type = decltype(seastar::futurize_invoke(std::declval(), std::declval())); static auto invoke_with_value(C& c, Arg&& arg) { // Arg must have a value return seastar::futurize_invoke(c, std::move(arg).value()); } }; template struct result_wrapped_call_traits, false> { using return_type = decltype(seastar::futurize_invoke(std::declval())); static auto invoke_with_value(C& c, result_with_exception&& arg) { return seastar::futurize_invoke(c); } }; template struct result_wrapped_call_traits, ExCont, exception_container_throw_policy>, true> { private: using result_type = bo::result, ExCont, exception_container_throw_policy>; public: using return_type = decltype(seastar::futurize_apply(std::declval(), std::declval>())); static auto invoke_with_value(C& c, result_type&& args) { // Arg must have a value return seastar::futurize_apply(c, std::move(args).value()); } }; template struct result_wrapped_call_traits { static_assert(false && sizeof(Arg), "result_wrap_apply must be called with a result> as a second argument"); }; template struct result_wrapper { C c; result_wrapper(C&& c) : c(std::move(c)) {} template requires ExceptionContainerResult auto operator()(InputResult arg) { using traits = internal::result_wrapped_call_traits; using return_type = typename traits::return_type; static_assert(ExceptionContainerResultFuture, "the return type of the call must be a future> for some T"); using return_result_type = typename return_type::value_type; static_assert(std::is_same_v, "the error type of accepted result and returned result are not the same"); if (arg) { return traits::invoke_with_value(c, std::move(arg)); } else { return seastar::make_ready_future(bo::failure(std::move(arg).assume_error())); } } }; } // Converts a callable to accept a result instead of T. // Given a callable which can be called with following arguments and return type: // // (T) -> future> // // ...returns a new callable which can be called with the following signature: // // (result) -> future> // // On call, the adapted callable is run only if the result contains a value. // Otherwise, the adapted callable is not called and the error is returned immediately. // The resulting callable must receive result<> as an argument when being called, // it won't be automatically converted to result<>. template auto result_wrap(C&& c) { return internal::result_wrapper(std::move(c)); } // Similar to result_wrap, but the resulting callable takes a result>, // unpacks the tuple and provides each argument separately to the wrapped callable. template auto result_wrap_unpack(C&& c) { return internal::result_wrapper(std::move(c)); } }