summaryrefslogtreecommitdiff
path: root/asl/status_or.hpp
blob: ec76e47e10113ac97024fce36be072ca17d5968e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
#pragma once

#include "asl/status.hpp"
#include "asl/maybe_uninit.hpp"
#include "asl/hash.hpp"

namespace asl
{

template<is_object T>
class status_or
{
    status          m_status;
    maybe_uninit<T> m_value{};

public:
    // @Todo Convert copy
    // @Todo Convert move

    constexpr status_or(const status_or& other)
        requires copy_constructible<T>
        : 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<T>
        : m_status{ASL_MOVE(other.m_status)}
    {
        if (other.ok())
        {
            m_value.construct_unsafe(ASL_MOVE(other.m_value.as_init_unsafe()));
        }
    }

    constexpr status_or& operator=(const status_or& other)
        requires copyable<T>
    {
        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<T>
    {
        if (&other != this)
        {
            if (ok())
            {
                if (other.ok())
                {
                    m_value.assign_unsafe(ASL_MOVE(other.m_value.as_init_unsafe()));
                }
                else
                {
                    m_value.destroy_unsafe();
                }
            }
            else if (other.ok())
            {
                m_value.construct_unsafe(ASL_MOVE(other.m_value.as_init_unsafe()));
            }
            m_status = ASL_MOVE(other.m_status);
        }
        return *this;
    }

    constexpr ~status_or()
    {
        if (m_status.ok())
        {
            m_value.destroy_unsafe();
        }
    }

    // NOLINTNEXTLINE(*-explicit-conversions)
    constexpr status_or(const status& status) : m_status{status}
    {
        ASL_ASSERT_RELEASE(!m_status.ok());
    }

    // NOLINTNEXTLINE(*-explicit-conversions)
    constexpr status_or(status&& status) : m_status{ASL_MOVE(status)}
    {
        ASL_ASSERT_RELEASE(!m_status.ok());
    }

    status_or& operator=(status status) = delete;

    template<typename U = T>
    constexpr explicit (!convertible_from<T, U&&>)
    status_or(U&& value)
        requires (
            constructible_from<T, U&&> &&
            !same_as<un_cvref_t<U>, status_or> &&
            !same_as<un_cvref_t<U>, status>
        )
        : m_status{status_code::ok}
        , m_value{in_place, ASL_FWD(value)}
    {}

    constexpr bool ok() const { return m_status.ok(); }

    constexpr status_code code() const { return m_status.code(); }

    constexpr string_view message() const { return m_status.message(); }

    constexpr status&& throw_status() && { return ASL_MOVE(m_status); }

    // @Todo(C++23) Deducing this
    constexpr const T& value() const&
    {
        ASL_ASSERT_RELEASE(ok());
        return m_value.as_init_unsafe();
    }

    constexpr T& value() &
    {
        ASL_ASSERT_RELEASE(ok());
        return m_value.as_init_unsafe();
    }

    constexpr T&& value() &&
    {
        ASL_ASSERT_RELEASE(ok());
        return ASL_MOVE(m_value.as_init_unsafe());
    }

    template<typename U>
    constexpr T value_or(U&& other_value) const&
        requires copy_constructible<T> && convertible_from<T, U&&>
    {
        return ok() ? value() : static_cast<T>(ASL_FWD(other_value));
    }

    template<typename U>
    constexpr T value_or(U&& other_value) &&
        requires move_constructible<T> && convertible_from<T, U&&>
    {
        return ok() ? ASL_MOVE(value()) : static_cast<T>(ASL_FWD(other_value));
    }

    friend void AslFormat(Formatter& f, const status_or& status)
    {
        AslFormat(f, status.m_status);
    }

    template<typename H>
    requires hashable<T>
    friend H AslHashValue(H h, const status_or& s)
    {
        if (s.ok())
        {
            return H::combine(ASL_MOVE(h), s.m_status, s.value());
        }
        return H::combine(ASL_MOVE(h), s.m_status);
    }
};

template<typename T>
status_or(T) -> status_or<T>;

} // namespace asl