• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

DNKpp / gimo / 26362170509

24 May 2026 01:09PM UTC coverage: 92.929% (-6.0%) from 98.925%
26362170509

push

github

DNKpp
chore: bump library version to 0.3.1

32 of 41 branches covered (78.05%)

Branch coverage included in aggregate %.

152 of 157 relevant lines covered (96.82%)

36.36 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

76.19
/include/gimo/algorithm/OrElse.hpp
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_OR_ELSE_HPP
7
#define GIMO_ALGORITHM_OR_ELSE_HPP
8

9
#pragma once
10

11
#include "gimo/Common.hpp"
12
#include "gimo/Pipeline.hpp"
13
#include "gimo/algorithm/BasicAlgorithm.hpp"
14

15
#include <concepts>
16
#include <functional>
17
#include <tuple>
18
#include <type_traits>
19
#include <utility>
20

21
namespace gimo::detail::or_else
22
{
23
    template <typename Nullable, typename Action>
24
    consteval Nullable& print_diagnostics()
25
    {
26
        if constexpr (!std::is_invocable_v<Action>)
27
        {
28
            static_assert(always_false_v<Nullable>, "The or_else algorithm requires an action invocable without any arguments.");
29
        }
30
        else if constexpr (!std::same_as<Nullable, std::invoke_result_t<Action>> && !std::is_void_v<std::invoke_result_t<Action>>)
31
        {
32
            static_assert(always_false_v<Nullable>, "The or_else algorithm requires an action returning the same nullable type or void.");
33
        }
34

35
        return std::declval<Nullable&>();
36
    }
37

38
    template <typename Action, nullable Nullable>
39
    [[nodiscard]]
40
    constexpr std::remove_cvref_t<Nullable> on_value(Action&& /*action*/, Nullable&& opt)
41
    {
42
        return std::forward<Nullable>(opt);
7✔
43
    }
44

45
    template <typename Action, nullable Nullable, typename Next, typename... Steps>
46
    [[nodiscard]]
47
    constexpr auto on_value(
48
        Action&& /*action*/,
49
        Nullable&& opt,
50
        Next&& next,
51
        Steps&&... steps)
52
    {
53
        return std::forward<Next>(next).on_value(
12✔
54
            std::forward<Nullable>(opt),
55
            std::forward<Steps>(steps)...);
4✔
56
    }
57

58
    template <typename Action, nullable Nullable>
59
    [[nodiscard]]
60
    constexpr std::remove_cvref_t<Nullable> on_null(Action&& action, Nullable&& /*opt*/)
61
    {
62
        if constexpr (std::is_void_v<std::invoke_result_t<Action>>)
63
        {
64
            std::invoke(std::forward<Action>(action));
1✔
65
            return detail::construct_empty<Nullable>();
1✔
66
        }
67
        else
68
        {
69
            return std::invoke(std::forward<Action>(action));
22✔
70
        }
71
    }
72

73
    template <nullable Nullable, typename Action, typename Next, typename... Steps>
74
    [[nodiscard]]
75
    constexpr auto on_null(Action&& action, Nullable&& opt, Next&& next, Steps&&... steps)
76
    {
77
        return std::invoke(
8✔
78
            std::forward<Next>(next),
79
            or_else::on_null(std::forward<Action>(action), std::forward<Nullable>(opt)),
12✔
80
            std::forward<Steps>(steps)...);
8✔
81
    }
82

83
    template <nullable Nullable, typename Action, typename Next, typename... Steps>
84
        requires std::is_void_v<std::invoke_result_t<Action>>
85
    [[nodiscard]]
86
    constexpr auto on_null(Action&& action, Nullable&& opt, Next&& next, Steps&&... steps)
87
    {
88
        return std::forward<Next>(next).on_null(
×
89
            or_else::on_null(std::forward<Action>(action), std::forward<Nullable>(opt)),
×
90
            std::forward<Steps>(steps)...);
×
91
    }
92

93
    struct traits
94
    {
95
        template <nullable Nullable, typename Action>
96
        static constexpr bool is_applicable_on = requires {
97
            requires std::same_as<
98
                         std::remove_cvref_t<Nullable>,
99
                         std::remove_cvref_t<std::invoke_result_t<Action>>>
100
                         || std::is_void_v<std::invoke_result_t<Action>>;
101
        };
102

103
        template <typename Action, nullable Nullable, typename... Steps>
104
        [[nodiscard]]
105
        static constexpr auto on_value(Action&& action, Nullable&& opt, Steps&&... steps)
106
        {
107
            GIMO_ASSERT(detail::has_value(opt), "Nullable is empty while it's expected to contain a value.");
108

109
            if constexpr (is_applicable_on<Nullable, Action>)
110
            {
111
                return or_else::on_value(
18✔
112
                    std::forward<Action>(action),
113
                    std::forward<Nullable>(opt),
114
                    std::forward<Steps>(steps)...);
17✔
115
            }
116
            else
117
            {
118
                return or_else::print_diagnostics<Nullable, Action>();
119
            }
120
        }
121

122
        template <typename Action, nullable Nullable, typename... Steps>
123
        [[nodiscard]]
124
        static constexpr auto on_null(Action&& action, Nullable&& opt, Steps&&... steps)
125
        {
126
            GIMO_ASSERT(!detail::has_value(opt), "Nullable contains a value while it's expected to be empty.");
127

128
            if constexpr (is_applicable_on<Nullable, Action>)
129
            {
130
                return or_else::on_null(
42✔
131
                    std::forward<Action>(action),
132
                    std::forward<Nullable>(opt),
133
                    std::forward<Steps>(steps)...);
29✔
134
            }
135
            else
136
            {
137
                return or_else::print_diagnostics<Nullable, Action>();
138
            }
139
        }
140
    };
141
}
142

143
namespace gimo
144
{
145
    namespace detail
146
    {
147
        template <typename Action>
148
        using or_else_t = BasicAlgorithm<or_else::traits, std::remove_cvref_t<Action>>;
149
    }
150

151
    /**
152
     * \brief Creates a pipeline step that handles the null/error case.
153
     * \ingroup ALGORITHM
154
     * \tparam Action The action type.
155
     * \param action A nullary operation.
156
     * \return A Pipeline step containing the `or_else` algorithm.
157
     * \details
158
     * - **On Value**: Propagates the value state immediately (i.e., `action` is not executed).
159
     * - **On Null**: Invokes the `action`. The `action` must either return the **exact same** `nullable` type or `void`.
160
     * \see https://en.wikipedia.org/wiki/Monad_(functional_programming)
161
     */
162
    template <typename Action>
163
    [[nodiscard]]
164
    constexpr auto or_else(Action&& action)
165
    {
166
        using Algorithm = detail::or_else_t<Action>;
167

168
        return Pipeline{std::tuple<Algorithm>{std::forward<Action>(action)}};
6✔
169
    }
170
}
171

172
#endif
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc