diff options
Diffstat (limited to 'app/source/Cplt/Model/Items.hpp')
-rw-r--r-- | app/source/Cplt/Model/Items.hpp | 253 |
1 files changed, 253 insertions, 0 deletions
diff --git a/app/source/Cplt/Model/Items.hpp b/app/source/Cplt/Model/Items.hpp new file mode 100644 index 0000000..c00ee59 --- /dev/null +++ b/app/source/Cplt/Model/Items.hpp @@ -0,0 +1,253 @@ +#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> + +template <class T> +class ItemList +{ +private: + std::vector<T> mStorage; + tsl::array_map<char, size_t> mNameLookup; + +public: + template <class... Args> + T& Insert(std::string name, Args... args) + { + auto iter = mNameLookup.find(name); + if (iter != mNameLookup.end()) { + 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); + 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 + { + return &mStorage[id]; + } + + const T* Find(std::string_view name) const + { + auto iter = mNameLookup.find(name); + if (iter != mNameLookup.end()) { + return &mStorage[iter.value()]; + } else { + return nullptr; + } + } + + 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; + } + + 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(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 + { + 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<ProductItem> +{ +private: + std::string mDescription; + int mPrice = 0; + int mStock = 0; + +public: + using ItemBase::ItemBase; + + const std::string& GetDescription() const; + void SetDescription(std::string description); + /// Get the price of this item in US cents. + int GetPrice() const; + void SetPrice(int price); + /// Get the current number of this product in warehouse. + /// This is a housekeeping field and shouldn't be editable by the user from the UI. + int GetStock() const; + void SetStock(int stock); + + Json::Value Serialize() const; + void Deserialize(const Json::Value& elm); +}; + +class FactoryItem : public ItemBase<FactoryItem> +{ +private: + std::string mDescription; + std::string mEmail; + +public: + using ItemBase::ItemBase; + + 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<CustomerItem> +{ +private: + std::string mDescription; + std::string mEmail; + +public: + using ItemBase::ItemBase; + + 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); +}; |