gimo v0.3.2
Loading...
Searching...
No Matches
TransformError.hpp
Go to the documentation of this file.
1// Copyright Dominic (DNKpp) Koepke 2025-2026.
2// Distributed under the Boost Software License, Version 1.0.
3// (See accompanying file LICENSE_1_0.txt or copy at
4// https://www.boost.org/LICENSE_1_0.txt)
5
6#ifndef GIMO_ALGORITHM_TRANSFORM_ERROR_HPP
7#define GIMO_ALGORITHM_TRANSFORM_ERROR_HPP
8
9#pragma once
10
11#include "gimo/Common.hpp"
12#include "gimo/Pipeline.hpp"
14
15#include <functional>
16#include <tuple>
17#include <type_traits>
18#include <utility>
19
20namespace gimo::detail::transform_error
21{
22 template <typename Expected, typename Action>
23 consteval Expected* print_diagnostics()
24 {
25 if constexpr (!expected_like<Expected>)
26 {
27 static_assert(always_false_v<Expected>, "The transform_error algorithm requires an expected-like input.");
28 }
29 else if constexpr (!std::is_invocable_v<Action, error_result_t<Expected>>)
30 {
31 static_assert(always_false_v<Expected>, "The transform_error algorithm requires an action invocable with the expected's error.");
32 }
33 else if constexpr (!rebindable_error_to<Expected, std::invoke_result_t<Action, error_result_t<Expected>>>)
34 {
35 static_assert(always_false_v<Expected>, "The transform_error algorithm requires an expected-like whose error-type can be rebound.");
36 }
37
38 return nullptr;
39 }
40
41 template <typename Expected, typename Action>
42 using result_t = rebind_error_t<
43 Expected,
44 std::invoke_result_t<Action, error_result_t<Expected>>>;
45
46 template <typename Action, expected_like Expected>
47 [[nodiscard]]
48 constexpr result_t<Expected, Action> on_value(Action&& /*action*/, Expected&& closure)
49 {
50 return detail::rebind_value<result_t<Expected, Action>, Expected>(closure);
51 }
52
53 template <typename Action, expected_like Expected, typename Next, typename... Steps>
54 [[nodiscard]]
55 constexpr auto on_value(
56 Action&& action,
57 Expected&& closure,
58 Next&& next,
59 Steps&&... steps)
60 {
61 return std::forward<Next>(next).on_value(
62 transform_error::on_value(std::forward<Action>(action), std::forward<Expected>(closure)),
63 std::forward<Steps>(steps)...);
64 }
65
66 template <typename Action, expected_like Expected>
67 [[nodiscard]]
68 constexpr result_t<Expected, Action> on_null(Action&& action, Expected&& closure)
69 {
70 return detail::construct_from_error<result_t<Expected, Action>>(
71 std::invoke(
72 std::forward<Action>(action),
73 detail::forward_error<Expected>(closure)));
74 }
75
76 template <typename Action, expected_like Expected, typename Next, typename... Steps>
77 [[nodiscard]]
78 constexpr auto on_null(Action&& action, Expected&& closure, Next&& next, Steps&&... steps)
79 {
80 return std::forward<Next>(next).on_null(
81 transform_error::on_null(std::forward<Action>(action), std::forward<Expected>(closure)),
82 std::forward<Steps>(steps)...);
83 }
84
85 struct traits
86 {
87 template <nullable Expected, typename Action>
88 static constexpr bool is_applicable_on = requires {
89 requires rebindable_error_to<
90 Expected,
91 std::invoke_result_t<Action, error_result_t<Expected>>>;
92 };
93
94 template <typename Action, nullable Expected, typename... Steps>
95 [[nodiscard]]
96 static constexpr auto on_value(Action&& action, Expected&& closure, Steps&&... steps)
97 {
98 GIMO_ASSERT(detail::has_value(closure), "Nullable is empty while it's expected to contain a value.");
99
100 if constexpr (is_applicable_on<Expected, Action>)
101 {
102 return transform_error::on_value(
103 std::forward<Action>(action),
104 std::forward<Expected>(closure),
105 std::forward<Steps>(steps)...);
106 }
107 else
108 {
109 return *transform_error::print_diagnostics<Expected, Action>();
110 }
111 }
112
113 template <typename Action, nullable Expected, typename... Steps>
114 [[nodiscard]]
115 static constexpr auto on_null(Action&& action, Expected&& closure, Steps&&... steps)
116 {
117 GIMO_ASSERT(!detail::has_value(closure), "Nullable contains a value while it's expected to be empty.");
118
119 if constexpr (is_applicable_on<Expected, Action>)
120 {
121 return transform_error::on_null(
122 std::forward<Action>(action),
123 std::forward<Expected>(closure),
124 std::forward<Steps>(steps)...);
125 }
126 else
127 {
128 return *transform_error::print_diagnostics<Expected, Action>();
129 }
130 }
131 };
132}
133
134namespace gimo
135{
136 namespace detail
137 {
138 template <typename Action>
139 using transform_error_t = BasicAlgorithm<transform_error::traits, std::remove_cvref_t<Action>>;
140 }
141
161 template <typename Action>
162 [[nodiscard]]
163 constexpr auto transform_error(Action&& action)
164 {
165 using Algorithm = detail::transform_error_t<Action>;
166
167 return Pipeline{std::tuple<Algorithm>{std::forward<Action>(action)}};
168 }
169}
170
171#endif
#define GIMO_ASSERT(condition, msg,...)
Definition Config.hpp:11
A composite object representing a sequence of monadic operations.
Definition Pipeline.hpp:30
constexpr auto transform_error(Action &&action)
Creates a pipeline step that transforms the error of an expected-like type.
Definition TransformError.hpp:163
Definition AndForward.hpp:21
typename traits< std::remove_cvref_t< Expected > >::template rebind_error< std::remove_cvref_t< Error > > rebind_error_t
Helper alias to obtain the Expected type with the rebound Error type.
Definition Common.hpp:416