blob: f4aa742d64c58a64ab1fffd6d2a762790627cbaf [file] [log] [blame]
/**
* 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"
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;
template<typename T>
concept expected = is_expected_v<std::remove_cvref_t<T>>;
// from cppreference.com: The type must not be an array type, a non-object type, a specialization of std::unexpected, or a cv-qualified type.
// base template: not cv-qualified, must be an object type, not an array type
template<typename T>
inline constexpr bool is_valid_unexpected_type_v = std::is_same_v<T, std::remove_cvref_t<T>> && std::is_object_v<T> && !std::is_array_v<T>;
// specialization: the type must not be a specialization of std::unexpected
template<typename E>
inline constexpr bool is_valid_unexpected_type_v<nonstd::unexpected_type<E>> = false;
template<typename T>
concept valid_unexpected_type = is_valid_unexpected_type_v<T>;
// transform implementation
template<expected Expected, typename F>
auto operator|(Expected&& object, transform_wrapper<F> f) {
using value_type = typename std::remove_cvref_t<Expected>::value_type;
using error_type = typename std::remove_cvref_t<Expected>::error_type;
static_assert(valid_unexpected_type<error_type>, "transform expects a valid unexpected type");
if constexpr (std::is_void_v<value_type>) {
using function_return_type = std::remove_cvref_t<std::invoke_result_t<F>>;
if (!object.has_value()) {
return nonstd::expected<function_return_type, error_type>{nonstd::unexpect, std::forward<Expected>(object).error()};
}
if constexpr (std::is_void_v<function_return_type>) {
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::remove_cvref_t<std::invoke_result_t<F, decltype(*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_void_v<function_return_type>) {
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))};
}
}
}
// andThen
template<expected Expected, typename F>
auto operator|(Expected&& object, and_then_wrapper<F> f) {
using value_type = typename std::remove_cvref_t<Expected>::value_type;
if constexpr (std::is_void_v<value_type>) {
using function_return_type = std::remove_cvref_t<std::invoke_result_t<F>>;
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::remove_cvref_t<std::invoke_result_t<F, decltype(*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()};
}
}
}
// orElse
template<expected Expected, typename F>
auto operator|(Expected&& object, or_else_wrapper<F> f) {
using error_type = typename std::remove_cvref_t<Expected>::error_type;
static_assert(valid_unexpected_type<error_type>, "orElse expects a valid unexpected type");
if (object.has_value()) {
return std::forward<Expected>(object);
}
constexpr bool invocable_with_argument = std::invocable<F, error_type>;
if constexpr (std::is_void_v<error_type> || !invocable_with_argument) {
constexpr bool invocable_with_no_argument = std::invocable<F>;
static_assert(invocable_with_no_argument);
using function_return_type = std::remove_cvref_t<std::invoke_result_t<F>>;
static_assert(is_expected_v<function_return_type> || std::is_void_v<function_return_type>, "orElse expects a function returning expected or void");
if constexpr (std::is_void_v<function_return_type>) {
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::remove_cvref_t<std::invoke_result_t<F, decltype(std::forward<Expected>(object).error())>>;
static_assert(is_expected_v<function_return_type> || std::is_void_v<function_return_type>, "orElse expects a function returning expected or void");
if constexpr (std::is_void_v<function_return_type>) {
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<expected Expected, typename F>
typename std::remove_cvref_t<Expected>::value_type operator|(Expected&& object, value_or_else_wrapper<F> f) {
using value_type = typename std::remove_cvref_t<Expected>::value_type;
using error_type = typename std::remove_cvref_t<Expected>::error_type;
static_assert(valid_unexpected_type<error_type>, "valueOrElse expects a valid unexpected type");
if (object.has_value()) {
return *std::forward<Expected>(object);
}
constexpr bool invocable_with_argument = std::invocable<F, error_type>;
if constexpr (std::is_void_v<error_type> || !invocable_with_argument) {
constexpr bool invocable_with_no_argument = std::invocable<F>;
static_assert(invocable_with_no_argument);
using function_return_type = std::remove_cvref_t<std::invoke_result_t<F>>;
static_assert((std::is_void_v<function_return_type> && 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_void_v<function_return_type>) {
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::remove_cvref_t<std::invoke_result_t<F, error_type>>;
static_assert((std::is_void_v<function_return_type> && 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_void_v<function_return_type>) {
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())};
}
}
}
// transformError
template<expected Expected, std::invocable<decltype(std::declval<Expected&&>().error())> F>
auto operator|(Expected&& object, transform_error_wrapper<F> f) {
using value_type = typename std::remove_cvref_t<Expected>::value_type;
using transformed_error_type = std::remove_cv_t<std::invoke_result_t<F, decltype(std::forward<Expected>(object).error())>>;
static_assert(valid_unexpected_type<transformed_error_type>, "transformError expects a function returning a valid unexpected type");
using transformed_expected_type = nonstd::expected<value_type, transformed_error_type>;
if (object.has_value()) {
return transformed_expected_type{std::forward<Expected>(object)};
}
return transformed_expected_type{nonstd::unexpect, 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::remove_cvref_t<std::invoke_result_t<F, Args&&...>>;
using return_type = nonstd::expected<action_return_type, std::exception_ptr>;
try {
if constexpr (std::is_void_v<action_return_type>) {
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