Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 99 additions & 0 deletions include/gimo/algorithm/AndForward.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
// Copyright Dominic (DNKpp) Koepke 2026.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// https://www.boost.org/LICENSE_1_0.txt)

#ifndef GIMO_ALGORITHM_AND_FORWARD_HPP
#define GIMO_ALGORITHM_AND_FORWARD_HPP

#pragma once

#include "gimo/Common.hpp"
#include "gimo/Pipeline.hpp"
#include "gimo/algorithm/BasicAlgorithm.hpp"

#include <concepts>
#include <functional>
#include <tuple>
#include <utility>

namespace gimo::detail::and_forward
{
template <typename Nullable, typename Action>
consteval void print_diagnostics()
{
if constexpr (!std::invocable<Action, value_result_t<Nullable>>)
{
static_assert(always_false_v<Nullable>, "The and_forward algorithm requires an action invocable with the nullable's value.");
}
}

struct traits
{
template <nullable Nullable, typename Action>
static constexpr bool is_applicable_on = requires {
requires std::invocable<Action, value_result_t<Nullable>>;
};

template <typename Action, nullable Nullable>
static constexpr void on_value(Action&& action, Nullable&& opt)
{
GIMO_ASSERT(detail::has_value(opt), "Nullable is empty while it's expected to contain a value.");

if constexpr (is_applicable_on<Nullable, Action>)
{
std::invoke(
std::forward<Action>(action),
detail::forward_value<Nullable>(opt));
}
else
{
and_forward::print_diagnostics<Nullable, Action>();
}
}

template <typename Action, nullable Nullable>
static constexpr void on_null(Action&& /*action*/, [[maybe_unused]] Nullable&& opt)
{
GIMO_ASSERT(!detail::has_value(opt), "Nullable contains a value while it's expected to be empty.");

if constexpr (!is_applicable_on<Nullable, Action>)
{
and_forward::print_diagnostics<Nullable, Action>();
}
}
};
}

namespace gimo
{
namespace detail
{
template <typename Action>
using and_forward_t = BasicAlgorithm<
and_forward::traits,
std::remove_cvref_t<Action>>;
}

/**
* \brief Creates a terminating pipeline step that forwards the contained value to the specified action.
* \ingroup ALGORITHM
* \tparam Action The action type.
* \param action A unary operation.
* \return A Pipeline step containing the `and_forward` algorithm.
* \details
* - **On Value**: Invokes the `action` with the underlying value of the input.
* Any returned value by `action` will be discarded, thus `action` may return `void`.
* - **On Null**: Silently terminates the pipeline.
*/
template <typename Action>
[[nodiscard]]
constexpr auto and_forward(Action&& action)
{
using Algorithm = detail::and_forward_t<Action>;

return Pipeline{std::tuple<Algorithm>{std::forward<Action>(action)}};
}
}

#endif
10 changes: 7 additions & 3 deletions include/gimo/algorithm/AndThen.hpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright Dominic (DNKpp) Koepke 2025.
// Copyright Dominic (DNKpp) Koepke 2025-2026.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// https://www.boost.org/LICENSE_1_0.txt)
Expand Down Expand Up @@ -64,14 +64,14 @@ namespace gimo::detail::and_then

template <typename Action, nullable Nullable>
[[nodiscard]]
constexpr result_t<Nullable, Action> on_null([[maybe_unused]] Action&& action, [[maybe_unused]] Nullable&& opt)
constexpr result_t<Nullable, Action> on_null(Action&& /*action*/, Nullable&& /*opt*/)
{
return detail::construct_empty<result_t<Nullable, Action>>();
}

