| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| */ |
| |
| #pragma once |
| |
| #include <fmt/format.h> |
| |
| #include <algorithm> |
| #include <memory> |
| #include <string> |
| #include <type_traits> |
| #include <utility> |
| |
| #include "logging.h" |
| #include "rocksdb/status.h" |
| #include "type_util.h" |
| |
| class [[nodiscard]] Status { |
| public: |
| enum Code : unsigned char { |
| NotOK = 1, |
| NotFound, |
| NotSupported, |
| InvalidArgument, |
| |
| // DB |
| DBOpenErr, |
| DBBackupErr, |
| DBGetWALErr, |
| |
| // Redis |
| RedisUnknownCmd, |
| RedisInvalidCmd, |
| RedisParseErr, |
| RedisExecErr, |
| RedisErrorNoPrefix, |
| RedisNoProto, |
| RedisLoading, |
| RedisMasterDown, |
| RedisNoScript, |
| RedisNoAuth, |
| RedisWrongType, |
| RedisReadOnly, |
| RedisExecAbort, |
| RedisBusyGroup, |
| RedisNoGroup, |
| RedisMoved, |
| RedisCrossSlot, |
| RedisTryAgain, |
| RedisClusterDown, |
| |
| // Cluster |
| ClusterInvalidInfo, |
| |
| // Blocking |
| BlockingCmd, |
| |
| // Search |
| NoPrefixMatched, |
| TypeMismatched, |
| |
| // IO |
| TryAgain, |
| EndOfFile, |
| }; |
| |
| Status() : impl_{nullptr} {} |
| |
| Status(Code code, std::string msg = {}) : impl_{new Impl{code, std::move(msg)}} { // NOLINT |
| CHECK(code != cOK); |
| } |
| |
| Status(const Status& s) : impl_{s.impl_ ? new Impl{s.impl_->code, s.impl_->msg} : nullptr} {} |
| Status(Status&&) = default; |
| |
| Status& operator=(const Status& s) { |
| Status tmp = s; |
| return *this = std::move(tmp); |
| } |
| Status& operator=(Status&& s) = default; |
| |
| ~Status() = default; |
| |
| template <Code code> |
| bool Is() const { |
| return impl_ && impl_->code == code; |
| } |
| |
| bool IsOK() const { return !impl_; } |
| explicit operator bool() const { return IsOK(); } |
| |
| Code GetCode() const { return impl_ ? impl_->code : cOK; } |
| |
| std::string Msg() const& { |
| if (*this) return ok_msg; |
| return impl_->msg; |
| } |
| |
| std::string Msg() && { |
| if (*this) return ok_msg; |
| return std::move(impl_->msg); |
| } |
| |
| static Status OK() { return {}; } |
| |
| static Status FromErrno() { return {NotOK, strerror(errno)}; } |
| static Status FromErrno(std::string_view prefix) { return {NotOK, fmt::format("{}: {}", prefix, strerror(errno))}; } |
| |
| Status Prefixed(std::string_view prefix) const { |
| if (*this) { |
| return {}; |
| } |
| return {impl_->code, fmt::format("{}: {}", prefix, impl_->msg)}; |
| } |
| |
| void GetValue() {} |
| |
| static constexpr const char* ok_msg = "ok"; |
| |
| private: |
| struct Impl { |
| Code code; |
| std::string msg; |
| }; |
| |
| std::unique_ptr<Impl> impl_; |
| |
| static constexpr Code cOK = static_cast<Code>(0); // NOLINT |
| |
| template <typename> |
| friend struct StatusOr; |
| }; |
| |
| template <typename, typename = void> |
| struct StringInStatusOr : private std::string { |
| using BaseType = std::string; |
| static constexpr bool inplace = true; |
| |
| explicit StringInStatusOr(std::string&& v) : BaseType(std::move(v)) {} |
| |
| template <typename U> |
| StringInStatusOr(StringInStatusOr<U>&& v) : BaseType(*std::move(v)) {} // NOLINT |
| StringInStatusOr(const StringInStatusOr& v) = delete; |
| |
| StringInStatusOr& operator=(const StringInStatusOr&) = delete; |
| |
| std::string& operator*() & { return *this; } |
| |
| const std::string& operator*() const& { return *this; } |
| |
| std::string&& operator*() && { return std::move(*this); } |
| |
| ~StringInStatusOr() = default; |
| }; |
| |
| template <typename T> |
| struct StringInStatusOr<T, std::enable_if_t<sizeof(T) < sizeof(std::string)>> : private std::unique_ptr<std::string> { |
| using BaseType = std::unique_ptr<std::string>; |
| static constexpr bool inplace = false; |
| |
| explicit StringInStatusOr(std::string&& v) : BaseType(new std::string(std::move(v))) {} |
| |
| template <typename U, typename std::enable_if_t<StringInStatusOr<U>::inplace, int> = 0> |
| StringInStatusOr(StringInStatusOr<U>&& v) : BaseType(new std::string(*std::move(v))) {} // NOLINT |
| template <typename U, typename std::enable_if_t<!StringInStatusOr<U>::inplace, int> = 0> |
| StringInStatusOr(StringInStatusOr<U>&& v) // NOLINT |
| : BaseType((typename StringInStatusOr<U>::BaseType&&)(std::move(v))) {} |
| |
| StringInStatusOr(const StringInStatusOr& v) = delete; |
| |
| StringInStatusOr& operator=(const StringInStatusOr&) = delete; |
| |
| std::string& operator*() & { return BaseType::operator*(); } |
| |
| const std::string& operator*() const& { return BaseType::operator*(); } |
| |
| std::string&& operator*() && { return std::move(BaseType::operator*()); } |
| |
| ~StringInStatusOr() = default; |
| }; |
| |
| template <typename> |
| struct StatusOr; |
| |
| template <typename> |
| struct IsStatusOr : std::integral_constant<bool, false> {}; |
| |
| template <typename T> |
| struct IsStatusOr<StatusOr<T>> : std::integral_constant<bool, true> {}; |
| |
| template <typename T> |
| struct [[nodiscard]] StatusOr { |
| static_assert(!std::is_same_v<T, Status>, "ValueType cannot be Status"); |
| static_assert(!std::is_same_v<T, Status::Code>, "ValueType cannot be Status::Code"); |
| static_assert(!IsStatusOr<T>::value, "ValueType cannot be StatusOr"); |
| static_assert(!std::is_reference_v<T>, "ValueType cannot be reference"); |
| |
| using ValueType = T; |
| |
| // we use std::unique_ptr to make the error part as small as enough |
| using ErrorType = StringInStatusOr<ValueType>; |
| |
| using Code = Status::Code; |
| |
| StatusOr(Status s) : code_(s.GetCode()) { // NOLINT |
| CHECK(!s); |
| new (&error) ErrorType(std::move(s.impl_->msg)); |
| } |
| |
| StatusOr(Code code, std::string msg = {}) : code_(code) { // NOLINT |
| CHECK(code != Status::cOK); |
| new (&error) ErrorType(std::move(msg)); |
| } |
| |
| template <typename... Ts, |
| typename std::enable_if<(sizeof...(Ts) > 0 && !std::is_same_v<Status, RemoveCVRef<FirstElement<Ts...>>> && |
| !std::is_same_v<Code, RemoveCVRef<FirstElement<Ts...>>> && |
| !std::is_same_v<StatusOr, RemoveCVRef<FirstElement<Ts...>>>), |
| int>::type = 0> // NOLINT |
| StatusOr(Ts&&... args) : code_(Status::cOK) { // NOLINT |
| new (&value) ValueType(std::forward<Ts>(args)...); |
| } |
| |
| StatusOr(const StatusOr&) = delete; |
| |
| template <typename U, typename std::enable_if<std::is_convertible<U, T>::value, int>::type = 0> |
| StatusOr(StatusOr<U>&& other) : code_(other.code_) { // NOLINT |
| if (code_ == Status::cOK) { |
| new (&value) ValueType(std::move(other.value)); |
| } else { |
| new (&error) ErrorType(std::move(other.error)); |
| } |
| } |
| |
| template <typename U, typename std::enable_if<!std::is_convertible<U, T>::value, int>::type = 0> |
| StatusOr(StatusOr<U>&& other) : code_(other.code_) { // NOLINT |
| CHECK(code_ != Status::cOK); |
| new (&error) ErrorType(std::move(other.error)); |
| } |
| |
| StatusOr& operator=(const StatusOr&) = delete; |
| |
| template <Code code> |
| bool Is() const { |
| return code_ == code; |
| } |
| |
| bool IsOK() const { return Is<Status::cOK>(); } |
| explicit operator bool() const { return IsOK(); } |
| |
| Status ToStatus() const& { |
| if (*this) return Status::OK(); |
| return {code_, *error}; |
| } |
| |
| Status ToStatus() && { |
| if (*this) return Status::OK(); |
| return {code_, std::move(*error)}; |
| } |
| |
| operator Status() const& { return ToStatus(); } // NOLINT |
| |
| operator Status() && { return std::move(*this).ToStatus(); } // NOLINT |
| |
| Code GetCode() const { return code_; } |
| |
| ValueType& GetValue() & { |
| CHECK(*this); |
| return value; |
| } |
| |
| ValueType&& GetValue() && { |
| CHECK(*this); |
| return std::move(value); |
| } |
| |
| const ValueType& ValueOr(const ValueType& v) const& { |
| if (IsOK()) { |
| return GetValue(); |
| } else { |
| return v; |
| } |
| } |
| |
| ValueType ValueOr(ValueType&& v) const& { |
| if (IsOK()) { |
| return GetValue(); |
| } else { |
| return std::move(v); |
| } |
| } |
| |
| ValueType ValueOr(const T& v) && { |
| if (IsOK()) { |
| return std::move(*this).GetValue(); |
| } else { |
| return v; |
| } |
| } |
| |
| ValueType&& ValueOr(T&& v) && { |
| if (IsOK()) { |
| return std::move(*this).GetValue(); |
| } else { |
| return std::move(v); |
| } |
| } |
| |
| const ValueType& GetValue() const& { |
| CHECK(*this); |
| return value; |
| } |
| |
| ValueType& operator*() & { return GetValue(); } |
| |
| ValueType&& operator*() && { return std::move(GetValue()); } |
| |
| const ValueType& operator*() const& { return GetValue(); } |
| |
| ValueType* operator->() { return &GetValue(); } |
| |
| const ValueType* operator->() const { return &GetValue(); } |
| |
| std::string Msg() const& { |
| if (*this) return Status::ok_msg; |
| return *error; |
| } |
| |
| std::string Msg() && { |
| if (*this) return Status::ok_msg; |
| return std::move(*error); |
| } |
| |
| StatusOr Prefixed(std::string_view prefix) && { |
| if (*this) { |
| return std::move(*this); |
| } |
| return {code_, fmt::format("{}: {}", prefix, std::move(*error))}; |
| } |
| |
| ~StatusOr() { |
| if (*this) { |
| value.~ValueType(); |
| } else { |
| error.~ErrorType(); |
| } |
| } |
| |
| private: |
| Status::Code code_; |
| union { |
| ValueType value; |
| ErrorType error; |
| }; |
| |
| template <typename> |
| friend struct StatusOr; |
| }; |
| |
| template <typename T, |
| std::enable_if_t<IsStatusOr<RemoveCVRef<T>>::value || std::is_same_v<RemoveCVRef<T>, Status>, int> = 0> |
| decltype(auto) StatusGetValue(T&& v) { |
| return std::forward<T>(v).GetValue(); |
| } |
| |
| template <typename T, std::enable_if_t<std::is_same_v<RemoveCVRef<T>, rocksdb::Status>, int> = 0> |
| void StatusGetValue(T&&) {} |
| |
| template <typename T, |
| std::enable_if_t<IsStatusOr<RemoveCVRef<T>>::value || std::is_same_v<RemoveCVRef<T>, Status>, int> = 0> |
| bool StatusIsOK(const T& v) { |
| return v.IsOK(); |
| } |
| |
| template <typename T, std::enable_if_t<std::is_same_v<RemoveCVRef<T>, rocksdb::Status>, int> = 0> |
| bool StatusIsOK(const T& v) { |
| return v.ok(); |
| } |
| |
| // NOLINTNEXTLINE |
| #define GET_OR_RET(...) \ |
| StatusGetValue(({ \ |
| auto&& status = (__VA_ARGS__); \ |
| if (!StatusIsOK(status)) return std::forward<decltype(status)>(status); \ |
| std::forward<decltype(status)>(status); \ |
| })) |