blob: fb0f3457e59e304f301a5f456312f557973ad8f8 [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 <optional>
#include <iostream>
#include "nonstd/expected.hpp"
#include "utils/detail/MonadicOperationWrappers.h"
#include "fmt/format.h"
#include "fmt/std.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>;
template<typename E>
std::string formatErrorType(const E& error) {
return fmt::format("{}", error);
}
template<>
inline std::string formatErrorType(const std::error_code& error) {
return fmt::format("{} ({})", error.message(), error);
}
// 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>, "andThen 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>, "andThen 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).value()};
}
return transformed_expected_type{nonstd::unexpect, std::invoke(std::forward<F>(f.function), std::forward<Expected>(object).error())};
}
template<expected Expected>
std::optional<typename std::remove_cvref_t<Expected>::value_type> operator|(Expected object, to_optional_wrapper) {
if (object) {
return std::move(*object);
}
return std::nullopt;
}
template<expected Expected>
typename std::remove_cvref_t<Expected>::value_type operator|(Expected object, or_throw_wrapper e) {
if (object) {
return std::move(*object);
}
throw std::runtime_error(fmt::format("{}, but got {}", e.reason, formatErrorType(object.error())));
}
template<expected Expected>
typename std::remove_cvref_t<Expected>::value_type operator|(Expected object, detail::or_terminate_wrapper e) {
if (object) {
return std::move(*object);
}
std::cerr << fmt::format("Aborting due to {}: {}", e.reason, formatErrorType(object.error())) << std::endl;
std::terminate();
}
} // 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
template <typename T, typename E>
concept HasStdExpected = requires { typename std::expected<T, E>; };
template <typename T, typename E>
concept ExpectedTypesDoNotConflict =
(!HasStdExpected<T, E> ||
!std::same_as<nonstd::expected<T, E>, std::expected<T, E>>);
// based on fmt::formatter<std::expected<T, E>, Char>
template <typename T, typename E, typename Char>
requires ExpectedTypesDoNotConflict<T, E> &&
(std::is_void_v<T> || fmt::is_formattable<T, Char>::value) &&
fmt::is_formattable<E, Char>::value
struct fmt::formatter<nonstd::expected<T, E>, Char> {
constexpr auto parse(auto& ctx) { return ctx.begin(); }
auto format(const nonstd::expected<T, E>& value, auto& ctx) const {
auto out = ctx.out();
if (value.has_value()) {
out = fmt::detail::write<Char>(out, "nonstd::expected(");
if constexpr (!std::is_void_v<T>)
out = fmt::detail::write_escaped_alternative<Char>(out, *value);
} else {
out = fmt::detail::write<Char>(out, "nonstd::unexpected(");
out = fmt::detail::write_escaped_alternative<Char>(out, value.error());
}
*out++ = ')';
return out;
}
};