/* 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 #include #include #include namespace mozilla { namespace dom { namespace 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 struct OkType final { T mValue; }; template <> struct OkType final {}; template using SpecialConstant = std::integral_constant; using FailureType = SpecialConstant; using InvalidType = SpecialConstant; struct ExceptionType final {}; struct VoidType final {}; } // namespace detail template constexpr inline detail::OkType> Ok(T&& aValue) { return {std::forward(aValue)}; } constexpr inline detail::OkType Ok() { return {}; } // 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 template class MOZ_MUST_USE_TYPE IDBResult; namespace detail { template struct IsSortedSet; template struct IsSortedSet : std::integral_constant::value && IsSortedSet::value> {}; template struct IsSortedSet : std::integral_constant {}; template struct IsSortedSet : std::true_type {}; template <> struct IsSortedSet<> : std::true_type {}; // IDBResultBase contains the bulk of the implementation of IDBResult, namely // functionality that's applicable to all values of T. template class IDBResultBase { // This assertion ensures that permutations of the set of possible special // values don't create distinct types. static_assert(IsSortedSet::value, "special value list must be sorted and unique"); template friend class IDBResultBase; protected: using ValueType = OkType; public: // Construct a normal result. Use the Ok function to create an object of type // ValueType. MOZ_IMPLICIT IDBResultBase(const ValueType& aValue) : mVariant(aValue) {} MOZ_IMPLICIT IDBResultBase(ValueType&& aValue) : mVariant(std::move(aValue)) {} MOZ_IMPLICIT IDBResultBase(ExceptionType, ErrorResult&& aErrorResult) : mVariant(std::move(aErrorResult)) {} template MOZ_IMPLICIT IDBResultBase(SpecialConstant) : mVariant(SpecialConstant{}) {} IDBResultBase(IDBResultBase&&) = default; IDBResultBase& operator=(IDBResultBase&&) = default; // Construct an IDBResult from another IDBResult whose set of possible special // values is a subset of this one's. template MOZ_IMPLICIT IDBResultBase(IDBResultBase&& aOther) : mVariant(aOther.mVariant.match( [](auto& aVariant) { return VariantType{std::move(aVariant)}; })) {} // Test whether the result is a normal return value. The choice of the first // parameter's type makes it possible to write `result.Is(Ok, rv)`, promoting // readability and uniformity with other functions in the overload set. bool Is(OkType (*)()) const { return mVariant.template is(); } bool Is(ExceptionType) const { return mVariant.template is(); } template bool Is(SpecialConstant) const { return mVariant.template is>(); } ErrorResult& AsException() { return mVariant.template as(); } template ErrorResult ExtractErrorResult(SpecialValueMappers... aSpecialValueMappers) { return mVariant.match( [](const ValueType&) -> ErrorResult { MOZ_CRASH("non-value expected"); }, [](ErrorResult& aException) { return std::move(aException); }, aSpecialValueMappers...); } template auto PropagateNotOk(); protected: using VariantType = Variant...>; 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 class MOZ_MUST_USE_TYPE IDBResult : public detail::IDBResultBase { public: using IDBResult::IDBResultBase::IDBResultBase; // Move the regular return value, asserting that this object // is indeed a regular return value. T Unwrap() { return std::move( this->mVariant.template as().mValue); } // Get a reference to the regular return value, asserting that this object // is indeed a regular return value. const T& Inspect() const { return this->mVariant.template as().mValue; } }; template class MOZ_MUST_USE_TYPE IDBResult : public detail::IDBResultBase { public: using IDBResult::IDBResultBase::IDBResultBase; }; template ErrorResult InvalidMapsTo(const indexedDB::detail::InvalidType&) { return ErrorResult{E}; } namespace detail { template template auto IDBResultBase::PropagateNotOk() { using ResultType = IDBResult; MOZ_ASSERT(!Is(Ok)); return mVariant.match( #if defined(__clang__) || (defined(__GNUC__) && __GNUC__ >= 8) [](const ValueType&) -> ResultType { MOZ_CRASH("non-value expected"); }, [](ErrorResult& aException) -> ResultType { return {SpecialValues::Exception, std::move(aException)}; }, [](SpecialConstant aSpecialValue) -> ResultType { return 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 [](auto& aParam) -> ResultType { if constexpr (std::is_same_v) { MOZ_CRASH("non-value expected"); } else if constexpr (std::is_same_v) { return {SpecialValues::Exception, std::move(aParam)}; } else { return aParam; } } #endif ); } } // namespace detail } // namespace indexedDB } // namespace dom } // namespace mozilla #endif // mozilla_dom_indexeddb_idbresult_h__