diff options
Diffstat (limited to 'core/src/Model/Items.hpp')
-rw-r--r-- | core/src/Model/Items.hpp | 209 |
1 files changed, 142 insertions, 67 deletions
diff --git a/core/src/Model/Items.hpp b/core/src/Model/Items.hpp index 0c7be41..e20a290 100644 --- a/core/src/Model/Items.hpp +++ b/core/src/Model/Items.hpp @@ -1,54 +1,21 @@ #pragma once +#include "cplt_fwd.hpp" + +#include <json/reader.h> +#include <json/value.h> +#include <json/writer.h> #include <tsl/array_map.h> #include <cstddef> +#include <limits> #include <stdexcept> #include <string> #include <string_view> +#include <utility> #include <vector> -/// Pointers and references returned by accessors are valid as long as no non-const functions have been called. template <class T> class ItemList { -public: - class Iterator { - private: - typename std::vector<T>::const_iterator mBackingIter; - - public: - Iterator(typename std::vector<T>::const_iterator it) - : mBackingIter{ it } { - } - - Iterator& operator++() { - ++mBackingIter; - return *this; - } - - Iterator& operator++(int) { - auto tmp = *this; - ++mBackingIter; - return tmp; - } - - Iterator& operator--() { - --mBackingIter; - return *this; - } - - Iterator& operator--(int) { - auto tmp = *this; - --mBackingIter; - return tmp; - } - - const T& operator*() const { - return *mBackingIter; - } - - friend bool operator==(const Iterator&, const Iterator&) = default; - }; - private: std::vector<T> mStorage; tsl::array_map<char, size_t> mNameLookup; @@ -61,9 +28,28 @@ public: throw std::runtime_error("Duplicate key."); } + for (size_t i = 0; i < mStorage.size(); ++i) { + if (mStorage[i].IsInvalid()) { + mStorage[i] = T(*this, i, std::move(name), std::forward<Args>(args)...); + mNameLookup.insert(name, i); + return mStorage[i]; + } + } + size_t id = mStorage.size(); mNameLookup.insert(name, id); - return mStorage.emplace_back(id, std::move(name), std::forward<Args>(args)...); + mStorage.emplace_back(*this, id, std::move(name), std::forward<Args>(args)...); + return mStorage[id]; + } + + void Remove(size_t index) { + auto& item = mStorage[index]; + mNameLookup.erase(item.GetName()); + mStorage[index] = T(*this); + } + + T* Find(size_t id) { + return &mStorage[id]; } const T* Find(size_t id) const { @@ -79,68 +65,157 @@ public: } } - Iterator begin() const { - return Iterator(mStorage.begin()); + Json::Value Serialize() const { + Json::Value items(Json::arrayValue); + for (auto& item : mStorage) { + if (!item.IsInvalid()) { + auto elm = item.Serialize(); + elm["Id"] = item.GetId(); + elm["Name"] = item.GetName(); + items.append(elm); + } + } + + Json::Value root; + root["MaxItemId"] = mStorage.size(); + root["Items"] = std::move(items); + + return root; } - Iterator end() const { - return Iterator(mStorage.end()); + ItemList() = default; + + ItemList(const Json::Value& root) { + constexpr const char* kMessage = "Failed to load item list from JSON."; + + auto& itemCount = root["MaxItemId"]; + if (!itemCount.isIntegral()) throw std::runtime_error(kMessage); + + mStorage.resize(itemCount.asInt64(), T(*this)); + + auto& items = root["Items"]; + if (!items.isArray()) throw std::runtime_error(kMessage); + + for (auto& elm : items) { + if (!elm.isObject()) throw std::runtime_error(kMessage); + + auto& id = elm["Id"]; + if (!id.isIntegral()) throw std::runtime_error(kMessage); + auto& name = elm["Name"]; + if (!name.isString()) throw std::runtime_error(kMessage); + + size_t iid = id.asInt64(); + mStorage[iid] = T(*this, iid, name.asString()); + mStorage[iid].Deserialize(elm); + } + } + + typename decltype(mStorage)::iterator begin() { + return mStorage.begin(); + } + + typename decltype(mStorage)::iterator end() { + return mStorage.end(); + } + + typename decltype(mStorage)::const_iterator begin() const { + return mStorage.begin(); + } + + typename decltype(mStorage)::const_iterator end() const { + return mStorage.end(); + } + +private: + template <class TSelf> + friend class ItemBase; + + void UpdateItemName(const T& item, const std::string& newName) { + mNameLookup.erase(item.GetName()); + mNameLookup.insert(newName, item.GetId()); } }; +template <class TSelf> class ItemBase { private: + ItemList<TSelf>* mList; size_t mId; + std::string mName; public: - ItemBase(); - ItemBase(size_t id); + ItemBase(ItemList<TSelf>& list, size_t id = std::numeric_limits<size_t>::max(), std::string name = "") + : mList{ &list } + , mId{ id } + , mName{ std::move(name) } { + } - bool IsInvalid() const; - size_t GetId() const; + bool IsInvalid() const { + return mId == std::numeric_limits<size_t>::max(); + } + + ItemList<TSelf>& GetList() const { + return *mList; + } + + size_t GetId() const { + return mId; + } + + const std::string& GetName() const { + return mName; + } + + void SetName(std::string name) { + mList->UpdateItemName(static_cast<TSelf&>(*this), name); + mName = std::move(name); + } }; -class ProductItem : public ItemBase { +class ProductItem : public ItemBase<ProductItem> { private: - std::string mName; std::string mDescription; public: - ProductItem() {} - ProductItem(size_t id, std::string name); + using ItemBase::ItemBase; - const std::string& GetName() const; - void SetName(std::string mName); const std::string& GetDescription() const; void SetDescription(std::string description); + + Json::Value Serialize() const; + void Deserialize(const Json::Value& elm); }; -class FactoryItem : public ItemBase { +class FactoryItem : public ItemBase<FactoryItem> { private: - std::string mName; std::string mDescription; + std::string mEmail; public: - FactoryItem() {} - FactoryItem(size_t id, std::string name); + using ItemBase::ItemBase; - const std::string& GetName() const; - void SetName(std::string name); const std::string& GetDescription() const; void SetDescription(std::string description); + const std::string& GetEmail() const; + void SetEmail(std::string email); + + Json::Value Serialize() const; + void Deserialize(const Json::Value& elm); }; -class CustomerItem : public ItemBase { +class CustomerItem : public ItemBase<CustomerItem> { private: - std::string mName; std::string mDescription; + std::string mEmail; public: - CustomerItem() {} - CustomerItem(size_t id, std::string name); + using ItemBase::ItemBase; - const std::string& GetName() const; - void SetName(std::string name); const std::string& GetDescription() const; void SetDescription(std::string description); + const std::string& GetEmail() const; + void SetEmail(std::string email); + + Json::Value Serialize() const; + void Deserialize(const Json::Value& elm); }; |