diff --git a/.gitignore b/.gitignore index 5ed97f792ec8e3f95437d94e572a2d16c0b39725..7ddf9b7e3c77a0cc492c738c47b5a97a6b7e7997 100644 --- a/.gitignore +++ b/.gitignore @@ -145,3 +145,12 @@ flycheck_*.el # Python/Sphinx env/ + +# Conan build files +build_tools +conan.lock +conan_imports_manifest.txt +conanbuildinfo.cmake +conanbuildinfo.txt +conaninfo.txt +graph_info.json diff --git a/Makefile b/Makefile index a024d31675b8ca1e3c1ec9967b0e5e3045872f25..367c0de989e9fa373b7a01afffc2720a5834508d 100644 --- a/Makefile +++ b/Makefile @@ -23,6 +23,23 @@ CLANG_FORMAT ?= clang-format-11 +CONAN_SETTINGS = "compiler.libcxx=libstdc++11" + +# OS specific handling, with code by Ken Jackson and oHo, +# from https://stackoverflow.com/a/52062069/232888 and +# https://stackoverflow.com/a/14777895/232888 +ifeq '$(findstring ;,$(PATH))' ';' + DETECTED_OS := Windows +else + DETECTED_OS := $(shell uname 2>/dev/null || echo Unknown) + DETECTED_OS := $(patsubst CYGWIN%,Cygwin,$(DETECTED_OS)) + DETECTED_OS := $(patsubst MSYS%,MSYS,$(DETECTED_OS)) + DETECTED_OS := $(patsubst MINGW%,MSYS,$(DETECTED_OS)) +endif +ifeq ($(DETECTED_OS),Darwin) # Test if we are on MacOS + CONAN_SETTINGS := "cppstd=11" +endif + .PHONY: help help: @@ -36,11 +53,13 @@ style: .PHONY: style conan-install: - conan install . -s "compiler.libcxx=libstdc++11" + conan install . -s $(CONAN_SETTINGS) || \ + (echo "'conan install' failed, trying to build from sources..."; \ + conan install . -s $(CONAN_SETTINGS) --build=missing) .PHONY: conan-install conan-create: - conan create . -s "compiler.libcxx=libstdc++11" + conan create . -s $(CONAN_SETTINGS) .PHONY: conan-create conan: conan-install conan-create diff --git a/doc/README_SETUP.md b/doc/README_SETUP.md index a5976af95de78a01e6ae174e39cc6a66881c28ac..46026eee1da7ea078f93687812833f957bd75312 100644 --- a/doc/README_SETUP.md +++ b/doc/README_SETUP.md @@ -34,6 +34,11 @@ have to add `build/lib/` (or, alternatively after installation, `CMAKE_INSTALL_PREFIX/lib`) to your `DYLD_LIBRARY_PATH` environmental variable. +#### Problems and solutions #### + +- Make sure that your conan version supports your XCode version. A typical symptom of version + mismatch is for example conan complaining about incompatible `compiler.version` settings. + ### How to build on Windows We use [Visual Studio 2019](https://visualstudio.microsoft.com/de/vs/features/cplusplus/) diff --git a/include/caosdb/data_type.h b/include/caosdb/data_type.h index e6b92d53d6ddb1aa8360af55f6438d23eafff7ae..42d376698872e086c05a5905484e2f63c11dd346 100644 --- a/include/caosdb/data_type.h +++ b/include/caosdb/data_type.h @@ -40,6 +40,7 @@ using ProtoReferenceDataType = caosdb::entity::v1alpha1::ReferenceDataType; using DataTypeCase = caosdb::entity::v1alpha1::DataType::DataTypeCase; using ListDataTypeCase = caosdb::entity::v1alpha1::ListDataType::ListDataTypeCase; using caosdb::utility::ProtoMessageWrapper; +using caosdb::utility::ScalarProtoMessageWrapper; class Entity; class Property; @@ -68,7 +69,7 @@ const std::map<AtomicDataType, std::string> atomicdatatype_names = { class DataType; class ListDataType; -class ReferenceDataType : public ProtoMessageWrapper<ProtoDataType> { +class ReferenceDataType : public ScalarProtoMessageWrapper<ProtoDataType> { public: [[nodiscard]] inline auto GetName() const noexcept -> const std::string & { // is list of reference? @@ -91,11 +92,11 @@ protected: inline static auto Create(ProtoDataType *wrapped) -> std::unique_ptr<ReferenceDataType> { return std::unique_ptr<ReferenceDataType>(new ReferenceDataType(wrapped)); } - ReferenceDataType() : ProtoMessageWrapper<ProtoDataType>() {} - ReferenceDataType(ProtoDataType *wrapped) : ProtoMessageWrapper<ProtoDataType>(wrapped) {} + ReferenceDataType() : ScalarProtoMessageWrapper<ProtoDataType>() {} + ReferenceDataType(ProtoDataType *wrapped) : ScalarProtoMessageWrapper<ProtoDataType>(wrapped) {} }; -class ListDataType : public ProtoMessageWrapper<ProtoDataType> { +class ListDataType : public ScalarProtoMessageWrapper<ProtoDataType> { public: [[nodiscard]] inline auto IsListOfReference() const noexcept -> bool { return this->wrapped->list_data_type().list_data_type_case() == @@ -130,21 +131,22 @@ protected: inline static auto Create(ProtoDataType *wrapped) -> std::unique_ptr<ListDataType> { return std::unique_ptr<ListDataType>(new ListDataType(wrapped)); } - ListDataType() : ProtoMessageWrapper<ProtoDataType>() {} + ListDataType() : ScalarProtoMessageWrapper<ProtoDataType>() {} - ListDataType(ProtoDataType *wrapped) : ProtoMessageWrapper<ProtoDataType>(wrapped) {} + ListDataType(ProtoDataType *wrapped) : ScalarProtoMessageWrapper<ProtoDataType>(wrapped) {} mutable std::unique_ptr<ReferenceDataType> reference_data_type; }; -class DataType : public ProtoMessageWrapper<ProtoDataType> { +class DataType : public ScalarProtoMessageWrapper<ProtoDataType> { public: - DataType(ProtoDataType *wrapped) : ProtoMessageWrapper<ProtoDataType>(wrapped) {} - DataType() : ProtoMessageWrapper<ProtoDataType>() {} + DataType(ProtoDataType *wrapped) : ScalarProtoMessageWrapper<ProtoDataType>(wrapped) {} + DataType() : ScalarProtoMessageWrapper<ProtoDataType>(static_cast<ProtoDataType *>(nullptr)) {} /** * Create an AtomicDataType typed DataType. For references, use the std::string constructor. */ - DataType(AtomicDataType data_type, bool list_type = false) : DataType() { + DataType(AtomicDataType data_type, bool list_type = false) + : ScalarProtoMessageWrapper<ProtoDataType>() { if (list_type) { this->wrapped->mutable_list_data_type()->set_atomic_data_type( static_cast<ProtoAtomicDataType>(data_type)); @@ -155,7 +157,8 @@ public: /** * Create a reference typed DataType. */ - DataType(const std::string &data_type, bool list_type = false) : DataType() { + DataType(const std::string &data_type, bool list_type = false) + : ScalarProtoMessageWrapper<ProtoDataType>() { if (list_type) { this->wrapped->mutable_list_data_type()->mutable_reference_data_type()->set_name(data_type); } else { @@ -164,27 +167,28 @@ public: } inline static auto ListOf(const AtomicDataType &atomic_data_type) -> DataType { - DataType result; - result.wrapped->mutable_list_data_type()->set_atomic_data_type( - static_cast<ProtoAtomicDataType>(atomic_data_type)); - return result; + return DataType(atomic_data_type, true); } inline static auto ListOf(const std::string reference_data_type) -> DataType { - DataType result; - result.wrapped->mutable_list_data_type()->mutable_reference_data_type()->set_name( - reference_data_type); - return result; + return DataType(reference_data_type, true); } + [[nodiscard]] inline auto IsUndefined() const noexcept -> bool { + return this->wrapped == nullptr; + } [[nodiscard]] inline auto IsAtomic() const noexcept -> bool { - return this->wrapped->data_type_case() == DataTypeCase::kAtomicDataType; + return !IsUndefined() && this->wrapped->data_type_case() == DataTypeCase::kAtomicDataType; } [[nodiscard]] inline auto GetAsAtomic() const noexcept -> AtomicDataType { + if (!IsAtomic()) { + static AtomicDataType undefined; + return undefined; + } return static_cast<AtomicDataType>(this->wrapped->atomic_data_type()); } [[nodiscard]] inline auto IsReference() const noexcept -> bool { - return this->wrapped->data_type_case() == DataTypeCase::kReferenceDataType; + return !IsUndefined() && this->wrapped->data_type_case() == DataTypeCase::kReferenceDataType; } [[nodiscard]] inline auto GetAsReference() const noexcept -> const ReferenceDataType & { if (!IsReference()) { @@ -197,7 +201,7 @@ public: } [[nodiscard]] inline auto IsList() const noexcept -> bool { - return this->wrapped->data_type_case() == DataTypeCase::kListDataType; + return !IsUndefined() && this->wrapped->data_type_case() == DataTypeCase::kListDataType; } [[nodiscard]] inline auto GetAsList() const noexcept -> const ListDataType & { @@ -209,9 +213,18 @@ public: return *list_data_type; } + /** + * Return true if `other` is equal to this object. + * + * This compares the underlying wrapped ProtoMessages and return true if they + * are both nullptrs or if the serialization is equal. + */ inline auto operator==(const DataType &other) const noexcept -> bool { - // TODO(tf) Is this safe? - return this->wrapped->SerializeAsString() == other.wrapped->SerializeAsString(); + if (this->wrapped != nullptr && other.wrapped != nullptr) { + return this->wrapped->SerializeAsString() == other.wrapped->SerializeAsString(); + } + // both nullptr? + return this->wrapped == other.wrapped; } friend class Entity; diff --git a/include/caosdb/entity.h b/include/caosdb/entity.h index de6d4928771af83edca68d748d73b58f690a35f7..6de939c37f19d9419815dcb8b7001fd19050133f 100644 --- a/include/caosdb/entity.h +++ b/include/caosdb/entity.h @@ -45,7 +45,6 @@ #include <boost/preprocessor/seq/limits/size_256.hpp> // for BOOST_PP_SEQ_... #include <cstdint> // for int64_t #include <google/protobuf/message.h> // for RepeatedPtrField -#include <google/protobuf/util/json_util.h> // for MessageToJson... #include <iosfwd> // for streamsize #include <iterator> // for iterator, output_iterato... #include <map> // for map @@ -53,6 +52,7 @@ #include <stdexcept> // for out_of_range #include <string> // for string, basic... #include <vector> // for vector +#include <utility> // for move namespace caosdb::entity { using boost::filesystem::exists; @@ -63,12 +63,15 @@ using ProtoProperty = caosdb::entity::v1alpha1::Property; using ProtoEntity = caosdb::entity::v1alpha1::Entity; using ProtoFileDescriptor = caosdb::entity::v1alpha1::FileDescriptor; using ProtoMessage = caosdb::entity::v1alpha1::Message; +using ProtoValue = caosdb::entity::v1alpha1::Value; +using ProtoDataType = caosdb::entity::v1alpha1::DataType; using caosdb::entity::v1alpha1::EntityRole; using ProtoImportance = caosdb::entity::v1alpha1::Importance; using caosdb::StatusCode; using caosdb::entity::v1alpha1::EntityResponse; using caosdb::entity::v1alpha1::FileTransmissionId; using caosdb::utility::get_arena; +using caosdb::utility::ProtoMessageWrapper; using ::google::protobuf::RepeatedPtrField; using google::protobuf::RepeatedPtrField; @@ -117,15 +120,18 @@ struct FileDescriptor { * Abstract base class for Messages, Properties and Parents container classes. * * This is a list-like class. + * + * This class wraps a RepeatedPtrField. */ -template <typename T, typename P> class RepeatedPtrFieldWrapper { +template <typename T, typename P> +class RepeatedPtrFieldWrapper : public ProtoMessageWrapper<RepeatedPtrField<P>> { class iterator; public: /** * Return the current size of the container. */ - [[nodiscard]] inline auto size() const -> int { return wrapped->size(); } + [[nodiscard]] inline auto size() const -> int { return this->wrapped->size(); } /** * Return a const reference to the element at the given index. */ @@ -139,10 +145,11 @@ public: throw std::out_of_range("Container has size " + std::to_string(size())); } if (cache.count(index) == 0) { - cache.emplace(index, T(&(wrapped->at(index)))); + cache.emplace(index, T(this->wrapped->Mutable(index))); } return &(cache.at(index)); } + /** * Return iterator positioned at the beginning of the list. */ @@ -164,10 +171,26 @@ public: virtual ~RepeatedPtrFieldWrapper(){}; + inline auto ToString() const noexcept -> const std::string override { + if (this->size() == 0) { + return "[]\n"; + } + std::string result("[\n"); + for (int i = 0; i < this->size();) { + CAOSDB_DEBUG_MESSAGE_STRING(this->wrapped->at(i), next); + result += next; + if (++i < this->size()) { + result.replace(result.size() - 1, 1, ",\n"); + } + } + + return result.append(std::string("]\n")); + } + protected: - RepeatedPtrFieldWrapper(){}; - explicit inline RepeatedPtrFieldWrapper(::google::protobuf::RepeatedPtrField<P> *wrapped) - : wrapped(wrapped){}; + RepeatedPtrFieldWrapper() : ProtoMessageWrapper<RepeatedPtrField<P>>(){}; + explicit inline RepeatedPtrFieldWrapper(RepeatedPtrField<P> *wrapped) + : ProtoMessageWrapper<RepeatedPtrField<P>>(wrapped) {} /** * Append an element. This adds the element to the end of the wrapped list @@ -175,13 +198,7 @@ protected: */ inline auto Append(const T &element) -> void { auto *destination = this->wrapped->Add(); - destination->Swap(element.wrapped); - - // Clear the originally wrapped object and return it to the Arena - element.wrapped->Clear(); - - // set the pointer to the new object which is owned by the RepeatedPtrField - element.wrapped = destination; + destination->CopyFrom(*element.wrapped); } /** @@ -202,11 +219,12 @@ protected: cache.insert(std::move(handle)); } } + // this is possible because cache is an ordered map + cache.erase(cache.find(size()), cache.end()); } inline auto Clear() noexcept -> void { this->wrapped->Clear(); } - ::google::protobuf::RepeatedPtrField<P> *wrapped; mutable std::map<int, T> cache; private: @@ -282,7 +300,7 @@ auto RepeatedPtrFieldWrapper<T, P>::end() const -> const RepeatedPtrFieldWrapper * A Message object can be thought of as kinf of a generalized error object in * other frameworks. Please have a look at MessageCodes for more details. */ -class Message { +class Message : public ScalarProtoMessageWrapper<ProtoMessage> { public: /** * Get the code of this message. @@ -310,9 +328,8 @@ public: friend class RepeatedPtrFieldWrapper<Message, ProtoMessage>; private: - explicit inline Message(ProtoMessage *wrapped) : wrapped(wrapped){}; - - ProtoMessage *wrapped; + explicit inline Message(ProtoMessage *wrapped) + : ScalarProtoMessageWrapper<ProtoMessage>(wrapped){}; }; /** @@ -338,10 +355,10 @@ private: * words, this class wraps a protobuf message and provides getter and setter * methods. */ -class Parent { +class Parent : public ScalarProtoMessageWrapper<ProtoParent> { public: - explicit inline Parent(ProtoParent *wrapped) : wrapped(wrapped){}; - Parent(); + explicit inline Parent(ProtoParent *wrapped) : ScalarProtoMessageWrapper<ProtoParent>(wrapped){}; + Parent() : ScalarProtoMessageWrapper<ProtoParent>(){}; /** * Return the id of the parent entity. @@ -365,19 +382,6 @@ public: */ auto SetName(const std::string &name) -> void; - /** - * Return a json string representing this parent. - * - * This is intended for debugging. - */ - inline auto ToString() const -> const std::string { - google::protobuf::util::JsonPrintOptions options; - options.add_whitespace = true; - std::string out; - google::protobuf::util::MessageToJsonString(*(this->wrapped), &out, options); - return out; - } - // TODO(fspreck) Finish the following implementations once we have // decided how to attach messages to parents. // /** @@ -410,22 +414,6 @@ public: friend class RepeatedPtrFieldWrapper<Parent, ProtoParent>; private: - /** - * Return an empty protobuf message pointer. - * - * This function is called by the default constructor of the - * caosdb::entity::Parent class and the protobuf message is used as the - * storage-backend for the new Parent instance. - * - * An 'Arena' takes care of the the memory management. Don't try to delete - * this. - */ - static auto CreateProtoParent() -> ProtoParent *; - - /** - * Message which serves as storage backend. - */ - mutable ProtoParent *wrapped; // Messages errors; // Messages warnings; // Messages infos; @@ -454,12 +442,40 @@ private: * This is a property which belongs to another entity. Don't confuse it with * an Entity with the "Property" role. */ -class Property { +class Property : public ScalarProtoMessageWrapper<ProtoProperty> { public: + /** + * Copy constructor. + */ + inline Property(const Property &other) + : Property(ProtoMessageWrapper<ProtoProperty>::CopyProtoMessage(other.wrapped)) { + CAOSDB_LOG_TRACE(logger_name) << "Property::Property(const Property &) " + << "- Copy constructor"; + }; + + /** + * Move constructor. + */ + inline Property(Property &&other) : Property() { + CAOSDB_LOG_TRACE(logger_name) << "Property::Property(Property &&) " + << "- Move constructor"; + this->wrapped = std::move(other.wrapped); + this->value.wrapped = this->wrapped->mutable_value(); + this->data_type.wrapped = this->wrapped->mutable_data_type(); + + other.data_type.wrapped = nullptr; + other.value.wrapped = nullptr; + } + explicit inline Property(ProtoProperty *other) - : value(Value(other->mutable_value())), data_type(DataType(other->mutable_data_type())), - wrapped(other){}; - Property(); + : ScalarProtoMessageWrapper<ProtoProperty>(other), + value(this->wrapped->has_value() ? this->wrapped->mutable_value() + : static_cast<ProtoValue *>(nullptr)), + data_type(this->wrapped->has_data_type() ? this->wrapped->mutable_data_type() + : static_cast<ProtoDataType *>(nullptr)){}; + inline Property() + : ScalarProtoMessageWrapper<ProtoProperty>(), value(static_cast<ProtoValue *>(nullptr)), + data_type(static_cast<ProtoDataType *>(nullptr)){}; /** * Return the id of this property @@ -541,16 +557,27 @@ public: auto SetDataType(const std::string &new_data_type, bool list_type = false) -> StatusCode; /** - * Return a json string representing this property. - * - * This is intended for debugging - */ - inline auto ToString() const -> const std::string { - google::protobuf::util::JsonPrintOptions options; - options.add_whitespace = true; - std::string out; - google::protobuf::util::MessageToJsonString(*(this->wrapped), &out, options); - return out; + * Copy assignment operator. + */ + auto operator=(const Property &other) -> Property & { + CAOSDB_LOG_TRACE(logger_name) << "Property::operator=(const Property &) " + << "- Copy assignment operator"; + this->wrapped->CopyFrom(*other.wrapped); + this->value = Value(this->wrapped->mutable_value()); + this->data_type = DataType(this->wrapped->mutable_data_type()); + return *this; + } + + /** + * Move assignment operator. + */ + auto operator=(Property &&other) -> Property & { + CAOSDB_LOG_TRACE(logger_name) << "Property::operator=(Property &&) " + << "- Move assignment operator"; + this->wrapped = std::move(other.wrapped); + this->value = std::move(other.value); + this->data_type = std::move(other.data_type); + return *this; } friend class Entity; @@ -558,11 +585,8 @@ public: friend class RepeatedPtrFieldWrapper<Property, ProtoProperty>; private: - static auto CreateProtoProperty() -> ProtoProperty *; Value value; DataType data_type; - - mutable ProtoProperty *wrapped; }; /** @@ -580,16 +604,15 @@ private: * for (auto &property : my_properties) {...} * \endcode */ -class Properties : public RepeatedPtrFieldWrapper<Property, caosdb::entity::v1alpha1::Property> { +class Properties : public RepeatedPtrFieldWrapper<Property, ProtoProperty> { public: ~Properties() = default; friend class Entity; private: inline Properties(){}; - explicit inline Properties( - ::google::protobuf::RepeatedPtrField<caosdb::entity::v1alpha1::Property> *wrapped) - : RepeatedPtrFieldWrapper<Property, caosdb::entity::v1alpha1::Property>(wrapped){}; + explicit inline Properties(RepeatedPtrField<ProtoProperty> *wrapped) + : RepeatedPtrFieldWrapper<Property, ProtoProperty>(wrapped){}; }; /** @@ -610,23 +633,22 @@ private: * response. calls Entity(), then moves the data to the wrapped ProtoEntity. * */ -class Entity { +class Entity : public ScalarProtoMessageWrapper<ProtoEntity> { public: - inline Entity(const Entity &original) : Entity(Copy(*original.wrapped)) { + inline Entity(const Entity &original) + : Entity(ProtoMessageWrapper::CopyProtoMessage(original.wrapped)) { this->errors.wrapped->CopyFrom(*original.errors.wrapped); this->warnings.wrapped->CopyFrom(*original.warnings.wrapped); this->infos.wrapped->CopyFrom(*original.infos.wrapped); }; explicit Entity(ProtoEntity *other) - : wrapped(other), value(Value(other->mutable_value())), - data_type(DataType(other->mutable_data_type())) { - data_type.wrapped = this->wrapped->mutable_data_type(); - value.wrapped = this->wrapped->mutable_value(); + : ScalarProtoMessageWrapper<ProtoEntity>(other), + value(this->wrapped->has_value() ? this->wrapped->mutable_value() + : static_cast<ProtoValue *>(nullptr)), + data_type(this->wrapped->has_data_type() ? this->wrapped->mutable_data_type() + : static_cast<ProtoDataType *>(nullptr)) { properties.wrapped = this->wrapped->mutable_properties(); parents.wrapped = this->wrapped->mutable_parents(); - errors.wrapped = CreateMessagesField(); - warnings.wrapped = CreateMessagesField(); - infos.wrapped = CreateMessagesField(); }; explicit inline Entity(EntityResponse *response) : Entity(response->release_entity()) { this->errors.wrapped->Swap(response->mutable_errors()); @@ -642,22 +664,35 @@ public: this->infos.wrapped->Swap(id_response->mutable_infos()); }; - explicit inline Entity() : Entity(Entity::CreateProtoEntity()){}; + explicit inline Entity() + : ScalarProtoMessageWrapper<ProtoEntity>(), value(static_cast<ProtoValue *>(nullptr)), + data_type(static_cast<ProtoDataType *>(nullptr)) { + properties.wrapped = this->wrapped->mutable_properties(); + parents.wrapped = this->wrapped->mutable_parents(); + }; - [[nodiscard]] inline auto GetId() const noexcept -> const std::string & { return wrapped->id(); }; - [[nodiscard]] inline auto HasId() const noexcept -> bool { return !wrapped->id().empty(); } + [[nodiscard]] inline auto GetId() const noexcept -> const std::string & { + return this->wrapped->id(); + }; + [[nodiscard]] inline auto HasId() const noexcept -> bool { return !this->wrapped->id().empty(); } [[nodiscard]] inline auto GetVersionId() const -> const std::string & { - return wrapped->version().id(); + return this->wrapped->version().id(); }; - [[nodiscard]] inline auto GetRole() const -> Role { return static_cast<Role>(wrapped->role()); }; - [[nodiscard]] inline auto GetName() const -> const std::string & { return wrapped->name(); }; + [[nodiscard]] inline auto GetRole() const -> Role { + return static_cast<Role>(this->wrapped->role()); + }; + [[nodiscard]] inline auto GetName() const -> const std::string & { + return this->wrapped->name(); + }; [[nodiscard]] inline auto GetDescription() const -> const std::string & { - return wrapped->description(); + return this->wrapped->description(); }; [[nodiscard]] inline auto GetDataType() const -> const DataType & { return this->data_type; }; - [[nodiscard]] inline auto GetUnit() const -> const std::string & { return wrapped->unit(); }; + [[nodiscard]] inline auto GetUnit() const -> const std::string & { + return this->wrapped->unit(); + }; [[nodiscard]] inline auto GetValue() const -> const Value & { return this->value; }; [[nodiscard]] auto GetParents() const -> const Parents &; @@ -673,14 +708,6 @@ public: [[nodiscard]] auto GetInfos() const -> const Messages & { return infos; } [[nodiscard]] inline auto HasInfos() const -> bool { return this->infos.wrapped->size() > 0; } - inline auto ToString() const -> const std::string { - google::protobuf::util::JsonPrintOptions options; - options.add_whitespace = true; - std::string out; - google::protobuf::util::MessageToJsonString(*(this->wrapped), &out, options); - return out; - } - auto SetRole(Role role) -> void; auto SetName(const std::string &name) -> void; /** @@ -761,11 +788,6 @@ public: } private: - static inline auto Copy(const ProtoEntity &from) -> ProtoEntity * { - auto to = from.New(); - to->CopyFrom(from); - return to; - } inline auto GetNextFileId() -> std::string { std::string str = "0123456789abcdef"; std::mt19937 generator(std::random_device{}()); @@ -777,14 +799,12 @@ private: } return result; } - static auto CreateProtoEntity() -> ProtoEntity *; static auto CreateMessagesField() -> RepeatedPtrField<ProtoMessage> *; auto SetId(const std::string &id) -> void; auto SetVersionId(const std::string &id) -> void; private: FileDescriptor file_descriptor; - ProtoEntity *wrapped; Properties properties; Parents parents; Messages errors; diff --git a/include/caosdb/protobuf_helper.h b/include/caosdb/protobuf_helper.h index 8574845b194f0080d2b0f1d07475c4bf1c3f82bd..4dfc11c396ded34db6bb2cbed2c938a5402dd7c7 100644 --- a/include/caosdb/protobuf_helper.h +++ b/include/caosdb/protobuf_helper.h @@ -22,9 +22,9 @@ #ifndef CAOSDB_PROTOBUF_HELPER_H #define CAOSDB_PROTOBUF_HELPER_H -#include "caosdb/status_code.h" // for StatusCode, SUCCESS -#include <google/protobuf/arena.h> // for Arena -#include <google/protobuf/extension_set.h> // for Arena +#include <google/protobuf/arena.h> // for Arena +// IWYU pragma: no_include "google/protobuf/extension_set.h" +// IWYU pragma: no_include "google/protobuf/generated_message_util.h" #include <google/protobuf/util/json_util.h> // for JsonOptions, MessageToJs... #include <string> // for string @@ -32,6 +32,7 @@ std::string out; \ { \ google::protobuf::util::JsonOptions options; \ + options.add_whitespace = true; \ google::protobuf::util::MessageToJsonString(message, &out, options); \ } @@ -41,27 +42,76 @@ using google::protobuf::Arena; auto get_arena() -> Arena *; +/** + * Abstract wrapper class for Protobuf messages. + */ template <typename P> class ProtoMessageWrapper { public: - ProtoMessageWrapper(const ProtoMessageWrapper &other) = default; - inline auto CopyFrom(const ProtoMessageWrapper &other) noexcept -> StatusCode { - this->wrapped->CopyFrom(*other.wrapped); - return StatusCode::SUCCESS; + virtual ~ProtoMessageWrapper() = 0; + /** + * Return a json representation of this object. + */ + virtual inline auto ToString() const noexcept -> const std::string = 0; + + /** + * Return true if the underlying Protobuf messages have the same + * serialization. + */ + inline auto operator==(const ProtoMessageWrapper<P> &other) const noexcept -> bool { + if (this->wrapped != nullptr && other.wrapped != nullptr) { + return this->wrapped->SerializeAsString() == other.wrapped->SerializeAsString(); + } + // both nullptr? + return this->wrapped == other.wrapped; } /** - * Return a json representation of this object. + * Return true if the underlying Protobuf messages have a different + * serialization. */ - [[nodiscard]] inline auto ToString() const noexcept -> const std::string { - CAOSDB_DEBUG_MESSAGE_STRING(*wrapped, out) - return out; + inline auto operator!=(const ProtoMessageWrapper<P> &other) const noexcept -> bool { + if (this->wrapped != nullptr && other.wrapped != nullptr) { + return this->wrapped->SerializeAsString() != other.wrapped->SerializeAsString(); + } + // only one is nullptr? + return this->wrapped != other.wrapped; } protected: + inline static auto CopyProtoMessage(P *wrapped) -> P * { + P *copy = Arena::CreateMessage<P>(get_arena()); + copy->CopyFrom(*wrapped); + return copy; + } ProtoMessageWrapper() : ProtoMessageWrapper(Arena::CreateMessage<P>(get_arena())) {} ProtoMessageWrapper(P *wrapped) : wrapped(wrapped) {} P *wrapped; }; +template <typename P> ProtoMessageWrapper<P>::~ProtoMessageWrapper() = default; + +/** + * Wrapper class for scalar Protobuf messages. + * + * Scalar means in this context, any message but classes derived from + * RepeatedPtrField. + */ +template <typename P> class ScalarProtoMessageWrapper : public ProtoMessageWrapper<P> { +public: + inline virtual ~ScalarProtoMessageWrapper() = default; + inline ScalarProtoMessageWrapper() = default; + inline ScalarProtoMessageWrapper(P *wrapped) : ProtoMessageWrapper<P>(wrapped) {} + /** + * Return a json representation of this object. + */ + inline auto ToString() const noexcept -> const std::string override { + if (this->wrapped == nullptr) { + return "{}\n"; + } + CAOSDB_DEBUG_MESSAGE_STRING(*this->wrapped, out) + return out; + } +}; + } // namespace caosdb::utility #endif diff --git a/include/caosdb/transaction.h b/include/caosdb/transaction.h index 153ee46f8173d98a13203f31cd6a140999a58689..634c7a0d01b613e4fb6ad4d56e037693d9abc2c2 100644 --- a/include/caosdb/transaction.h +++ b/include/caosdb/transaction.h @@ -35,7 +35,6 @@ #include <boost/preprocessor/seq/limits/enum_256.hpp> // for BOOST_PP_SEQ_E... #include <boost/preprocessor/seq/limits/size_256.hpp> // for BOOST_PP_SEQ_S... #include <google/protobuf/arena.h> // for Arena -#include <google/protobuf/generated_message_util.h> // for CreateMessage... #include <google/protobuf/util/json_util.h> // for MessageToJsonS... #include <grpcpp/impl/codegen/completion_queue.h> // for CompletionQueue #include <algorithm> // for max diff --git a/include/caosdb/value.h b/include/caosdb/value.h index fd70c4f837dccaeedca0c9801c2518eb111af398..8296be311a8262339b58de06d49940100fc183f3 100644 --- a/include/caosdb/value.h +++ b/include/caosdb/value.h @@ -31,7 +31,8 @@ #include <vector> // for vector #define LIST_VALUE_CONSTRUCTOR(TYPE, SETTER) \ - explicit inline Value(const std::vector<TYPE> &values) : ProtoMessageWrapper<ProtoValue>() { \ + explicit inline Value(const std::vector<TYPE> &values) \ + : ScalarProtoMessageWrapper<ProtoValue>() { \ for (const auto &value : values) { \ this->wrapped->mutable_list_values()->add_values()->SETTER(value); \ } \ @@ -40,6 +41,7 @@ namespace caosdb::entity { using caosdb::utility::get_arena; using caosdb::utility::ProtoMessageWrapper; +using caosdb::utility::ScalarProtoMessageWrapper; using google::protobuf::Arena; using ProtoSpecialValue = caosdb::entity::v1alpha1::SpecialValue; using ProtoValue = caosdb::entity::v1alpha1::Value; @@ -167,7 +169,7 @@ protected: inline AbstractValue::~AbstractValue() {} -class ScalarValue : public AbstractValue, public ProtoMessageWrapper<ProtoScalarValue> { +class ScalarValue : public AbstractValue, public ScalarProtoMessageWrapper<ProtoScalarValue> { public: /** * Destructor. @@ -203,37 +205,39 @@ public: } inline ScalarValue(ProtoScalarValue *wrapped) - : ProtoMessageWrapper<ProtoScalarValue>(wrapped), proto_value(nullptr) {} + : ScalarProtoMessageWrapper<ProtoScalarValue>(wrapped), proto_value(nullptr) {} [[nodiscard]] inline auto IsNull() const noexcept -> bool { - return (this->wrapped->scalar_value_case() == ScalarValueCase::kSpecialValue && + return this->wrapped == nullptr || + (this->wrapped->scalar_value_case() == ScalarValueCase::kSpecialValue && this->wrapped->special_value() == ProtoSpecialValue::SPECIAL_VALUE_UNSPECIFIED); } [[nodiscard]] inline auto IsString() const noexcept -> bool { - return (this->wrapped->scalar_value_case() == ScalarValueCase::kStringValue) || - (this->wrapped->scalar_value_case() == ScalarValueCase::kSpecialValue && - this->wrapped->special_value() == ProtoSpecialValue::SPECIAL_VALUE_EMPTY_STRING); + return !IsNull() && + ((this->wrapped->scalar_value_case() == ScalarValueCase::kStringValue) || + (this->wrapped->scalar_value_case() == ScalarValueCase::kSpecialValue && + this->wrapped->special_value() == ProtoSpecialValue::SPECIAL_VALUE_EMPTY_STRING)); } [[nodiscard]] inline auto GetAsString() const noexcept -> const std::string & { return this->wrapped->string_value(); } [[nodiscard]] inline auto IsDouble() const noexcept -> bool { - return (this->wrapped->scalar_value_case() == ScalarValueCase::kDoubleValue); + return !IsNull() && (this->wrapped->scalar_value_case() == ScalarValueCase::kDoubleValue); } [[nodiscard]] inline auto GetAsDouble() const noexcept -> double { return this->wrapped->double_value(); } [[nodiscard]] inline auto IsInt64() const noexcept -> bool { - return (this->wrapped->scalar_value_case() == ScalarValueCase::kIntegerValue); + return !IsNull() && (this->wrapped->scalar_value_case() == ScalarValueCase::kIntegerValue); } [[nodiscard]] inline auto GetAsInt64() const noexcept -> int64_t { return this->wrapped->integer_value(); } [[nodiscard]] inline auto IsBool() const noexcept -> bool { - return (this->wrapped->scalar_value_case() == ScalarValueCase::kBooleanValue); + return !IsNull() && (this->wrapped->scalar_value_case() == ScalarValueCase::kBooleanValue); } [[nodiscard]] inline auto GetAsBool() const noexcept -> bool { return this->wrapped->boolean_value(); @@ -248,7 +252,7 @@ public: return empty_collection; } [[nodiscard]] inline auto ToString() const noexcept -> const std::string { - return ProtoMessageWrapper::ToString(); + return ScalarProtoMessageWrapper::ToString(); } friend class Value; @@ -261,18 +265,20 @@ protected: } return this->proto_value; }; - inline ScalarValue() : ProtoMessageWrapper<ProtoScalarValue>(), proto_value(nullptr) {} + inline ScalarValue() : ScalarProtoMessageWrapper<ProtoScalarValue>(), proto_value(nullptr) {} private: mutable ProtoValue *proto_value; }; -class Value : public AbstractValue, public ProtoMessageWrapper<ProtoValue> { +class Value : public AbstractValue, public ScalarProtoMessageWrapper<ProtoValue> { public: /** * Copy constructor. */ - inline Value(const Value &original) : Value() { this->wrapped->CopyFrom(*original.wrapped); } + inline Value(const Value &original) : ScalarProtoMessageWrapper<ProtoValue>() { + this->wrapped->CopyFrom(*original.wrapped); + } /** * Move constructor. */ @@ -281,30 +287,30 @@ public: * Destructor. */ inline ~Value() {} - inline Value() : ProtoMessageWrapper<ProtoValue>() { + inline Value() : ScalarProtoMessageWrapper<ProtoValue>(static_cast<ProtoValue *>(nullptr)) { // has NULL_VALUE now } - explicit inline Value(const ScalarValue &value) : Value() { + explicit inline Value(const ScalarValue &value) : ScalarProtoMessageWrapper<ProtoValue>() { this->wrapped->mutable_scalar_value()->CopyFrom(*value.wrapped); } - explicit inline Value(const AbstractValue &value) : Value() { + explicit inline Value(const AbstractValue &value) : ScalarProtoMessageWrapper<ProtoValue>() { this->wrapped->CopyFrom(*value.GetProtoValue()); } - explicit inline Value(ProtoValue *wrapped) : ProtoMessageWrapper<ProtoValue>(wrapped) {} - explicit inline Value(const std::string &value) : ProtoMessageWrapper<ProtoValue>() { + explicit inline Value(ProtoValue *wrapped) : ScalarProtoMessageWrapper<ProtoValue>(wrapped) {} + explicit inline Value(const std::string &value) : ScalarProtoMessageWrapper<ProtoValue>() { this->wrapped->mutable_scalar_value()->set_string_value(value); } - explicit inline Value(const char *value) : ProtoMessageWrapper<ProtoValue>() { + explicit inline Value(const char *value) : ScalarProtoMessageWrapper<ProtoValue>() { this->wrapped->mutable_scalar_value()->set_string_value(std::string(value)); } - explicit inline Value(double value) : ProtoMessageWrapper<ProtoValue>() { + explicit inline Value(double value) : ScalarProtoMessageWrapper<ProtoValue>() { this->wrapped->mutable_scalar_value()->set_double_value(value); } - explicit inline Value(int64_t value) : ProtoMessageWrapper<ProtoValue>() { + explicit inline Value(int64_t value) : ScalarProtoMessageWrapper<ProtoValue>() { this->wrapped->mutable_scalar_value()->set_integer_value(value); } explicit inline Value(int value) : Value(static_cast<int64_t>(value)) {} - explicit inline Value(bool value) : ProtoMessageWrapper<ProtoValue>() { + explicit inline Value(bool value) : ScalarProtoMessageWrapper<ProtoValue>() { this->wrapped->mutable_scalar_value()->set_boolean_value(value); } @@ -316,14 +322,15 @@ public: LIST_VALUE_CONSTRUCTOR(bool, set_boolean_value) [[nodiscard]] inline auto IsNull() const noexcept -> bool { - return this->wrapped->value_case() == ValueCase::VALUE_NOT_SET || - (this->wrapped->scalar_value().scalar_value_case() == ScalarValueCase::kSpecialValue && - this->wrapped->scalar_value().special_value() == - ProtoSpecialValue::SPECIAL_VALUE_UNSPECIFIED); + return this->wrapped == nullptr || + (this->wrapped->value_case() == ValueCase::VALUE_NOT_SET || + (this->wrapped->scalar_value().scalar_value_case() == ScalarValueCase::kSpecialValue && + this->wrapped->scalar_value().special_value() == + ProtoSpecialValue::SPECIAL_VALUE_UNSPECIFIED)); } [[nodiscard]] inline auto IsString() const noexcept -> bool { - if (this->wrapped->value_case() == ValueCase::kScalarValue) { + if (!IsNull() && this->wrapped->value_case() == ValueCase::kScalarValue) { return (this->wrapped->scalar_value().scalar_value_case() == ScalarValueCase::kStringValue) || (this->wrapped->scalar_value().scalar_value_case() == ScalarValueCase::kSpecialValue && @@ -333,50 +340,60 @@ public: return false; } [[nodiscard]] inline auto GetAsString() const noexcept -> const std::string & { + if (!IsString()) { + static std::string empty_string; + return empty_string; + } return this->wrapped->scalar_value().string_value(); - ; } [[nodiscard]] inline auto IsDouble() const noexcept -> bool { - if (this->wrapped->value_case() == ValueCase::kScalarValue) { + if (!IsNull() && this->wrapped->value_case() == ValueCase::kScalarValue) { return (this->wrapped->scalar_value().scalar_value_case() == ScalarValueCase::kDoubleValue); } return false; } [[nodiscard]] inline auto GetAsDouble() const noexcept -> double { + if (!IsDouble()) + return 0.0; return this->wrapped->scalar_value().double_value(); } [[nodiscard]] inline auto IsInt64() const noexcept -> bool { - if (this->wrapped->value_case() == ValueCase::kScalarValue) { + if (!IsNull() && this->wrapped->value_case() == ValueCase::kScalarValue) { return (this->wrapped->scalar_value().scalar_value_case() == ScalarValueCase::kIntegerValue); } return false; } [[nodiscard]] inline auto GetAsInt64() const noexcept -> int64_t { + if (!IsInt64()) + return 0; return this->wrapped->scalar_value().integer_value(); } [[nodiscard]] inline auto IsBool() const noexcept -> bool { - if (this->wrapped->value_case() == ValueCase::kScalarValue) { + if (!IsNull() && this->wrapped->value_case() == ValueCase::kScalarValue) { return (this->wrapped->scalar_value().scalar_value_case() == ScalarValueCase::kBooleanValue); } return false; } [[nodiscard]] inline auto GetAsBool() const noexcept -> bool { + if (!IsBool()) + return false; return this->wrapped->scalar_value().boolean_value(); } [[nodiscard]] inline auto IsVector() const noexcept -> bool { - return this->wrapped->value_case() == ValueCase::kListValues; + return !IsNull() && this->wrapped->value_case() == ValueCase::kListValues; } [[nodiscard]] inline auto GetAsVector() const noexcept -> const std::vector<ScalarValue> & { if (!IsVector()) { // create empty list - this->collection_values = std::make_unique<std::vector<ScalarValue>>(); + static std::vector<ScalarValue> empty_values; + return empty_values; } if (this->collection_values == nullptr) { this->collection_values = std::make_unique<std::vector<ScalarValue>>(); @@ -387,20 +404,13 @@ public: return *(this->collection_values); } - /** - * Return true if the underlying Protobuf messages have the same - * serialization. - */ - inline auto operator==(const Value &other) const noexcept -> bool { - return this->wrapped->SerializeAsString() == other.wrapped->SerializeAsString(); - } - /** * Copy assignment operator. */ inline auto operator=(const Value &other) -> Value & { if (&other != this) { this->wrapped->CopyFrom(*other.wrapped); + this->collection_values.reset(); } return *this; } @@ -411,12 +421,13 @@ public: inline auto operator=(Value &&other) -> Value & { if (&other != this) { this->wrapped = std::move(other.wrapped); + this->collection_values.reset(); } return *this; } [[nodiscard]] inline auto ToString() const noexcept -> const std::string { - return ProtoMessageWrapper::ToString(); + return ScalarProtoMessageWrapper::ToString(); } friend class Entity; diff --git a/src/caosdb/entity.cpp b/src/caosdb/entity.cpp index c409a46e8ad37aee93b67b2a36109b1fe8f8c640..bb73431e4af00ed1db48b8f87fa883db6f4f9b31 100644 --- a/src/caosdb/entity.cpp +++ b/src/caosdb/entity.cpp @@ -42,18 +42,6 @@ Messages::~Messages() = default; // Parent ///////////////////////////////////////////////////////////////////// -Parent::Parent() : wrapped(Parent::CreateProtoParent()) { - // TODO(fspreck) Re-enable once we have decided how to attach - // messages to parents. - // errors.wrapped = this->wrapped->mutable_errors(); - // warnings.wrapped = this->wrapped->mutable_warnings(); - // infos.wrapped = this->wrapped->mutable_infos(); -} - -auto Parent::CreateProtoParent() -> ProtoParent * { - return Arena::CreateMessage<ProtoParent>(get_arena()); -} - auto Parent::SetName(const std::string &name) -> void { this->wrapped->set_name(name); } auto Parent::SetId(const std::string &id) -> void { this->wrapped->set_id(id); } @@ -66,11 +54,7 @@ auto Parent::SetId(const std::string &id) -> void { this->wrapped->set_id(id); } return this->wrapped->description(); } -Property::Property() : Property(Property::CreateProtoProperty()) {} - -auto Property::CreateProtoProperty() -> ProtoProperty * { - return Arena::CreateMessage<ProtoProperty>(get_arena()); -} +// Property /////////////////////////////////////////////////////////////////// [[nodiscard]] auto Property::GetId() const -> const std::string & { return this->wrapped->id(); } @@ -106,7 +90,16 @@ auto Property::SetImportance(Importance importance) -> void { this->wrapped->set_importance(static_cast<ProtoImportance>(importance)); } -auto Property::SetValue(const Value &value) -> StatusCode { return this->value.CopyFrom(value); } +auto Property::SetValue(const Value &value) -> StatusCode { + if (value.wrapped == nullptr) { + this->wrapped->clear_value(); + this->value = Value(); + } else { + this->wrapped->mutable_value()->CopyFrom(*value.wrapped); + this->value = Value(this->wrapped->mutable_value()); + } + return StatusCode::SUCCESS; +} auto Property::SetValue(const AbstractValue &value) -> StatusCode { return SetValue(Value(value)); } @@ -149,7 +142,14 @@ auto Property::SetValue(const bool value) -> StatusCode { return SetValue(Value( auto Property::SetUnit(const std::string &unit) -> void { this->wrapped->set_unit(unit); } auto Property::SetDataType(const DataType &new_data_type) -> StatusCode { - return this->data_type.CopyFrom(new_data_type); + if (new_data_type.wrapped == nullptr) { + this->wrapped->clear_data_type(); + this->data_type = DataType(); + } else { + this->wrapped->mutable_data_type()->CopyFrom(*new_data_type.wrapped); + this->data_type = DataType(this->wrapped->mutable_data_type()); + } + return StatusCode::SUCCESS; } auto Property::SetDataType(const AtomicDataType new_data_type, bool list_type) -> StatusCode { @@ -173,10 +173,6 @@ auto Entity::AppendProperty(const Property &property) -> void { this->properties auto Entity::RemoveProperty(int index) -> void { this->properties.Remove(index); } -auto Entity::CreateProtoEntity() -> ProtoEntity * { - return Arena::CreateMessage<ProtoEntity>(get_arena()); -} - auto Entity::CreateMessagesField() -> RepeatedPtrField<ProtoMessage> * { return Arena::CreateMessage<RepeatedPtrField<ProtoMessage>>(get_arena()); } @@ -201,7 +197,14 @@ auto Entity::SetValue(const Value &value) -> StatusCode { if (GetRole() != Role::PROPERTY) { return StatusCode::ENTITY_CANNOT_HAVE_A_VALUE; } - return this->value.CopyFrom(value); + if (value.wrapped == nullptr) { + this->wrapped->clear_value(); + this->value = Value(); + } else { + this->wrapped->mutable_value()->CopyFrom(*value.wrapped); + this->value = Value(this->wrapped->mutable_value()); + } + return StatusCode::SUCCESS; } auto Entity::SetValue(const AbstractValue &value) -> StatusCode { return SetValue(Value(value)); } @@ -248,7 +251,14 @@ auto Entity::SetDataType(const DataType &new_data_type) -> StatusCode { if (GetRole() != Role::PROPERTY) { return StatusCode::ENTITY_CANNOT_HAVE_A_DATA_TYPE; } - return this->data_type.CopyFrom(new_data_type); + if (new_data_type.wrapped == nullptr) { + this->wrapped->clear_data_type(); + this->data_type = DataType(); + } else { + this->wrapped->mutable_data_type()->CopyFrom(*new_data_type.wrapped); + this->data_type = DataType(this->wrapped->mutable_data_type()); + } + return StatusCode::SUCCESS; } auto Entity::SetDataType(const AtomicDataType new_data_type, bool list_type) -> StatusCode { diff --git a/src/caosdb/file_transmission/download_request_handler.cpp b/src/caosdb/file_transmission/download_request_handler.cpp index a157754b337e798fc4c144cb44abea153053ed79..cbe79e93eb89261d1d85484cf39f4ad5deecba18 100644 --- a/src/caosdb/file_transmission/download_request_handler.cpp +++ b/src/caosdb/file_transmission/download_request_handler.cpp @@ -59,17 +59,16 @@ #include <boost/preprocessor/seq/limits/size_256.hpp> // for BOOST_PP_SEQ_... #include <exception> // IWYU pragma: keep // IWYU pragma: no_include <bits/exception.h> -#include <google/protobuf/arena.h> // for Arena -#include <google/protobuf/generated_message_util.h> // for Arena -#include <grpcpp/impl/codegen/async_stream.h> // for ClientAsyncRe... -#include <grpcpp/impl/codegen/client_context.h> // for ClientContext -#include <grpcpp/impl/codegen/completion_queue.h> // for CompletionQueue -#include <grpcpp/impl/codegen/status.h> // for Status -#include <grpcpp/impl/codegen/status_code_enum.h> // for OK, UNAUTHENT... -#include <iostream> // for char_traits -#include <stdexcept> // for runtime_error -#include <string> // for string, opera... -#include <utility> // for move +#include <google/protobuf/arena.h> // for Arena +#include <grpcpp/impl/codegen/async_stream.h> // for ClientAsyncRe... +#include <grpcpp/impl/codegen/client_context.h> // for ClientContext +#include <grpcpp/impl/codegen/completion_queue.h> // for CompletionQueue +#include <grpcpp/impl/codegen/status.h> // for Status +#include <grpcpp/impl/codegen/status_code_enum.h> // for OK, UNAUTHENT... +#include <iostream> // for char_traits +#include <stdexcept> // for runtime_error +#include <string> // for string, opera... +#include <utility> // for move namespace caosdb::transaction { using caosdb::StatusCode; diff --git a/src/caosdb/file_transmission/upload_request_handler.cpp b/src/caosdb/file_transmission/upload_request_handler.cpp index cc8170249b932fb8bfa572fd345746bbbacc16af..bc74391753a0c7c3dce01f80d8fa67acc9e782cd 100644 --- a/src/caosdb/file_transmission/upload_request_handler.cpp +++ b/src/caosdb/file_transmission/upload_request_handler.cpp @@ -61,17 +61,16 @@ #include <cstdint> // for uint64_t #include <exception> // IWYU pragma: keep // IWYU pragma: no_include <bits/exception.h> -#include <google/protobuf/arena.h> // for Arena -#include <google/protobuf/generated_message_util.h> // for CreateMessage... -#include <grpcpp/impl/codegen/async_stream.h> // for ClientAsyncWr... -#include <grpcpp/impl/codegen/call_op_set.h> // for WriteOptions -#include <grpcpp/impl/codegen/client_context.h> // for ClientContext -#include <grpcpp/impl/codegen/completion_queue.h> // for CompletionQueue -#include <grpcpp/impl/codegen/status.h> // for Status -#include <grpcpp/impl/codegen/status_code_enum.h> // for OK, UNAUTHENT... -#include <iostream> // for endl, streamsize -#include <string> // for basic_string -#include <utility> // for move +#include <google/protobuf/arena.h> // for Arena +#include <grpcpp/impl/codegen/async_stream.h> // for ClientAsyncWr... +#include <grpcpp/impl/codegen/call_op_set.h> // for WriteOptions +#include <grpcpp/impl/codegen/client_context.h> // for ClientContext +#include <grpcpp/impl/codegen/completion_queue.h> // for CompletionQueue +#include <grpcpp/impl/codegen/status.h> // for Status +#include <grpcpp/impl/codegen/status_code_enum.h> // for OK, UNAUTHENT... +#include <iostream> // for endl, streamsize +#include <string> // for basic_string +#include <utility> // for move namespace caosdb::transaction { using caosdb::StatusCode; diff --git a/src/caosdb/protobuf_helper.cpp b/src/caosdb/protobuf_helper.cpp index e8bbd07834ead9b561c7e8769ed834527337f7a6..418d14b9c847bc204582f6165fae81bf6adcc156 100644 --- a/src/caosdb/protobuf_helper.cpp +++ b/src/caosdb/protobuf_helper.cpp @@ -19,8 +19,7 @@ * */ #include "caosdb/protobuf_helper.h" -#include <google/protobuf/arena.h> // for Arena -#include <google/protobuf/extension_set.h> // for Arena +#include <google/protobuf/arena.h> // for Arena namespace caosdb::utility { diff --git a/src/caosdb/transaction.cpp b/src/caosdb/transaction.cpp index 678d6fbd728cdd82158e263a476f4ce63bc19310..002d20d792b49bfb88b160cb39e7d8a8e6a5bd01 100644 --- a/src/caosdb/transaction.cpp +++ b/src/caosdb/transaction.cpp @@ -35,15 +35,14 @@ #include <boost/preprocessor/seq/limits/enum_256.hpp> // for BOOST_PP_SEQ_... #include <boost/preprocessor/seq/limits/size_256.hpp> // for BOOST_PP_SEQ_... // IWYU pragma: no_include <bits/exception.h> -#include <exception> // IWYU pragma: keep -#include <google/protobuf/arena.h> // for Arena -#include <google/protobuf/generated_message_util.h> // for CreateMessage... -#include <grpc/impl/codegen/gpr_types.h> // for gpr_timespec -#include <grpcpp/impl/codegen/completion_queue.h> // for CompletionQueue -#include <iosfwd> // for streamsize -#include <map> // for map, operator!= -#include <memory> // for unique_ptr -#include <utility> // for move, pair +#include <exception> // IWYU pragma: keep +#include <google/protobuf/arena.h> // for Arena +#include <grpc/impl/codegen/gpr_types.h> // for gpr_timespec +#include <grpcpp/impl/codegen/completion_queue.h> // for CompletionQueue +#include <iosfwd> // for streamsize +#include <map> // for map, operator!= +#include <memory> // for unique_ptr +#include <utility> // for move, pair namespace caosdb::transaction { using caosdb::entity::v1alpha1::EntityTransactionService; diff --git a/test/test_data_type.cpp b/test/test_data_type.cpp index 51624a7e0513f4654f2eaa9fdf3519399726199d..78e5100f3ce984ece46918df4aca9ffb8f1d2522 100644 --- a/test/test_data_type.cpp +++ b/test/test_data_type.cpp @@ -117,4 +117,16 @@ TEST(test_data_type, test_list_of_reference) { EXPECT_EQ(list_data_type.GetReferenceDataType().GetName(), "person"); } +TEST(test_data_type, test_data_type_to_string) { + DataType data_type1; + EXPECT_EQ(data_type1.ToString(), "{}\n"); + + ProtoDataType proto_data_type; + DataType data_type2(&proto_data_type); + EXPECT_EQ(data_type2.ToString(), "{}\n"); + + DataType data_type3(AtomicDataType::INTEGER); + EXPECT_EQ(data_type3.ToString(), "{\n \"atomicDataType\": \"ATOMIC_DATA_TYPE_INTEGER\"\n}\n"); +} + } // namespace caosdb::entity diff --git a/test/test_entity.cpp b/test/test_entity.cpp index 75adc7e7a051cc1e2a154d6b3723e44123e28130..de0206f8b547884b7ae438b3bd4873230b9128d8 100644 --- a/test/test_entity.cpp +++ b/test/test_entity.cpp @@ -38,6 +38,7 @@ #include <memory> // for allocator, shared_ptr #include <stdexcept> // for out_of_range #include <string> // for operator+, to_string +#include <utility> // for move namespace caosdb::entity { using caosdb::entity::v1alpha1::IdResponse; @@ -132,11 +133,88 @@ TEST(test_entity, test_append_property) { EXPECT_EQ(prop.GetName(), same_prop.GetName()); EXPECT_EQ(prop.GetId(), same_prop.GetId()); EXPECT_EQ(prop.GetImportance(), same_prop.GetImportance()); - EXPECT_EQ(prop.GetValue(), same_prop.GetValue()); + EXPECT_EQ(prop.GetValue().ToString(), same_prop.GetValue().ToString()); EXPECT_EQ(prop.GetUnit(), same_prop.GetUnit()); EXPECT_EQ(prop.GetDataType(), same_prop.GetDataType()); } +TEST(test_entity, test_property_copy_constructor) { + Property prop; + prop.SetName("prop_name"); + prop.SetId("prop_id"); + prop.SetImportance(Importance::RECOMMENDED); + prop.SetValue("prop_value"); + prop.SetUnit("prop_unit"); + prop.SetDataType("prop_dtype"); + + Property other_prop(prop); + + EXPECT_EQ(prop, other_prop); + EXPECT_EQ(prop.GetName(), other_prop.GetName()); + EXPECT_EQ(prop.GetId(), other_prop.GetId()); + EXPECT_EQ(prop.GetImportance(), other_prop.GetImportance()); + EXPECT_EQ(prop.GetValue().ToString(), other_prop.GetValue().ToString()); + EXPECT_EQ(prop.GetUnit(), other_prop.GetUnit()); + EXPECT_EQ(prop.GetDataType(), other_prop.GetDataType()); +} + +TEST(test_entity, test_property_copy_assignment) { + Property prop; + prop.SetName("prop_name"); + prop.SetId("prop_id"); + prop.SetImportance(Importance::RECOMMENDED); + prop.SetValue("prop_value"); + prop.SetUnit("prop_unit"); + prop.SetDataType("prop_dtype"); + + auto other_prop = prop; + EXPECT_EQ(prop, other_prop); + EXPECT_EQ(prop.GetName(), other_prop.GetName()); + EXPECT_EQ(prop.GetId(), other_prop.GetId()); + EXPECT_EQ(prop.GetImportance(), other_prop.GetImportance()); + EXPECT_EQ(prop.GetValue().ToString(), other_prop.GetValue().ToString()); + EXPECT_EQ(prop.GetUnit(), other_prop.GetUnit()); + EXPECT_EQ(prop.GetDataType(), other_prop.GetDataType()); + + other_prop.SetName("other_prop_name"); + EXPECT_NE(prop, other_prop); + EXPECT_NE(prop.GetName(), other_prop.GetName()); + EXPECT_EQ(prop.GetName(), "prop_name"); + EXPECT_EQ(other_prop.GetName(), "other_prop_name"); +} + +TEST(test_entity, test_property_move_assignment) { + Property prop; + prop.SetName("prop_name"); + prop.SetId("prop_id"); + prop.SetImportance(Importance::RECOMMENDED); + prop.SetValue("prop_value"); + prop.SetUnit("prop_unit"); + prop.SetDataType("prop_dtype"); + + // we compare the moved one with this one + const Property copy_prop(prop); + + Property other_prop = std::move(prop); + // EXPECT_NE(prop, copy_prop); NOLINT + // EXPECT_NE(prop, other_prop); NOLINT + // EXPECT_EQ(prop.ToString(), "{}"); NOLINT + + EXPECT_EQ(copy_prop, other_prop); + EXPECT_EQ(copy_prop.GetName(), other_prop.GetName()); + EXPECT_EQ(copy_prop.GetId(), other_prop.GetId()); + EXPECT_EQ(copy_prop.GetImportance(), other_prop.GetImportance()); + EXPECT_EQ(copy_prop.GetValue().ToString(), other_prop.GetValue().ToString()); + EXPECT_EQ(copy_prop.GetUnit(), other_prop.GetUnit()); + EXPECT_EQ(copy_prop.GetDataType(), other_prop.GetDataType()); + + other_prop.SetName("other_prop_name"); + EXPECT_NE(copy_prop, other_prop); + EXPECT_NE(copy_prop.GetName(), other_prop.GetName()); + EXPECT_EQ(copy_prop.GetName(), "prop_name"); + EXPECT_EQ(other_prop.GetName(), "other_prop_name"); +} + TEST(test_entity, test_copy_to) { auto entity = Entity(); entity.SetRole(Role::RECORD); @@ -363,12 +441,39 @@ TEST(test_entity, test_remove_property) { // P0,P1,P2, P4,P5,P6,P7,P8,P9 entity.RemoveProperty(3); + ASSERT_EQ(entity.GetProperties().size(), 9); + for (int i = 0; i < 3; i++) { + auto name = "PROPERTY-" + std::to_string(i); + const auto &property = entity.GetProperties().at(i); + EXPECT_EQ(property.GetName(), name); + } + for (int i = 3; i < 9; i++) { + auto name = "PROPERTY-" + std::to_string(i + 1); + const auto &property = entity.GetProperties().at(i); + EXPECT_EQ(property.GetName(), name); + } + // Remove at index 6 // P0,P1,P2, P4,P5,P6,P7,P8,P9 // ^ // P0,P1,P2, P4,P5,P6, P8,P9 entity.RemoveProperty(6); ASSERT_EQ(entity.GetProperties().size(), 8); + for (int i = 0; i < 3; i++) { + auto name = "PROPERTY-" + std::to_string(i); + const auto &property = entity.GetProperties().at(i); + EXPECT_EQ(property.GetName(), name); + } + for (int i = 3; i < 6; i++) { + auto name = "PROPERTY-" + std::to_string(i + 1); + const auto &property = entity.GetProperties().at(i); + EXPECT_EQ(property.GetName(), name); + } + for (int i = 6; i < 8; i++) { + auto name = "PROPERTY-" + std::to_string(i + 2); + const auto &property = entity.GetProperties().at(i); + EXPECT_EQ(property.GetName(), name); + } // AppendProperty another property // P0,P1,P2, P4,P5,P6, P8,P9 @@ -460,4 +565,91 @@ TEST(test_entity, test_add_file) { EXPECT_EQ(entity.SetLocalPath(TEST_DATA_DIR + "/test.json"), StatusCode::SUCCESS); } +TEST(test_entity, test_entity_to_string) { + Entity entity; + EXPECT_EQ(entity.ToString(), "{}\n"); + + entity.SetRole(Role::PROPERTY); + EXPECT_EQ(entity.ToString(), "{\n \"role\": \"ENTITY_ROLE_PROPERTY\"\n}\n"); + + entity.SetValue(Value()); + EXPECT_EQ(entity.ToString(), "{\n \"role\": \"ENTITY_ROLE_PROPERTY\"\n}\n"); + + entity.SetDataType(DataType()); + EXPECT_EQ(entity.ToString(), "{\n \"role\": \"ENTITY_ROLE_PROPERTY\"\n}\n"); +} + +TEST(test_entity, test_properties_to_string) { + Entity entity; + EXPECT_EQ(entity.GetProperties().ToString(), "[]\n"); + + Property property; + property.SetName("Prop1"); + entity.AppendProperty(property); + EXPECT_EQ(entity.GetProperties().ToString(), "[\n{\n \"name\": \"Prop1\"\n}\n]\n"); + + Property property2; + property2.SetName("Prop2"); + entity.AppendProperty(property2); + EXPECT_EQ(entity.GetProperties().ToString(), + "[\n{\n \"name\": \"Prop1\"\n},\n{\n \"name\": \"Prop2\"\n}\n]\n"); +} + +TEST(test_entity, test_property_to_string) { + Property property; + EXPECT_EQ(property.ToString(), "{}\n"); + + property.SetDataType(AtomicDataType::DOUBLE); + EXPECT_EQ(property.ToString(), + "{\n \"dataType\": {\n \"atomicDataType\": \"ATOMIC_DATA_TYPE_DOUBLE\"\n }\n}\n"); +} + +TEST(test_entity, test_parents_to_string) { + Parent parent; + parent.SetName("the name"); + + Entity entity; + entity.AppendParent(parent); + + EXPECT_EQ(entity.GetParents().ToString(), "[\n{\n \"name\": \"the name\"\n}\n]\n"); +} + +TEST(test_entity, test_parent_to_string) { + Parent parent; + EXPECT_EQ(parent.ToString(), "{}\n"); + + parent.SetName("the name"); + EXPECT_EQ(parent.ToString(), "{\n \"name\": \"the name\"\n}\n"); +} + +TEST(test_entity, test_messages_to_string) { + IdResponse idResponse; + idResponse.set_id("entity_id"); + auto *error = idResponse.add_errors(); + error->set_code(MessageCode::ENTITY_DOES_NOT_EXIST); + error->set_description("error_desc"); + + Entity entity(&idResponse); + + // Messages are not printed, currently. + EXPECT_EQ(entity.ToString(), "{\n \"id\": \"entity_id\",\n \"version\": {}\n}\n"); + EXPECT_EQ(entity.GetErrors().ToString(), + "[\n{\n \"code\": 2,\n \"description\": \"error_desc\"\n}\n]\n"); +} + +TEST(test_entity, test_message_to_string) { + IdResponse idResponse; + idResponse.set_id("entity_id"); + auto *error = idResponse.add_errors(); + error->set_code(MessageCode::ENTITY_DOES_NOT_EXIST); + error->set_description("error_desc"); + + Entity entity(&idResponse); + + // Messages are not printed, currently. + EXPECT_EQ(entity.ToString(), "{\n \"id\": \"entity_id\",\n \"version\": {}\n}\n"); + EXPECT_EQ(entity.GetErrors().at(0).ToString(), + "{\n \"code\": 2,\n \"description\": \"error_desc\"\n}\n"); +} + } // namespace caosdb::entity diff --git a/test/test_list_properties.cpp b/test/test_list_properties.cpp index 27d0aad53cb9eb1342e1cb87ca24d7c3b71938f0..bc82896f5e7623643cb249979f381f3a7097d40d 100644 --- a/test/test_list_properties.cpp +++ b/test/test_list_properties.cpp @@ -24,6 +24,7 @@ #include "caosdb/entity.h" // for Entity #include "caosdb/entity/v1alpha1/main.pb.h" // for AtomicDataType, DataType #include "caosdb/value.h" // for Value +#include <cstdint> // for int64_t #include <gtest/gtest-message.h> // for Message #include <gtest/gtest-test-part.h> // for TestPartResult, SuiteApi... #include <gtest/gtest_pred_impl.h> // for AssertionResult, Test @@ -61,4 +62,39 @@ TEST(test_list_property, test_list_of_text) { EXPECT_EQ(value.GetAsVector().at(1).GetAsString(), "item2"); } +TEST(test_list_property, test_list_reassignment) { + Property list_property; + // assign int list + std::vector<int64_t> int_values{1, 2, 3}; + list_property.SetValue(int_values); + const auto &value_ints = list_property.GetValue(); + EXPECT_TRUE(value_ints.IsVector()); + EXPECT_EQ(value_ints.GetAsVector().size(), 3); + for (int ii = 0; ii < 3; ii++) { + EXPECT_TRUE(value_ints.GetAsVector().at(ii).IsInt64()); + EXPECT_EQ(value_ints.GetAsVector().at(ii).GetAsInt64(), int_values[ii]); + } + + // Re-assign to double scalar + double double_value(1.23); + list_property.SetValue(double_value); + const auto &value_double = list_property.GetValue(); + EXPECT_FALSE(value_double.IsVector()); + EXPECT_TRUE(value_double.IsDouble()); + EXPECT_FALSE(value_double.IsInt64()); + EXPECT_EQ(value_double.GetAsDouble(), double_value); + + // Re-assign to boolean list + std::vector<bool> bool_values{true, false, false, true}; + list_property.SetValue(bool_values); + const auto &value_bools = list_property.GetValue(); + EXPECT_TRUE(value_bools.IsVector()); + EXPECT_EQ(value_bools.GetAsVector().size(), 4); + for (int jj = 0; jj < 4; jj++) { + EXPECT_TRUE(value_bools.GetAsVector().at(jj).IsBool()); + EXPECT_FALSE(value_bools.GetAsVector().at(jj).IsInt64()); + EXPECT_EQ(value_bools.GetAsVector().at(jj).GetAsBool(), bool_values[jj]); + } +} + } // namespace caosdb::entity diff --git a/test/test_utility.cpp b/test/test_utility.cpp index 875cfbfcf199100d4a03d847680c997ecc4cb582..3a9a420cee742c1001c727a6154452ee10e8d8fb 100644 --- a/test/test_utility.cpp +++ b/test/test_utility.cpp @@ -26,6 +26,7 @@ #include "boost/json/value.hpp" // for value #include "caosdb/data_type.h" // for atomicdatatype_names #include "caosdb/entity.h" // for importance_names, role... +#include "caosdb/status_code.h" // for get_status_description #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 @@ -78,4 +79,10 @@ TEST(test_utility, enum_names) { std::out_of_range, "Could not find enum value for string 'Invalid name'."); } +TEST(test_utility, test_status_code_description) { + EXPECT_EQ(caosdb::get_status_description(12412323), "MISSING DESCRIPTION"); + EXPECT_EQ(caosdb::get_status_description(static_cast<int>(StatusCode::UNKNOWN)), + "Unknown error. This is typically a bug (server or client)."); +} + } // namespace caosdb::utility diff --git a/test/test_value.cpp b/test/test_value.cpp index 25f8a12909e52fee4e3ed3a6b688b1871a41a32f..241f2bd0ce2a7291184a79cdaa0612f2b99fa204 100644 --- a/test/test_value.cpp +++ b/test/test_value.cpp @@ -22,6 +22,7 @@ #include "caosdb/value.h" // for Value #include "caosdb/entity/v1alpha1/main.pb.h" // for AtomicDataType, DataType +#include "caosdb/protobuf_helper.h" // for ProtoMessageWrapper #include <algorithm> // for max #include <cmath> // for isnan #include <gtest/gtest-message.h> // for Message @@ -32,6 +33,7 @@ #include <vector> // for vector namespace caosdb::entity { +using ProtoValue = caosdb::entity::v1alpha1::Value; using ProtoEntity = caosdb::entity::v1alpha1::Entity; using ProtoParent = caosdb::entity::v1alpha1::Parent; using ProtoDataType = caosdb::entity::v1alpha1::DataType; @@ -174,4 +176,21 @@ TEST(test_value, test_abstract_value) { EXPECT_EQ(scalar_value2.GetAsDouble(), 27.5); } +TEST(test_value, test_value_to_string) { + Value value1; + EXPECT_EQ(value1.ToString(), "{}\n"); + + ProtoValue proto_value; + Value value2(&proto_value); + EXPECT_EQ(value2.ToString(), "{}\n"); + + Value value3(2.6); + EXPECT_EQ(value3.ToString(), "{\n \"scalarValue\": {\n \"doubleValue\": 2.6\n }\n}\n"); + + Value value4(std::vector<bool>{true, false}); + EXPECT_EQ(value4.ToString(), + "{\n \"listValues\": {\n \"values\": [\n {\n \"booleanValue\": true\n },\n " + "{\n \"booleanValue\": false\n }\n ]\n }\n}\n"); +} + } // namespace caosdb::entity