summaryrefslogtreecommitdiffstats
path: root/dom/indexedDB/IDBResult.h
blob: 424eda9bff1206f8a7d9a16f12ca2867d22c4544 (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
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef mozilla_dom_indexeddb_idbresult_h__
#define mozilla_dom_indexeddb_idbresult_h__

#include "mozilla/ErrorResult.h"
#include "mozilla/ResultVariant.h"
#include "mozilla/Variant.h"

#include <type_traits>
#include <utility>

namespace mozilla::dom::indexedDB {

// IDBSpecialValue represents two special return values, distinct from any other
// value, used in several places in the IndexedDB spec.
enum class IDBSpecialValue {
  Failure,
  Invalid,
};

namespace detail {

template <IDBSpecialValue Value>
using SpecialConstant = std::integral_constant<IDBSpecialValue, Value>;
using FailureType = SpecialConstant<IDBSpecialValue::Failure>;
using InvalidType = SpecialConstant<IDBSpecialValue::Invalid>;
struct ExceptionType final {};
}  // namespace detail

// Put these in a subnamespace to avoid conflicts from the combination of 1.
// using namespace mozilla::dom::indexedDB; in cpp files, 2. the unified build
// and 3. mozilla::dom::Exception
namespace SpecialValues {
constexpr const detail::FailureType Failure;
constexpr const detail::InvalidType Invalid;
constexpr const detail::ExceptionType Exception;
}  // namespace SpecialValues

namespace detail {
template <IDBSpecialValue... Elements>
struct IsSortedSet;

template <IDBSpecialValue First, IDBSpecialValue Second,
          IDBSpecialValue... Rest>
struct IsSortedSet<First, Second, Rest...>
    : std::integral_constant<bool, IsSortedSet<First, Second>::value &&
                                       IsSortedSet<Second, Rest...>::value> {};

template <IDBSpecialValue First, IDBSpecialValue Second>
struct IsSortedSet<First, Second>
    : std::integral_constant<bool, (First < Second)> {};

template <IDBSpecialValue First>
struct IsSortedSet<First> : std::true_type {};

template <>
struct IsSortedSet<> : std::true_type {};

template <IDBSpecialValue... S>
class IDBError {
  // This assertion ensures that permutations of the set of possible special
  // values don't create distinct types.
  static_assert(IsSortedSet<S...>::value,
                "special value list must be sorted and unique");

  template <IDBSpecialValue... U>
  friend class IDBError;

 public:
  MOZ_IMPLICIT IDBError(nsresult aRv) : mVariant(ErrorResult{aRv}) {}

  IDBError(ExceptionType, ErrorResult&& aErrorResult)
      : mVariant(std::move(aErrorResult)) {}

  template <IDBSpecialValue Special>
  MOZ_IMPLICIT IDBError(SpecialConstant<Special>)
      : mVariant(SpecialConstant<Special>{}) {}

  IDBError(IDBError&&) = default;
  IDBError& operator=(IDBError&&) = default;

  // Construct an IDBResult from another IDBResult whose set of possible special
  // values is a subset of this one's.
  template <IDBSpecialValue... U>
  MOZ_IMPLICIT IDBError(IDBError<U...>&& aOther)
      : mVariant(aOther.mVariant.match(
            [](auto& aVariant) { return VariantType{std::move(aVariant)}; })) {}

  bool Is(ExceptionType) const { return mVariant.template is<ErrorResult>(); }

  template <IDBSpecialValue Special>
  bool Is(SpecialConstant<Special>) const {
    return mVariant.template is<SpecialConstant<Special>>();
  }

  ErrorResult& AsException() { return mVariant.template as<ErrorResult>(); }

  template <typename... SpecialValueMappers>
  ErrorResult ExtractErrorResult(SpecialValueMappers... aSpecialValueMappers) {
#if defined(__clang__) || (defined(__GNUC__) && __GNUC__ >= 8)
    return mVariant.match(
        [](ErrorResult& aException) { return std::move(aException); },
        [aSpecialValueMappers](const SpecialConstant<S>& aSpecialValue) {
          return ErrorResult{aSpecialValueMappers(aSpecialValue)};
        }...);
#else
    // gcc 7 doesn't accept the kind of parameter pack expansion above,
    // probably due to https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47226
    return mVariant.match([aSpecialValueMappers...](auto& aValue) {
      if constexpr (std::is_same_v<ErrorResult&, decltype(aValue)>) {
        return std::move(aValue);
      } else {
        return ErrorResult{aSpecialValueMappers(aValue)...};
      }
    });
#endif
  }

  template <typename... SpecialValueMappers>
  nsresult ExtractNSResult(SpecialValueMappers... aSpecialValueMappers) {
    return mVariant.match(
        [](ErrorResult& aException) { return aException.StealNSResult(); },
        aSpecialValueMappers...);
  }

 protected:
  using VariantType = Variant<ErrorResult, SpecialConstant<S>...>;

  VariantType mVariant;
};
}  // namespace detail

// Represents a return value of an IndexedDB algorithm. T is the type of the
// regular return value, while S is a list of special values that can be
// returned by the particular algorithm.
template <typename T, IDBSpecialValue... S>
using IDBResult = Result<T, detail::IDBError<S...>>;

template <nsresult E>
nsresult InvalidMapsTo(const indexedDB::detail::InvalidType&) {
  return E;
}

inline detail::IDBError<> IDBException(nsresult aRv) {
  return {SpecialValues::Exception, ErrorResult{aRv}};
}

template <IDBSpecialValue Special>
detail::IDBError<Special> IDBError(detail::SpecialConstant<Special> aResult) {
  return {aResult};
}

}  // namespace mozilla::dom::indexedDB

#endif  // mozilla_dom_indexeddb_idbresult_h__