--- /dev/null
+/*
+ Copyright (c) 2018-2019 Nokia.
+
+ Licensed 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.
+*/
+
+#include <sstream>
+#include "private/createlogger.hpp"
+#include "private/error.hpp"
+
+using namespace shareddatalayer;
+using namespace shareddatalayer::redis;
+
+namespace
+{
+ /* Error codes under this category are not set directly. All SDL implementation specific error codes can be
+ * mapped to these error codes. These error codes are further on mapped to error codes which are available to
+ * SDL clients (shareddatalayer::Error category).
+ * We could directly map implementation specific error codes to client error codes but having this intermediate
+ * mapping gives some benefits:
+ * - We can easily provide more error categories for clients (e.g. severity, etc.) than just client error code
+ * classification if needed.
+ * - We can implement SDL internal error handling logic based on these internal error codes if needed.
+ * - If implementation specific error would be directly mapped to client error codes, mapping implementation would
+ * easily be quite complicated (especially if the amount of implementation specific errors/error categories increases).
+ */
+ class InternalErrorCategory : public std::error_category
+ {
+ public:
+ InternalErrorCategory() = default;
+ const char* name() const noexcept override;
+ std::string message(int condition) const override;
+ };
+
+ const char* InternalErrorCategory::name() const noexcept
+ {
+ return "SDL-internal-errorcodes";
+ }
+
+ std::string InternalErrorCategory::message(int) const
+ {
+ return "Only for SDL internal usage.";
+ }
+
+ const std::error_category& getInternalErrorCategory() noexcept
+ {
+ static const InternalErrorCategory theInternalErrorCategory;
+ return theInternalErrorCategory;
+ }
+
+ /* This error category is used by both AsyncHiredisCommandDispatcher and AsyncHiredisClusterCommandDispatcher,
+ * thus it is defined here. Error categories related to single class are defined in the files of the corresponding class.
+ * AsyncHiredisCommandDispatcher and AsyncHiredisClusterCommandDispatcher can use common error category as error
+ * handling is identical in those two classes. Also, only one of those classes is always used at a time (depending
+ * on deployment).
+ */
+ class AsyncRedisCommandDispatcherErrorCategory: public std::error_category
+ {
+ public:
+ AsyncRedisCommandDispatcherErrorCategory() = default;
+
+ const char* name() const noexcept override;
+
+ std::string message(int condition) const override;
+
+ std::error_condition default_error_condition(int condition) const noexcept override;
+ };
+
+ const char* AsyncRedisCommandDispatcherErrorCategory::name() const noexcept
+ {
+ /* As the correct dispacther is selected during runtime, we do not known here (without additional implementation)
+ * which dispatcher (redis/rediscluster) is currently in use. At least for now, we do not indicate in error
+ * category name the exact dispacther type but return the same name for all dispatchers.
+ * Main reason for this decision was that in error investigation situations we anyway need to have some other efficient
+ * way to figure out what kind of deployment was used as there can be error situations which do not generate any error
+ * code. Thus it was not seen worth the errort to implement correct dispatcher name display to error category name.
+ * Detailed dispacther name display can be added later if a need for that arises.
+ */
+ return "asyncrediscommanddispatcher";
+ }
+
+ std::string AsyncRedisCommandDispatcherErrorCategory::message(int condition) const
+ {
+ switch (static_cast<AsyncRedisCommandDispatcherErrorCode>(condition))
+ {
+ case AsyncRedisCommandDispatcherErrorCode::SUCCESS:
+ return std::error_code().message();
+ case AsyncRedisCommandDispatcherErrorCode::CONNECTION_LOST:
+ return "redis connection lost";
+ case AsyncRedisCommandDispatcherErrorCode::PROTOCOL_ERROR:
+ return "redis protocol error";
+ case AsyncRedisCommandDispatcherErrorCode::OUT_OF_MEMORY:
+ return "redis out of memory";
+ case AsyncRedisCommandDispatcherErrorCode::DATASET_LOADING:
+ return "redis dataset still being loaded into memory";
+ case AsyncRedisCommandDispatcherErrorCode::NOT_CONNECTED:
+ return "not connected to redis, SDL operation not started";
+ case AsyncRedisCommandDispatcherErrorCode::UNKNOWN_ERROR:
+ return "redis error";
+ case AsyncRedisCommandDispatcherErrorCode::IO_ERROR:
+ return "redis I/O error";
+ case AsyncRedisCommandDispatcherErrorCode::END_MARKER:
+ logErrorOnce("AsyncRedisCommandDispatcherErrorCode::END_MARKER is not meant to be queried (it is only for enum loop control)");
+ return "unsupported error code for message()";
+ default:
+ return "description missing for AsyncRedisCommandDispatcherErrorCategory error: " + std::to_string(condition);
+ }
+ }
+
+ std::error_condition AsyncRedisCommandDispatcherErrorCategory::default_error_condition(int condition) const noexcept
+ {
+ switch (static_cast<AsyncRedisCommandDispatcherErrorCode>(condition))
+ {
+ case AsyncRedisCommandDispatcherErrorCode::SUCCESS:
+ return InternalError::SUCCESS;
+ case AsyncRedisCommandDispatcherErrorCode::CONNECTION_LOST:
+ return InternalError::BACKEND_CONNECTION_LOST;
+ case AsyncRedisCommandDispatcherErrorCode::PROTOCOL_ERROR:
+ return InternalError::BACKEND_REJECTED_REQUEST;
+ case AsyncRedisCommandDispatcherErrorCode::OUT_OF_MEMORY:
+ return InternalError::BACKEND_ERROR;
+ case AsyncRedisCommandDispatcherErrorCode::DATASET_LOADING:
+ return InternalError::BACKEND_NOT_READY;
+ case AsyncRedisCommandDispatcherErrorCode::NOT_CONNECTED:
+ return InternalError::SDL_NOT_CONNECTED_TO_BACKEND;
+ case AsyncRedisCommandDispatcherErrorCode::UNKNOWN_ERROR:
+ return InternalError::BACKEND_ERROR;
+ case AsyncRedisCommandDispatcherErrorCode::IO_ERROR:
+ return InternalError::BACKEND_ERROR;
+ case AsyncRedisCommandDispatcherErrorCode::END_MARKER:
+ logErrorOnce("AsyncRedisCommandDispatcherErrorCode::END_MARKER is not meant to be mapped to InternalError (it is only for enum loop control)");
+ return InternalError::SDL_ERROR_CODE_LOGIC_ERROR;
+ default:
+ std::ostringstream msg;
+ msg << "default error condition missing for AsyncRedisCommandDispatcherErrorCategory error: "
+ << condition;
+ logErrorOnce(msg.str());
+ return InternalError::SDL_ERROR_CODE_LOGIC_ERROR;
+ }
+ }
+
+ const std::error_category& getAsyncRedisCommandDispatcherErrorCategory() noexcept
+ {
+ static const AsyncRedisCommandDispatcherErrorCategory theAsyncRedisCommandDispatcherErrorCategory;
+ return theAsyncRedisCommandDispatcherErrorCategory;
+ }
+}
+
+namespace shareddatalayer
+{
+ std::error_condition make_error_condition(InternalError errorCode)
+ {
+ return {static_cast<int>(errorCode), getInternalErrorCategory()};
+ }
+
+ namespace redis
+ {
+ std::error_code make_error_code(AsyncRedisCommandDispatcherErrorCode errorCode)
+ {
+ return std::error_code(static_cast<int>(errorCode), getAsyncRedisCommandDispatcherErrorCategory());
+ }
+
+ AsyncRedisCommandDispatcherErrorCode& operator++ (AsyncRedisCommandDispatcherErrorCode& ecEnum)
+ {
+ if (ecEnum == AsyncRedisCommandDispatcherErrorCode::END_MARKER)
+ throw std::out_of_range("for AsyncRedisCommandDispatcherErrorCode& operator ++");
+
+ ecEnum = AsyncRedisCommandDispatcherErrorCode(static_cast<std::underlying_type<AsyncRedisCommandDispatcherErrorCode>::type>(ecEnum) + 1);
+ return ecEnum;
+ }
+ }
+}