libminifi/include/utils/expected.h (144 lines of code) (raw):
/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 <type_traits>
#include <utility>
#include "nonstd/expected.hpp"
#include "utils/detail/MonadicOperationWrappers.h"
#include "utils/GeneralUtils.h"
#include "utils/meta/detected.h"
namespace org::apache::nifi::minifi::utils {
namespace detail {
template<typename T>
inline constexpr bool is_expected_v = false;
template<typename V, typename E>
inline constexpr bool is_expected_v<nonstd::expected<V, E>> = true;
// map implementation
template<typename Expected, typename F, typename = std::enable_if_t<is_expected_v<utils::remove_cvref_t<Expected>>>>
auto operator|(Expected&& object, map_wrapper<F> f) {
using value_type = typename utils::remove_cvref_t<Expected>::value_type;
using error_type = typename utils::remove_cvref_t<Expected>::error_type;
if constexpr (std::is_same_v<value_type, void>) {
using function_return_type = std::decay_t<decltype(std::invoke(std::forward<F>(f.function)))>;
if (!object.has_value()) {
return nonstd::expected<function_return_type, error_type>{nonstd::unexpect, std::forward<Expected>(object).error()};
}
if constexpr (std::is_same_v<function_return_type, void>) {
std::invoke(std::forward<F>(f.function));
return nonstd::expected<void, error_type>{};
} else {
return nonstd::expected<function_return_type, error_type>{std::invoke(std::forward<F>(f.function))};
}
} else {
using function_return_type = std::decay_t<decltype(std::invoke(std::forward<F>(f.function), *std::forward<Expected>(object)))>;
if (!object.has_value()) {
return nonstd::expected<function_return_type, error_type>{nonstd::unexpect, std::forward<Expected>(object).error()};
}
if constexpr (std::is_same_v<function_return_type, void>) {
std::invoke(std::forward<F>(f.function), *std::forward<Expected>(object));
return nonstd::expected<void, error_type>{};
} else {
return nonstd::expected<function_return_type, error_type>{std::invoke(std::forward<F>(f.function), *std::forward<Expected>(object))};
}
}
}
// flatMap
template<typename Expected, typename F, typename = std::enable_if_t<is_expected_v<utils::remove_cvref_t<Expected>>>>
auto operator|(Expected&& object, flat_map_wrapper<F> f) {
using value_type = typename utils::remove_cvref_t<Expected>::value_type;
if constexpr (std::is_same_v<value_type, void>) {
using function_return_type = std::decay_t<decltype(std::invoke(std::forward<F>(f.function)))>;
static_assert(is_expected_v<function_return_type>, "flatMap expects a function returning expected");
if (object.has_value()) {
return std::invoke(std::forward<F>(f.function));
} else {
return function_return_type{nonstd::unexpect, std::forward<Expected>(object).error()};
}
} else {
using function_return_type = std::decay_t<decltype(std::invoke(std::forward<F>(f.function), *std::forward<Expected>(object)))>;
static_assert(is_expected_v<function_return_type>, "flatMap expects a function returning expected");
if (object.has_value()) {
return std::invoke(std::forward<F>(f.function), *std::forward<Expected>(object));
} else {
return function_return_type{nonstd::unexpect, std::forward<Expected>(object).error()};
}
}
}
template<typename Func, typename... Args>
using invocable_detector = decltype(std::invoke(std::declval<Func>(), std::declval<Args>()...));
// orElse
template<typename Expected, typename F, typename = std::enable_if_t<is_expected_v<utils::remove_cvref_t<Expected>>>>
auto operator|(Expected&& object, or_else_wrapper<F> f) {
using error_type = typename utils::remove_cvref_t<Expected>::error_type;
if (object.has_value()) {
return std::forward<Expected>(object);
}
constexpr bool invocable_with_argument = meta::is_detected_v<invocable_detector, F, error_type>;
if constexpr (std::is_same_v<error_type, void> || !invocable_with_argument) {
constexpr bool invocable_with_no_argument = meta::is_detected_v<invocable_detector, F>;
static_assert(invocable_with_no_argument);
using function_return_type = std::decay_t<decltype(std::invoke(std::forward<F>(f.function)))>;
static_assert(is_expected_v<function_return_type> || std::is_same_v<function_return_type, void>, "orElse expects a function returning expected or void");
if constexpr (std::is_same_v<function_return_type, void>) {
std::invoke(std::forward<F>(f.function));
return std::forward<Expected>(object);
} else {
return std::invoke(std::forward<F>(f.function));
}
} else {
static_assert(invocable_with_argument);
using function_return_type = std::decay_t<decltype(std::invoke(std::forward<F>(f.function), std::forward<Expected>(object).error()))>;
static_assert(is_expected_v<function_return_type> || std::is_same_v<function_return_type, void>, "orElse expects a function returning expected or void");
if constexpr (std::is_same_v<function_return_type, void>) {
std::invoke(std::forward<F>(f.function), std::forward<Expected>(object).error());
return std::forward<Expected>(object);
} else {
return std::invoke(std::forward<F>(f.function), std::forward<Expected>(object).error());
}
}
}
// valueOrElse
template<typename Expected, typename F, typename = std::enable_if_t<is_expected_v<utils::remove_cvref_t<Expected>>>>
typename utils::remove_cvref_t<Expected>::value_type operator|(Expected&& object, value_or_else_wrapper<F> f) {
using value_type = typename utils::remove_cvref_t<Expected>::value_type;
using error_type = typename utils::remove_cvref_t<Expected>::error_type;
if (object.has_value()) {
return *std::forward<Expected>(object);
}
constexpr bool invocable_with_argument = meta::is_detected_v<invocable_detector, F, error_type>;
if constexpr (std::is_same_v<error_type, void> || !invocable_with_argument) {
constexpr bool invocable_with_no_argument = meta::is_detected_v<invocable_detector, F>;
static_assert(invocable_with_no_argument);
using function_return_type = std::decay_t<decltype(std::invoke(std::forward<F>(f.function)))>;
static_assert((std::is_same_v<function_return_type, void> && std::is_default_constructible_v<value_type>) || std::is_constructible_v<value_type, function_return_type>,
"valueOrElse expects a function returning value_type or void");
if constexpr (std::is_same_v<function_return_type, void>) {
std::invoke(std::forward<F>(f.function));
return value_type{};
} else {
return value_type{std::invoke(std::forward<F>(f.function))};
}
} else {
static_assert(invocable_with_argument);
using function_return_type = std::decay_t<decltype(std::invoke(std::forward<F>(f.function), std::forward<Expected>(object).error()))>;
static_assert((std::is_same_v<function_return_type, void> && std::is_default_constructible_v<value_type>) || std::is_constructible_v<value_type, function_return_type>,
"valueOrElse expects a function returning value_type or void");
if constexpr (std::is_same_v<function_return_type, void>) {
std::invoke(std::forward<F>(f.function), std::forward<Expected>(object).error());
return value_type{};
} else {
return value_type{std::invoke(std::forward<F>(f.function), std::forward<Expected>(object).error())};
}
}
}
} // namespace detail
template<typename F, typename... Args>
auto try_expression(F&& action, Args&&... args) noexcept {
using action_return_type = std::decay_t<decltype(std::invoke(std::forward<F>(action), std::forward<Args>(args)...))>;
using return_type = nonstd::expected<action_return_type, std::exception_ptr>;
try {
if constexpr (std::is_same_v<action_return_type, void>) {
std::invoke(std::forward<F>(action), std::forward<Args>(args)...);
return return_type{};
} else {
return return_type{std::invoke(std::forward<F>(action), std::forward<Args>(args)...)};
}
} catch (...) {
return return_type{nonstd::unexpect, std::current_exception()};
}
}
} // namespace org::apache::nifi::minifi::utils