1/**
2 * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#ifndef LIBPANDABASE_UTILS_EXPECTED_H
17#define LIBPANDABASE_UTILS_EXPECTED_H
18
19#include <type_traits>
20#include <variant>
21
22#include "macros.h"
23
24namespace panda {
25
26template <class E>
27class Unexpected final {
28public:
29    explicit Unexpected(E e) noexcept(std::is_nothrow_move_constructible_v<E>) : e_(std::move(e)) {}
30
31    const E &Value() const &noexcept
32    {
33        return e_;
34    }
35    E &Value() & noexcept
36    {
37        return e_;
38    }
39    E &&Value() && noexcept
40    {
41        return std::move(e_);
42    }
43
44    ~Unexpected() = default;
45
46    DEFAULT_COPY_SEMANTIC(Unexpected);
47    NO_MOVE_SEMANTIC(Unexpected);
48
49private:
50    E e_;
51};
52
53class ExpectedConfig final {
54public:
55#ifdef NDEBUG
56    static constexpr bool RELEASE = true;
57#else
58    static constexpr bool RELEASE = false;
59#endif
60};
61
62// Simplified implementation of proposed std::expected
63// Note that as with std::expected, program that tries to instantiate
64// Expected with T or E for a reference type is ill-formed.
65template <class T, class E>
66class Expected final {
67public:
68    template <typename U = T, typename = std::enable_if_t<std::is_default_constructible_v<U>>>
69    Expected() noexcept : v_(T())
70    {
71    }
72    // The following constructors are non-explicit to be aligned with std::expected
73    // NOLINTNEXTLINE(google-explicit-constructor)
74    Expected(T v) noexcept(std::is_nothrow_move_constructible_v<T>) : v_(std::move(v)) {}
75    // NOLINTNEXTLINE(google-explicit-constructor)
76    Expected(Unexpected<E> e) noexcept(std::is_nothrow_move_constructible_v<E>) : v_(std::move(e.Value())) {}
77
78    bool HasValue() const noexcept
79    {
80        return std::holds_alternative<T>(v_);
81    }
82    explicit operator bool() const noexcept
83    {
84        return HasValue();
85    }
86
87    const E &Error() const &noexcept(ExpectedConfig::RELEASE)
88    {
89        ASSERT(!HasValue());
90        return std::get<E>(v_);
91    }
92    E &Error() & noexcept(ExpectedConfig::RELEASE)
93    {
94        ASSERT(!HasValue());
95        return std::get<E>(v_);
96    }
97    E &&Error() && noexcept(ExpectedConfig::RELEASE)
98    {
99        ASSERT(!HasValue());
100        return std::move(std::get<E>(v_));
101    }
102
103    const T &Value() const &noexcept(ExpectedConfig::RELEASE)
104    {
105        ASSERT(HasValue());
106        return std::get<T>(v_);
107    }
108    // TODO(aemelenko): Delete next line when the issue 388 is resolved
109    // NOLINTNEXTLINE(bugprone-exception-escape)
110    T &Value() & noexcept(ExpectedConfig::RELEASE)
111    {
112        ASSERT(HasValue());
113        return std::get<T>(v_);
114    }
115    T &&Value() && noexcept(ExpectedConfig::RELEASE)
116    {
117        ASSERT(HasValue());
118        return std::move(std::get<T>(v_));
119    }
120    const T &operator*() const &noexcept(ExpectedConfig::RELEASE)
121    {
122        return Value();
123    }
124    T &operator*() & noexcept(ExpectedConfig::RELEASE)
125    {
126        return Value();
127    }
128    T &&operator*() && noexcept(ExpectedConfig::RELEASE)
129    {
130        return std::move(*this).Value();
131    }
132
133    template <class U = T>
134    T ValueOr(U &&v) const &
135    {
136        if (HasValue()) {
137            return Value();
138        }
139        return std::forward<U>(v);
140    }
141    template <class U = T>
142    T ValueOr(U &&v) &&
143    {
144        if (HasValue()) {
145            return Value();
146        }
147        return std::forward<U>(v);
148    }
149
150    ~Expected() = default;
151
152    DEFAULT_COPY_SEMANTIC(Expected);
153    DEFAULT_MOVE_SEMANTIC(Expected);
154
155private:
156    std::variant<T, E> v_;
157};
158
159}  // namespace panda
160
161#endif  // LIBPANDABASE_UTILS_EXPECTED_H
162