// Copyright 2025 Steven Le Rouzic // // SPDX-License-Identifier: BSD-3-Clause #pragma once #include "asl/types/status.hpp" #include "asl/types/maybe_uninit.hpp" #include "asl/hashing/hash.hpp" namespace asl { template class status_or { status m_status; maybe_uninit m_value{}; public: // @Todo Convert copy // @Todo Convert move constexpr status_or(const status_or& other) requires copy_constructible : m_status{other.m_status} { if (other.ok()) { m_value.construct_unsafe(other.m_value.as_init_unsafe()); } } constexpr status_or(status_or&& other) requires move_constructible : m_status{std::move(other.m_status)} { if (other.ok()) { m_value.construct_unsafe(std::move(other.m_value.as_init_unsafe())); } } constexpr status_or& operator=(const status_or& other) requires copyable { if (&other != this) { if (ok()) { if (other.ok()) { m_value.assign_unsafe(other.m_value.as_init_unsafe()); } else { m_value.destroy_unsafe(); } } else if (other.ok()) { m_value.construct_unsafe(other.m_value.as_init_unsafe()); } m_status = other.m_status; } return *this; } constexpr status_or& operator=(status_or&& other) requires moveable { if (&other != this) { if (ok()) { if (other.ok()) { m_value.assign_unsafe(std::move(other.m_value.as_init_unsafe())); } else { m_value.destroy_unsafe(); } } else if (other.ok()) { m_value.construct_unsafe(std::move(other.m_value.as_init_unsafe())); } m_status = std::move(other.m_status); } return *this; } constexpr ~status_or() { if (m_status.ok()) { m_value.destroy_unsafe(); } } // NOLINTNEXTLINE(*explicit*) constexpr status_or(const status& status) : m_status{status} { ASL_ASSERT_RELEASE(!m_status.ok()); } // NOLINTNEXTLINE(*explicit*) constexpr status_or(status&& status) : m_status{std::move(status)} { ASL_ASSERT_RELEASE(!m_status.ok()); } status_or& operator=(status status) = delete; template constexpr explicit (!convertible_to) status_or(U&& value) requires ( constructible_from && !same_as, status_or> && !same_as, status> ) : m_status{status_code::ok} , m_value{in_place, std::forward(value)} {} [[nodiscard]] constexpr bool ok() const { return m_status.ok(); } [[nodiscard]] constexpr status_code code() const { return m_status.code(); } [[nodiscard]] constexpr string_view message() const { return m_status.message(); } constexpr status&& throw_status() && { return std::move(m_status); } constexpr auto&& value(this auto&& self) { ASL_ASSERT_RELEASE(self.ok()); return std::forward(self).m_value.as_init_unsafe(); } template constexpr T value_or(U&& other_value) const& requires copy_constructible && convertible_to { return ok() ? value() : static_cast(std::forward(other_value)); } template constexpr T value_or(U&& other_value) && requires move_constructible && convertible_to { return ok() ? std::move(value()) : static_cast(std::forward(other_value)); } friend void AslFormat(Formatter& f, const status_or& status) { AslFormat(f, status.m_status); } template requires hashable friend H AslHashValue(H h, const status_or& s) { if (s.ok()) { return H::combine(std::move(h), s.m_status, s.value()); } return H::combine(std::move(h), s.m_status); } }; template status_or(T) -> status_or; } // namespace asl