aboutsummaryrefslogtreecommitdiff
path: root/lib/expected.h
blob: 81e186eabef14107fa3143d42e17eb7bb50cbb09 (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
// SPDX-FileCopyrightText: 2022 Kitsune Ral <Kitsune-Ral@users.sf.net>
// SPDX-License-Identifier: LGPL-2.1-or-later

#pragma once

#include <variant>

namespace Quotient {

//! \brief A minimal subset of std::expected from C++23
template <typename T, typename E,
          std::enable_if_t<!std::is_same_v<T, E>, bool> = true>
class Expected {
private:
    template <typename X>
    using enable_if_constructible_t = std::enable_if_t<
        std::is_constructible_v<T, X> || std::is_constructible_v<E, X>>;

public:
    using value_type = T;
    using error_type = E;

    Expected() = default;
    Expected(const Expected&) = default;
    Expected(Expected&&) noexcept = default;
    ~Expected() = default;

    template <typename X, typename = enable_if_constructible_t<X>>
    QUO_IMPLICIT Expected(X&& x) // NOLINT(google-explicit-constructor)
        : data(std::forward<X>(x))
    {}

    Expected& operator=(const Expected&) = default;
    Expected& operator=(Expected&&) noexcept = default;

    template <typename X, typename = enable_if_constructible_t<X>>
    Expected& operator=(X&& x)
    {
        data = std::forward<X>(x);
        return *this;
    }

    bool has_value() const { return std::holds_alternative<T>(data); }
    explicit operator bool() const { return has_value(); }

    const value_type& value() const& { return std::get<T>(data); }
    value_type& value() & { return std::get<T>(data); }
    value_type value() && { return std::get<T>(std::move(data)); }

    const value_type& operator*() const& { return value(); }
    value_type& operator*() & { return value(); }

    const value_type* operator->() const& { return std::get_if<T>(&data); }
    value_type* operator->() & { return std::get_if<T>(&data); }

    template <class U>
    T value_or(U&& fallback) const&
    {
        if (has_value())
            return value();
        return std::forward<U>(fallback);
    }
    template <class U>
    T value_or(U&& fallback) &&
    {
        if (has_value())
            return value();
        return std::forward<U>(fallback);
    }

    const E& error() const& { return std::get<E>(data); }
    E& error() & { return std::get<E>(data); }

private:
    std::variant<T, E> data;
};

} // namespace Quotient