1 #pragma once 2 3 #include <rapidjson/pointer.h> 4 5 #include <algorithm> 6 #include <cinttypes> 7 #include <map> 8 #include <memory> 9 #include <mutex> 10 #include <pajlada/settings/common.hpp> 11 #include <pajlada/settings/detail/fs.hpp> 12 #include <pajlada/settings/signalargs.hpp> 13 #include <vector> 14 15 namespace pajlada { 16 namespace Settings { 17 18 class SettingData; 19 20 class SettingManager 21 { 22 public: 23 SettingManager(); 24 ~SettingManager(); 25 26 enum class LoadError { 27 NoError, 28 CannotOpenFile, 29 FileHandleError, 30 FileReadError, 31 FileSeekError, 32 JSONParseError, 33 }; 34 35 // Print given document json data prettily 36 void pp(const std::string &prefix = std::string()); 37 static void gPP(const std::string &prefix = std::string()); 38 static std::string stringify(const rapidjson::Value &v); 39 40 rapidjson::Value *get(const char *path); 41 bool set(const char *path, const rapidjson::Value &value, 42 SignalArgs args = SignalArgs()); 43 44 private: 45 // Called from set 46 void notifyUpdate(const std::string &path, const rapidjson::Value &value, 47 SignalArgs args = SignalArgs()); 48 49 // Called from load 50 void notifyLoadedValues(); 51 52 public: 53 // Useful array helper methods 54 static rapidjson::SizeType arraySize(const std::string &path); 55 static bool isNull(const std::string &path); 56 bool _isNull(const std::string &path); 57 static void setNull(const std::string &path); 58 59 // Basically the same as setNull, except we fully remove a value if it's the 60 // last index of the array 61 static bool removeArrayValue(const std::string &arrayPath, 62 rapidjson::SizeType index); 63 64 static rapidjson::SizeType cleanArray(const std::string &arrayPath); 65 66 // Useful object helper methods 67 static std::vector<std::string> getObjectKeys( 68 const std::string &objectPath); 69 70 static void clear(); 71 72 static std::weak_ptr<SettingData> getSetting( 73 const std::string &path, std::shared_ptr<SettingManager> instance); 74 75 static bool removeSetting(const std::string &path); 76 77 private: 78 template <typename Type> 79 friend class Setting; 80 81 bool _removeSetting(const std::string &path); 82 83 void clearSettings(const std::string &root); 84 85 public: 86 void setPath(const fs::path &newPath); 87 88 static LoadError gLoad(const fs::path &path = fs::path()); 89 static LoadError gLoadFrom(const fs::path &path); 90 91 // Load from given path and set given path as the "default path" (or load 92 // from default path if nullptr is sent) 93 LoadError load(const fs::path &path = fs::path()); 94 // Load from given path 95 LoadError loadFrom(const fs::path &path); 96 97 static bool gSave(const fs::path &path = fs::path()); 98 static bool gSaveAs(const fs::path &path); 99 100 // Force a settings save 101 // It is recommended to run this every now and then unless your application 102 // is crash free 103 // Save to given path and set path as the default path (or save from default 104 // path if filePath is a nullptr) 105 bool save(const fs::path &path = fs::path()); 106 // Save to given path 107 bool saveAs(const fs::path &path); 108 109 private: 110 bool writeTo(const fs::path &path); 111 112 public: 113 // Functions prefixed with g are static functions that work 114 // on the statically initialized SettingManager instance 115 116 enum class SaveMethod : uint64_t { 117 SaveOnExit = (1ull << 1ull), 118 SaveOnSettingChange = (1ull << 2ull), 119 120 // Force user to manually call SettingsManager::save() to save 121 SaveManually = 0, 122 SaveAllTheTime = SaveOnExit | SaveOnSettingChange, 123 } saveMethod = SaveMethod::SaveOnExit; 124 125 private: 126 // Returns true if the given save method is activated 127 inline bool hasSaveMethodFlag(SettingManager::SaveMethod testSaveMethod) const128 hasSaveMethodFlag(SettingManager::SaveMethod testSaveMethod) const 129 { 130 return (static_cast<uint64_t>(this->saveMethod) & 131 static_cast<uint64_t>(testSaveMethod)) != 0; 132 } 133 134 struct { 135 bool enabled{}; 136 uint8_t numSlots = 3; 137 } backup; 138 139 public: 140 void setBackupEnabled(bool enabled = true); 141 void setBackupSlots(uint8_t numSlots); 142 143 static const std::shared_ptr<SettingManager> & getInstance()144 getInstance() 145 { 146 static auto m = std::make_shared<SettingManager>(); 147 148 return m; 149 } 150 151 private: 152 std::shared_ptr<SettingData> getSetting(const std::string &path); 153 154 public: 155 rapidjson::Document document; 156 157 private: 158 fs::path filePath = "settings.json"; 159 160 std::mutex settingsMutex; 161 162 // path setting 163 std::map<std::string, std::shared_ptr<SettingData>> settings; 164 }; 165 166 } // namespace Settings 167 } // namespace pajlada 168