From e5010408d3a62e0bc073dc71f77b0b864551ba0e Mon Sep 17 00:00:00 2001 From: Daniel <d.hornung@indiscale.com> Date: Wed, 18 Aug 2021 17:10:44 +0200 Subject: [PATCH] ENH: New functions getEnumNameFromValue() and getEnumValueFromName() --- include/caosdb/data_type.h | 15 +++++- include/caosdb/entity.h | 30 +++++++++--- include/caosdb/utility.h | 52 ++++++++++++++++++++- src/CMakeLists.txt | 1 + src/caosdb/utility.cpp | 95 ++++++++++++++++++++++++++++++++++++++ test/test_utility.cpp | 27 +++++++++++ 6 files changed, 209 insertions(+), 11 deletions(-) create mode 100644 src/caosdb/utility.cpp diff --git a/include/caosdb/data_type.h b/include/caosdb/data_type.h index 7afc694..ad5d35a 100644 --- a/include/caosdb/data_type.h +++ b/include/caosdb/data_type.h @@ -46,9 +46,9 @@ class Entity; class Property; // Atomic data types. -enum AtomicDataType { +enum class AtomicDataType { // The data type is unset/unknown. - UNSPECIFIED_DATA_TYPE = ProtoAtomicDataType::ATOMIC_DATA_TYPE_UNSPECIFIED, + UNSPECIFIED = ProtoAtomicDataType::ATOMIC_DATA_TYPE_UNSPECIFIED, // TEXT data type. TEXT = ProtoAtomicDataType::ATOMIC_DATA_TYPE_TEXT, // DOUBLE data type. @@ -61,6 +61,17 @@ enum AtomicDataType { BOOLEAN = ProtoAtomicDataType::ATOMIC_DATA_TYPE_BOOLEAN, }; +const std::map<AtomicDataType, std::string> atomicdatatype_names = + { + {AtomicDataType::UNSPECIFIED, "UNSPECIFIED"}, + {AtomicDataType::TEXT, "TEXT"}, + {AtomicDataType::DOUBLE, "DOUBLE"}, + {AtomicDataType::DATETIME, "DATETIME"}, + {AtomicDataType::INTEGER, "INTEGER"}, + {AtomicDataType::BOOLEAN, "BOOLEAN"} + }; + + class DataType; class ListDataType; diff --git a/include/caosdb/entity.h b/include/caosdb/entity.h index b66a0d2..0734a3c 100644 --- a/include/caosdb/entity.h +++ b/include/caosdb/entity.h @@ -73,27 +73,43 @@ static const std::string logger_name = "caosdb::entity"; /** * The property importance. */ -enum Importance { - IMPORTANCE_UNSPECIFIED = - ProtoImportance::IMPORTANCE_UNSPECIFIED, ///< Unset/None +enum class Importance { + UNSPECIFIED = + ProtoImportance::IMPORTANCE_UNSPECIFIED, ///< Unset/None OBLIGATORY = - ProtoImportance::IMPORTANCE_OBLIGATORY, ///< Obligatory importance. + ProtoImportance::IMPORTANCE_OBLIGATORY, ///< Obligatory importance. RECOMMENDED = - ProtoImportance::IMPORTANCE_RECOMMENDED, ///< Recommended importance. + ProtoImportance::IMPORTANCE_RECOMMENDED, ///< Recommended importance. SUGGESTED = ProtoImportance::IMPORTANCE_SUGGESTED, ///< Suggested importance. FIX = ProtoImportance::IMPORTANCE_FIX, ///< Fix importance. }; +const std::map<Importance, std::string> importance_names = + { + {Importance::UNSPECIFIED, "UNSPECIFIED"}, + {Importance::OBLIGATORY, "OBLIGATORY"}, + {Importance::RECOMMENDED, "RECOMMENDED"}, + {Importance::SUGGESTED, "SUGGESTED"}, + {Importance::FIX, "FIX"} + }; /** * The entity role. */ -enum Role { - ROLE_UNSPECIFIED = EntityRole::ENTITY_ROLE_UNSPECIFIED, ///< Unset/None +enum class Role { + UNSPECIFIED = EntityRole::ENTITY_ROLE_UNSPECIFIED, ///< Unset/None RECORD_TYPE = EntityRole::ENTITY_ROLE_RECORD_TYPE, ///< RecordType RECORD = EntityRole::ENTITY_ROLE_RECORD, ///< Record PROPERTY = EntityRole::ENTITY_ROLE_PROPERTY, ///< Property FILE = EntityRole::ENTITY_ROLE_FILE, ///< File }; +const std::map<Role, std::string> role_names = + { + {Role::UNSPECIFIED, "UNSPECIFIED"}, + {Role::RECORD_TYPE, "RECORD_TYPE"}, + {Role::RECORD, "RECORD"}, + {Role::PROPERTY, "PROPERTY"}, + {Role::FILE, "FILE"} + }; struct FileDescriptor { FileTransmissionId *file_transmission_id; diff --git a/include/caosdb/utility.h b/include/caosdb/utility.h index dd53ff9..0bc4ff1 100644 --- a/include/caosdb/utility.h +++ b/include/caosdb/utility.h @@ -21,20 +21,25 @@ #ifndef CAOSDB_UTILS_H #define CAOSDB_UTILS_H +#include "caosdb/entity.h" +#include "caosdb/data_type.h" #include <boost/beast/core/detail/base64.hpp> #include <boost/filesystem.hpp> #include <boost/filesystem/fstream.hpp> #include <boost/filesystem/string_file.hpp> +#include <boost/lexical_cast.hpp> // for lexical_cast #include <boost/json.hpp> #include <cassert> #include <cstdlib> +#include <exception> // for logic_error #include <fstream> #include <iostream> +#include <map> #include <memory> -#include <string> -#include <string_view> #include <mutex> #include <shared_mutex> +#include <string> +#include <string_view> namespace caosdb::utility { using boost::filesystem::exists; @@ -43,6 +48,49 @@ using boost::filesystem::path; using boost::json::stream_parser; using boost::json::value; +/** + * @brief Get the name of the enum value. May be useful for higher-order CaosDB clients. + */ +template <typename Enum> +auto getEnumNameFromValue(Enum v) -> std::string { + if (std::is_same_v<std::underlying_type_t<Enum>, int >) { + return boost::lexical_cast<std::string>(static_cast<int>(v)); + } + throw std::logic_error(std::string("Enum type ") + typeid(v).name() + " not implemented."); +} + +// Forward declaration of specializations +template <> +auto getEnumNameFromValue<caosdb::entity::AtomicDataType>(caosdb::entity::AtomicDataType v) + -> std::string; +template <> +auto getEnumNameFromValue<caosdb::entity::Importance>(caosdb::entity::Importance v) + -> std::string; +template <> +auto getEnumNameFromValue<caosdb::entity::Role>(caosdb::entity::Role v) + -> std::string; + +/** + * @brief Get the enum value from a string. + * + * @detail May be useful for higher-order CaosDB clients and only makes sense if specialized. + */ +template <typename Enum> +auto getEnumValueFromName(const std::string &name) -> Enum { + throw std::logic_error(std::string("Enum type ") + typeid(Enum).name() + " not implemented."); +} + +// Forward declaration of specializations +template <> +auto getEnumValueFromName<caosdb::entity::AtomicDataType>(const std::string &name) + -> caosdb::entity::AtomicDataType; +template <> +auto getEnumValueFromName<caosdb::entity::Importance>(const std::string &name) + -> caosdb::entity::Importance; +template <> +auto getEnumValueFromName<caosdb::entity::Role>(const std::string &name) + -> caosdb::entity::Role; + /** * @brief Read a text file into a string and return the file's content. */ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 89180ee..cb00898 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -29,6 +29,7 @@ set(libcaosdb_SRC ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/protobuf_helper.cpp ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/transaction.cpp ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/transaction_handler.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/utility.cpp ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/unary_rpc_handler.cpp ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/file_transmission/register_file_upload_handler.cpp ${CMAKE_CURRENT_SOURCE_DIR}/caosdb/file_transmission/upload_request_handler.cpp diff --git a/src/caosdb/utility.cpp b/src/caosdb/utility.cpp new file mode 100644 index 0000000..243e85e --- /dev/null +++ b/src/caosdb/utility.cpp @@ -0,0 +1,95 @@ +/* + * This file is a part of the CaosDB Project. + * + * Copyright (C) 2021 Daniel Hornung <d.hornung@indiscale.com> + * Copyright (C) 2021 IndiScale GmbH <info@indiscale.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + * + */ +#include "caosdb/data_type.h" +#include "caosdb/entity.h" +#include "caosdb/utility.h" +#include <algorithm> + +namespace caosdb::utility { + +using caosdb::entity::AtomicDataType; +using caosdb::entity::Importance; +using caosdb::entity::Role; + + +// using emap = std::map<int, std::string>; // enum mapping + +// Enum helper template specializations ////////////////////////////////////// +template <> +auto getEnumNameFromValue<Importance>(Importance v) -> std::string { + auto result = caosdb::entity::importance_names.at(v); + return result; +} + +template <> +auto getEnumNameFromValue<Role>(Role v) -> std::string { + auto result = caosdb::entity::role_names.at(v); + return result; +} + +template <> +auto getEnumNameFromValue<AtomicDataType>(AtomicDataType v) -> std::string { + auto result = caosdb::entity::atomicdatatype_names.at(v); + return result; +} + +template <> +auto getEnumValueFromName<Importance>(const std::string &name) -> Importance { + // TODO (dh): Why does this compile? + // if (caosdb::entity::importance_names.begin()->second == name) {} + // std::for_each(caosdb::entity::importance_names.begin(), + // caosdb::entity::importance_names.end(), + // [](const auto &entry){}); + // TODO (dh): Whereas this does not? + // auto result = std::find(caosdb::entity::importance_names.cbegin(), + // caosdb::entity::importance_names.cend(), + // [name](const auto& entry){ return entry.second == name; }); + // Workaround: plaint old iteration: + for (auto entry: caosdb::entity::importance_names) { + if (entry.second == name) { + return entry.first; + } + } + throw std::out_of_range(std::string("Could not find enum value for string '") + name + "'."); +} + +template <> +auto getEnumValueFromName<AtomicDataType>(const std::string &name) -> AtomicDataType { + for (auto entry: caosdb::entity::atomicdatatype_names) { + if (entry.second == name) { + return entry.first; + } + } + throw std::out_of_range(std::string("Could not find enum value for string '") + name + "'."); +} +template <> +auto getEnumValueFromName<Role>(const std::string &name) -> Role { + for (auto entry: caosdb::entity::role_names) { + if (entry.second == name) { + return entry.first; + } + } + throw std::out_of_range(std::string("Could not find enum value for string '") + name + "'."); +} + +// End of template specialization ///////////////////////////////////////////// + +} // namespace caosdb::utility diff --git a/test/test_utility.cpp b/test/test_utility.cpp index cb6021d..3d30263 100644 --- a/test/test_utility.cpp +++ b/test/test_utility.cpp @@ -24,6 +24,8 @@ #include "boost/beast/core/detail/base64.hpp" // for encoded_size #include "boost/json/object.hpp" // for object #include "boost/json/value.hpp" // for value +#include "caosdb/entity.h" // for importance_names, role... +#include "caosdb/data_type.h" // for atomicdatatype_names #include "caosdb/utility.h" // for base64_encode, load_js... #include "caosdb_test_utility.h" // for TEST_DATA_DIR #include <gtest/gtest-message.h> // for Message @@ -53,4 +55,29 @@ TEST(test_utility, test_load_json_file) { EXPECT_THAT(sub["see?"].as_array(), ElementsAre(true, false)); } +TEST(test_utility, enum_names) { + // All working enums + for (auto entry : caosdb::entity::importance_names) { + EXPECT_EQ(getEnumNameFromValue<caosdb::entity::Importance>(entry.first), entry.second); + EXPECT_EQ(getEnumValueFromName<caosdb::entity::Importance>(entry.second), entry.first); + } + for (auto entry : caosdb::entity::role_names) { + EXPECT_EQ(getEnumNameFromValue<caosdb::entity::Role>(entry.first), entry.second); + EXPECT_EQ(getEnumValueFromName<caosdb::entity::Role>(entry.second), entry.first); + } + for (auto entry : caosdb::entity::atomicdatatype_names) { + EXPECT_EQ(getEnumNameFromValue<caosdb::entity::AtomicDataType>(entry.first), entry.second); + EXPECT_EQ(getEnumValueFromName<caosdb::entity::AtomicDataType>(entry.second), entry.first); + } + + // Some non-working examples + EXPECT_THROW_MESSAGE(getEnumValueFromName<caosdb::entity::Importance>("Invalid name"), + std::out_of_range, + "Could not find enum value for string 'Invalid name'."); + enum e1 {a}; + EXPECT_THROW_STARTS_WITH(getEnumNameFromValue<e1>(a), std::logic_error, "Enum type "); + EXPECT_THROW_STARTS_WITH(getEnumValueFromName<e1>("Hello!"), std::logic_error, "Enum type "); +} + + } // namespace caosdb::utility -- GitLab