template <typename Action, expected_like Expected>
[[nodiscard]]
constexpr result_t<Expected, Action> on_null([[maybe_unused]] Action&& action, Expected&& expected)
constexpr result_t<Expected, Action> on_null(Action&& /*action*/, Expected&& expected)
{
return detail::rebind_error<result_t<Expected, Action>, Expected>(expected);
}
Expand All @@ -96,6 +96,8 @@ namespace gimo::detail::and_then
[[nodiscard]]
static constexpr auto on_value(Action&& action, Nullable&& opt, Steps&&... steps)
{
GIMO_ASSERT(detail::has_value(opt), "Nullable is empty while it's expected to contain a value.");

if constexpr (is_applicable_on<Nullable, Action>)
{
return and_then::on_value(
Expand All @@ -113,6 +115,8 @@ namespace gimo::detail::and_then
[[nodiscard]]
static constexpr auto on_null(Action&& action, Nullable&& opt, Steps&&... steps)
{
GIMO_ASSERT(!detail::has_value(opt), "Nullable contains a value while it's expected to be empty.");

if constexpr (is_applicable_on<Nullable, Action>)
{
return and_then::on_null(
Expand Down
6 changes: 5 additions & 1 deletion include/gimo/algorithm/OrElse.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ namespace gimo::detail::or_else
if constexpr (std::is_void_v<std::invoke_result_t<Action>>)
{
std::invoke(std::forward<Action>(action));
return null_v<Nullable>;
return detail::construct_empty<Nullable>();
}
else
{
Expand Down Expand Up @@ -104,6 +104,8 @@ namespace gimo::detail::or_else
[[nodiscard]]
static constexpr auto on_value(Action&& action, Nullable&& opt, Steps&&... steps)
{
GIMO_ASSERT(detail::has_value(opt), "Nullable is empty while it's expected to contain a value.");

if constexpr (is_applicable_on<Nullable, Action>)
{
return or_else::on_value(
Expand All @@ -121,6 +123,8 @@ namespace gimo::detail::or_else
[[nodiscard]]
static constexpr auto on_null(Action&& action, Nullable&& opt, Steps&&... steps)
{
GIMO_ASSERT(!detail::has_value(opt), "Nullable contains a value while it's expected to be empty.");

if constexpr (is_applicable_on<Nullable, Action>)
{
return or_else::on_null(
Expand Down
14 changes: 9 additions & 5 deletions include/gimo/algorithm/Transform.hpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright Dominic (DNKpp) Koepke 2025.
// Copyright Dominic (DNKpp) Koepke 2025-2026.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// https://www.boost.org/LICENSE_1_0.txt)
Expand Down Expand Up @@ -41,7 +41,7 @@ namespace gimo::detail::transform

template <typename Action, nullable Nullable>
[[nodiscard]]
constexpr result_t<Nullable, Action> on_value([[maybe_unused]] Action&& action, Nullable&& opt)
constexpr result_t<Nullable, Action> on_value(Action&& action, Nullable&& opt)
{
return construct_from_value<result_t<Nullable, Action>>(
std::invoke(
Expand All @@ -52,7 +52,7 @@ namespace gimo::detail::transform
template <typename Action, nullable Nullable, typename Next, typename... Steps>
[[nodiscard]]
constexpr auto on_value(
[[maybe_unused]] Action&& action,
Action&& action,
Nullable&& opt,
Next&& next,
Steps&&... steps)
Expand All @@ -64,14 +64,14 @@ namespace gimo::detail::transform

template <typename Action, nullable Nullable>
[[nodiscard]]
constexpr result_t<Nullable, Action> on_null([[maybe_unused]] Action&& action, [[maybe_unused]] Nullable&& opt)
constexpr result_t<Nullable, Action> on_null(Action&& /*action*/, Nullable&& /*opt*/)
{
return detail::construct_empty<result_t<Nullable, Action>>();
}

template <typename Action, expected_like Expected>
[[nodiscard]]
constexpr result_t<Expected, Action> on_null([[maybe_unused]] Action&& action, Expected&& expected)
constexpr result_t<Expected, Action> on_null(Action&& /*action*/, Expected&& expected)
{
return detail::rebind_error<result_t<Expected, Action>, Expected>(expected);
}
Expand All @@ -98,6 +98,8 @@ namespace gimo::detail::transform
[[nodiscard]]
static constexpr auto on_value(Action&& action, Nullable&& opt, Steps&&... steps)
{
GIMO_ASSERT(detail::has_value(opt), "Nullable is empty while it's expected to contain a value.");

if constexpr (is_applicable_on<Nullable, Action>)
{
return transform::on_value(
Expand All @@ -115,6 +117,8 @@ namespace gimo::detail::transform
[[nodiscard]]
static constexpr auto on_null(Action&& action, Nullable&& opt, Steps&&... steps)
{
GIMO_ASSERT(!detail::has_value(opt), "Nullable contains a value while it's expected to be empty.");

if constexpr (is_applicable_on<Nullable, Action>)
{
return transform::on_null(
Expand Down
8 changes: 6 additions & 2 deletions include/gimo/algorithm/TransformError.hpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright Dominic (DNKpp) Koepke 2025.
// Copyright Dominic (DNKpp) Koepke 2025-2026.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// https://www.boost.org/LICENSE_1_0.txt)
Expand Down Expand Up @@ -45,7 +45,7 @@ namespace gimo::detail::transform_error

template <typename Action, expected_like Expected>
[[nodiscard]]
constexpr result_t<Expected, Action> on_value([[maybe_unused]] Action&& action, Expected&& closure)
constexpr result_t<Expected, Action> on_value(Action&& /*action*/, Expected&& closure)
{
return detail::rebind_value<result_t<Expected, Action>, Expected>(closure);
}
Expand Down Expand Up @@ -95,6 +95,8 @@ namespace gimo::detail::transform_error
[[nodiscard]]
static constexpr auto on_value(Action&& action, Expected&& closure, Steps&&... steps)
{
GIMO_ASSERT(detail::has_value(opt), "Nullable is empty while it's expected to contain a value.");

if constexpr (is_applicable_on<Expected, Action>)
{
return transform_error::on_value(
Expand All @@ -112,6 +114,8 @@ namespace gimo::detail::transform_error
[[nodiscard]]
static constexpr auto on_null(Action&& action, Expected&& closure, Steps&&... steps)
{
GIMO_ASSERT(!detail::has_value(opt), "Nullable contains a value while it's expected to be empty.");

if constexpr (is_applicable_on<Expected, Action>)
{
return transform_error::on_null(
Expand Down
6 changes: 5 additions & 1 deletion include/gimo/algorithm/ValueOrElse.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,10 @@ namespace gimo::detail::value_or_else

template <typename Action, nullable Nullable>
[[nodiscard]]
static constexpr auto on_value([[maybe_unused]] Action&& action, Nullable&& opt)
static constexpr auto on_value(Action&& /*action*/, Nullable&& opt)
{
GIMO_ASSERT(detail::has_value(opt), "Nullable is empty while it's expected to contain a value.");

if constexpr (is_applicable_on<Nullable, Action>)
{
return detail::forward_value<Nullable>(opt);
Expand All @@ -60,6 +62,8 @@ namespace gimo::detail::value_or_else
[[nodiscard]]
static constexpr auto on_null(Action&& action, [[maybe_unused]] Nullable&& opt)
{
GIMO_ASSERT(!detail::has_value(opt), "Nullable contains a value while it's expected to be empty.");

if constexpr (is_applicable_on<Nullable, Action>)
{
return static_cast<result_t<Nullable>>(
Expand Down
Loading
Loading