Add first version
[ric-plt/sdl.git] / src / error.cpp
diff --git a/src/error.cpp b/src/error.cpp
new file mode 100644 (file)
index 0000000..347e651
--- /dev/null
@@ -0,0 +1,182 @@
+/*
+   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;
+        }
+    }
+}