aboutsummaryrefslogtreecommitdiff
path: root/app/source/Cplt/Model/Items.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'app/source/Cplt/Model/Items.hpp')
-rw-r--r--app/source/Cplt/Model/Items.hpp253
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);
+};