mirror of
				https://github.com/ciphervance/supercell-wx.git
				synced 2025-10-31 04:50:06 +00:00 
			
		
		
		
	Initial loading of JSON-based settings
This commit is contained in:
		
							parent
							
								
									28ea12cbfe
								
							
						
					
					
						commit
						1c0140fc98
					
				
					 8 changed files with 561 additions and 38 deletions
				
			
		|  | @ -1,5 +1,6 @@ | |||
| #include <scwx/qt/main/main_window.hpp> | ||||
| #include <scwx/qt/manager/resource_manager.hpp> | ||||
| #include <scwx/qt/manager/settings_manager.hpp> | ||||
| 
 | ||||
| #include <boost/log/expressions.hpp> | ||||
| #include <boost/log/trivial.hpp> | ||||
|  | @ -10,6 +11,9 @@ int main(int argc, char* argv[]) | |||
|    boost::log::core::get()->set_filter(boost::log::trivial::severity >= | ||||
|                                        boost::log::trivial::debug); | ||||
| 
 | ||||
|    QCoreApplication::setApplicationName("Supercell Wx"); | ||||
| 
 | ||||
|    scwx::qt::manager::SettingsManager::Initialize(); | ||||
|    scwx::qt::manager::ResourceManager::PreLoad(); | ||||
| 
 | ||||
|    QApplication               a(argc, argv); | ||||
|  |  | |||
							
								
								
									
										116
									
								
								scwx-qt/source/scwx/qt/manager/settings_manager.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										116
									
								
								scwx-qt/source/scwx/qt/manager/settings_manager.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,116 @@ | |||
| #include <scwx/qt/manager/settings_manager.hpp> | ||||
| #include <scwx/qt/util/json.hpp> | ||||
| 
 | ||||
| #include <filesystem> | ||||
| #include <fstream> | ||||
| 
 | ||||
| #include <QDir> | ||||
| #include <QStandardPaths> | ||||
| 
 | ||||
| #include <boost/log/trivial.hpp> | ||||
| 
 | ||||
| namespace scwx | ||||
| { | ||||
| namespace qt | ||||
| { | ||||
| namespace manager | ||||
| { | ||||
| namespace SettingsManager | ||||
| { | ||||
| 
 | ||||
| static const std::string logPrefix_ = "[scwx::qt::manager::settings_manager] "; | ||||
| 
 | ||||
| static std::shared_ptr<settings::GeneralSettings> generalSettings_ = nullptr; | ||||
| 
 | ||||
| static boost::json::value ConvertSettingsToJson(); | ||||
| static void               GenerateDefaultSettings(); | ||||
| static bool               LoadSettings(const boost::json::object& settingsJson); | ||||
| 
 | ||||
| bool Initialize() | ||||
| { | ||||
|    std::string appDataPath { | ||||
|       QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) | ||||
|          .toStdString()}; | ||||
| 
 | ||||
|    if (!std::filesystem::is_directory(appDataPath)) | ||||
|    { | ||||
|       if (!std::filesystem::create_directories(appDataPath)) | ||||
|       { | ||||
|          BOOST_LOG_TRIVIAL(error) | ||||
|             << logPrefix_ << "Unable to create application data directory: \"" | ||||
|             << appDataPath << "\""; | ||||
|          return false; | ||||
|       } | ||||
|    } | ||||
| 
 | ||||
|    std::string settingsPath {appDataPath + "/settings.json"}; | ||||
| 
 | ||||
|    ReadSettings(settingsPath); | ||||
| 
 | ||||
|    return true; | ||||
| } | ||||
| 
 | ||||
| void ReadSettings(const std::string& settingsPath) | ||||
| { | ||||
|    boost::json::value settingsJson = nullptr; | ||||
| 
 | ||||
|    if (std::filesystem::exists(settingsPath)) | ||||
|    { | ||||
|       settingsJson = util::json::ReadJsonFile(settingsPath); | ||||
|    } | ||||
| 
 | ||||
|    if (settingsJson == nullptr || !settingsJson.is_object()) | ||||
|    { | ||||
|       GenerateDefaultSettings(); | ||||
|       settingsJson = ConvertSettingsToJson(); | ||||
|       util::json::WriteJsonFile(settingsPath, settingsJson); | ||||
|    } | ||||
|    else | ||||
|    { | ||||
|       bool jsonDirty = LoadSettings(settingsJson.as_object()); | ||||
| 
 | ||||
|       if (jsonDirty) | ||||
|       { | ||||
|          settingsJson = ConvertSettingsToJson(); | ||||
|          util::json::WriteJsonFile(settingsPath, settingsJson); | ||||
|       } | ||||
|    }; | ||||
| } | ||||
| 
 | ||||
| std::shared_ptr<settings::GeneralSettings> general_settings() | ||||
| { | ||||
|    return generalSettings_; | ||||
| } | ||||
| 
 | ||||
| static boost::json::value ConvertSettingsToJson() | ||||
| { | ||||
|    boost::json::object settingsJson; | ||||
| 
 | ||||
|    settingsJson["general"] = generalSettings_->ToJson(); | ||||
| 
 | ||||
|    return settingsJson; | ||||
| } | ||||
| 
 | ||||
| static void GenerateDefaultSettings() | ||||
| { | ||||
|    BOOST_LOG_TRIVIAL(info) << logPrefix_ << "Generating default settings"; | ||||
| 
 | ||||
|    generalSettings_ = settings::GeneralSettings::Create(); | ||||
| } | ||||
| 
 | ||||
| static bool LoadSettings(const boost::json::object& settingsJson) | ||||
| { | ||||
|    BOOST_LOG_TRIVIAL(info) << logPrefix_ << "Loading settings"; | ||||
| 
 | ||||
|    bool jsonDirty = false; | ||||
| 
 | ||||
|    generalSettings_ = settings::GeneralSettings::Load( | ||||
|       settingsJson.if_contains("general"), jsonDirty); | ||||
| 
 | ||||
|    return jsonDirty; | ||||
| } | ||||
| 
 | ||||
| } // namespace SettingsManager
 | ||||
| } // namespace manager
 | ||||
| } // namespace qt
 | ||||
| } // namespace scwx
 | ||||
							
								
								
									
										22
									
								
								scwx-qt/source/scwx/qt/manager/settings_manager.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								scwx-qt/source/scwx/qt/manager/settings_manager.hpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,22 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include <scwx/qt/settings/general_settings.hpp> | ||||
| 
 | ||||
| namespace scwx | ||||
| { | ||||
| namespace qt | ||||
| { | ||||
| namespace manager | ||||
| { | ||||
| namespace SettingsManager | ||||
| { | ||||
| 
 | ||||
| bool Initialize(); | ||||
| void ReadSettings(const std::string& settingsPath); | ||||
| 
 | ||||
| std::shared_ptr<settings::GeneralSettings> general_settings(); | ||||
| 
 | ||||
| } // namespace SettingsManager
 | ||||
| } // namespace manager
 | ||||
| } // namespace qt
 | ||||
| } // namespace scwx
 | ||||
							
								
								
									
										98
									
								
								scwx-qt/source/scwx/qt/settings/general_settings.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										98
									
								
								scwx-qt/source/scwx/qt/settings/general_settings.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,98 @@ | |||
| #include <scwx/qt/settings/general_settings.hpp> | ||||
| #include <scwx/qt/util/json.hpp> | ||||
| 
 | ||||
| #include <boost/log/trivial.hpp> | ||||
| 
 | ||||
| namespace scwx | ||||
| { | ||||
| namespace qt | ||||
| { | ||||
| namespace settings | ||||
| { | ||||
| 
 | ||||
| static const std::string logPrefix_ = "[scwx::qt::settings::general_settings] "; | ||||
| 
 | ||||
| static const std::string& DEFAULT_DEFAULT_RADAR_SITE = "KLSX"; | ||||
| 
 | ||||
| class GeneralSettingsImpl | ||||
| { | ||||
| public: | ||||
|    explicit GeneralSettingsImpl() {} | ||||
| 
 | ||||
|    ~GeneralSettingsImpl() {} | ||||
| 
 | ||||
|    void SetDefaults() { defaultRadarSite_ = DEFAULT_DEFAULT_RADAR_SITE; } | ||||
| 
 | ||||
|    std::string defaultRadarSite_; | ||||
| }; | ||||
| 
 | ||||
| GeneralSettings::GeneralSettings() : p(std::make_unique<GeneralSettingsImpl>()) | ||||
| { | ||||
| } | ||||
| GeneralSettings::~GeneralSettings() = default; | ||||
| 
 | ||||
| GeneralSettings::GeneralSettings(GeneralSettings&&) noexcept = default; | ||||
| GeneralSettings& | ||||
| GeneralSettings::operator=(GeneralSettings&&) noexcept = default; | ||||
| 
 | ||||
| const std::string& GeneralSettings::default_radar_site() | ||||
| { | ||||
|    return p->defaultRadarSite_; | ||||
| } | ||||
| 
 | ||||
| boost::json::value GeneralSettings::ToJson() | ||||
| { | ||||
|    boost::json::object json; | ||||
| 
 | ||||
|    json["default_radar_site"] = p->defaultRadarSite_; | ||||
| 
 | ||||
|    return json; | ||||
| } | ||||
| 
 | ||||
| std::shared_ptr<GeneralSettings> GeneralSettings::Create() | ||||
| { | ||||
|    std::shared_ptr<GeneralSettings> generalSettings = | ||||
|       std::make_shared<GeneralSettings>(); | ||||
| 
 | ||||
|    generalSettings->p->SetDefaults(); | ||||
| 
 | ||||
|    return generalSettings; | ||||
| } | ||||
| 
 | ||||
| std::shared_ptr<GeneralSettings> | ||||
| GeneralSettings::Load(const boost::json::value* json, bool& jsonDirty) | ||||
| { | ||||
|    std::shared_ptr<GeneralSettings> generalSettings = | ||||
|       std::make_shared<GeneralSettings>(); | ||||
| 
 | ||||
|    if (json != nullptr && json->is_object()) | ||||
|    { | ||||
|       jsonDirty |= | ||||
|          !util::json::FromJsonString(json->as_object(), | ||||
|                                      "default_radar_site", | ||||
|                                      generalSettings->p->defaultRadarSite_, | ||||
|                                      DEFAULT_DEFAULT_RADAR_SITE); | ||||
|    } | ||||
|    else | ||||
|    { | ||||
|       if (json == nullptr) | ||||
|       { | ||||
|          BOOST_LOG_TRIVIAL(warning) | ||||
|             << logPrefix_ << "Key is not present, resetting to defaults"; | ||||
|       } | ||||
|       else if (!json->is_object()) | ||||
|       { | ||||
|          BOOST_LOG_TRIVIAL(warning) | ||||
|             << logPrefix_ << "Invalid json, resetting to defaults"; | ||||
|       } | ||||
| 
 | ||||
|       generalSettings->p->SetDefaults(); | ||||
|       jsonDirty = true; | ||||
|    } | ||||
| 
 | ||||
|    return generalSettings; | ||||
| } | ||||
| 
 | ||||
| } // namespace settings
 | ||||
| } // namespace qt
 | ||||
| } // namespace scwx
 | ||||
							
								
								
									
										43
									
								
								scwx-qt/source/scwx/qt/settings/general_settings.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								scwx-qt/source/scwx/qt/settings/general_settings.hpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,43 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include <memory> | ||||
| #include <string> | ||||
| 
 | ||||
| #include <boost/json.hpp> | ||||
| 
 | ||||
| namespace scwx | ||||
| { | ||||
| namespace qt | ||||
| { | ||||
| namespace settings | ||||
| { | ||||
| 
 | ||||
| class GeneralSettingsImpl; | ||||
| 
 | ||||
| class GeneralSettings | ||||
| { | ||||
| public: | ||||
|    explicit GeneralSettings(); | ||||
|    ~GeneralSettings(); | ||||
| 
 | ||||
|    GeneralSettings(const GeneralSettings&) = delete; | ||||
|    GeneralSettings& operator=(const GeneralSettings&) = delete; | ||||
| 
 | ||||
|    GeneralSettings(GeneralSettings&&) noexcept; | ||||
|    GeneralSettings& operator=(GeneralSettings&&) noexcept; | ||||
| 
 | ||||
|    const std::string& default_radar_site(); | ||||
| 
 | ||||
|    boost::json::value ToJson(); | ||||
| 
 | ||||
|    static std::shared_ptr<GeneralSettings> Create(); | ||||
|    static std::shared_ptr<GeneralSettings> Load(const boost::json::value* json, | ||||
|                                                 bool& jsonDirty); | ||||
| 
 | ||||
| private: | ||||
|    std::unique_ptr<GeneralSettingsImpl> p; | ||||
| }; | ||||
| 
 | ||||
| } // namespace settings
 | ||||
| } // namespace qt
 | ||||
| } // namespace scwx
 | ||||
							
								
								
									
										193
									
								
								scwx-qt/source/scwx/qt/util/json.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										193
									
								
								scwx-qt/source/scwx/qt/util/json.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,193 @@ | |||
| #include <scwx/qt/util/json.hpp> | ||||
| 
 | ||||
| #include <fstream> | ||||
| 
 | ||||
| #include <boost/log/trivial.hpp> | ||||
| 
 | ||||
| namespace scwx | ||||
| { | ||||
| namespace qt | ||||
| { | ||||
| namespace util | ||||
| { | ||||
| namespace json | ||||
| { | ||||
| 
 | ||||
| static const std::string logPrefix_ = "[scwx::qt::util::json] "; | ||||
| 
 | ||||
| /* Adapted from:
 | ||||
|  * https://www.boost.org/doc/libs/1_77_0/libs/json/doc/html/json/examples.html#json.examples.pretty
 | ||||
|  * | ||||
|  * Copyright (c) 2019, 2020 Vinnie Falco | ||||
|  * Copyright (c) 2020 Krystian Stasiowski | ||||
|  * Distributed under the Boost Software License, Version 1.0. (See | ||||
|  * http://www.boost.org/LICENSE_1_0.txt)
 | ||||
|  */ | ||||
| static void PrettyPrintJson(std::ostream&             os, | ||||
|                             boost::json::value const& jv, | ||||
|                             std::string*              indent = nullptr); | ||||
| 
 | ||||
| bool FromJsonString(const boost::json::object& json, | ||||
|                     const std::string&         key, | ||||
|                     std::string&               value, | ||||
|                     const std::string&         defaultValue) | ||||
| { | ||||
|    const boost::json::value* jv    = json.if_contains(key); | ||||
|    bool                      found = false; | ||||
| 
 | ||||
|    if (jv != nullptr) | ||||
|    { | ||||
|       if (jv->is_string()) | ||||
|       { | ||||
|          value = boost::json::value_to<std::string>(*jv); | ||||
|          found = true; | ||||
|       } | ||||
|       else | ||||
|       { | ||||
|          BOOST_LOG_TRIVIAL(warning) | ||||
|             << logPrefix_ << key | ||||
|             << " is not a string, setting to default: " << defaultValue; | ||||
|          value = defaultValue; | ||||
|       } | ||||
|    } | ||||
|    else | ||||
|    { | ||||
|       BOOST_LOG_TRIVIAL(debug) | ||||
|          << logPrefix_ << key | ||||
|          << " is not present, setting to default: " << defaultValue; | ||||
|       value = defaultValue; | ||||
|    } | ||||
| 
 | ||||
|    return found; | ||||
| } | ||||
| 
 | ||||
| boost::json::value ReadJsonFile(const std::string& path) | ||||
| { | ||||
|    std::ifstream ifs {path}; | ||||
|    std::string   line; | ||||
| 
 | ||||
|    boost::json::stream_parser p; | ||||
|    boost::json::error_code    ec; | ||||
| 
 | ||||
|    while (std::getline(ifs, line)) | ||||
|    { | ||||
|       p.write(line, ec); | ||||
|       if (ec) | ||||
|       { | ||||
|          BOOST_LOG_TRIVIAL(warning) << logPrefix_ << ec.message(); | ||||
|          return nullptr; | ||||
|       } | ||||
|    } | ||||
| 
 | ||||
|    p.finish(ec); | ||||
|    if (ec) | ||||
|    { | ||||
|       BOOST_LOG_TRIVIAL(warning) << logPrefix_ << ec.message(); | ||||
|       return nullptr; | ||||
|    } | ||||
| 
 | ||||
|    return p.release(); | ||||
| } | ||||
| 
 | ||||
| void WriteJsonFile(const std::string&        path, | ||||
|                    const boost::json::value& json, | ||||
|                    bool                      prettyPrint) | ||||
| { | ||||
|    std::ofstream ofs {path}; | ||||
| 
 | ||||
|    if (prettyPrint) | ||||
|    { | ||||
|       PrettyPrintJson(ofs, json); | ||||
|    } | ||||
|    else | ||||
|    { | ||||
|       ofs << json; | ||||
|    } | ||||
|    ofs.close(); | ||||
| } | ||||
| 
 | ||||
| static void PrettyPrintJson(std::ostream&             os, | ||||
|                             boost::json::value const& jv, | ||||
|                             std::string*              indent) | ||||
| { | ||||
|    std::string indent_; | ||||
|    if (!indent) | ||||
|       indent = &indent_; | ||||
|    switch (jv.kind()) | ||||
|    { | ||||
|    case boost::json::kind::object: | ||||
|    { | ||||
|       os << "{\n"; | ||||
|       indent->append(4, ' '); | ||||
|       auto const& obj = jv.get_object(); | ||||
|       if (!obj.empty()) | ||||
|       { | ||||
|          auto it = obj.begin(); | ||||
|          for (;;) | ||||
|          { | ||||
|             os << *indent << boost::json::serialize(it->key()) << " : "; | ||||
|             PrettyPrintJson(os, it->value(), indent); | ||||
|             if (++it == obj.end()) | ||||
|                break; | ||||
|             os << ",\n"; | ||||
|          } | ||||
|       } | ||||
|       os << "\n"; | ||||
|       indent->resize(indent->size() - 4); | ||||
|       os << *indent << "}"; | ||||
|       break; | ||||
|    } | ||||
| 
 | ||||
|    case boost::json::kind::array: | ||||
|    { | ||||
|       os << "[\n"; | ||||
|       indent->append(4, ' '); | ||||
|       auto const& arr = jv.get_array(); | ||||
|       if (!arr.empty()) | ||||
|       { | ||||
|          auto it = arr.begin(); | ||||
|          for (;;) | ||||
|          { | ||||
|             os << *indent; | ||||
|             PrettyPrintJson(os, *it, indent); | ||||
|             if (++it == arr.end()) | ||||
|                break; | ||||
|             os << ",\n"; | ||||
|          } | ||||
|       } | ||||
|       os << "\n"; | ||||
|       indent->resize(indent->size() - 4); | ||||
|       os << *indent << "]"; | ||||
|       break; | ||||
|    } | ||||
| 
 | ||||
|    case boost::json::kind::string: | ||||
|    { | ||||
|       os << boost::json::serialize(jv.get_string()); | ||||
|       break; | ||||
|    } | ||||
| 
 | ||||
|    case boost::json::kind::uint64: os << jv.get_uint64(); break; | ||||
| 
 | ||||
|    case boost::json::kind::int64: os << jv.get_int64(); break; | ||||
| 
 | ||||
|    case boost::json::kind::double_: os << jv.get_double(); break; | ||||
| 
 | ||||
|    case boost::json::kind::bool_: | ||||
|       if (jv.get_bool()) | ||||
|          os << "true"; | ||||
|       else | ||||
|          os << "false"; | ||||
|       break; | ||||
| 
 | ||||
|    case boost::json::kind::null: os << "null"; break; | ||||
|    } | ||||
| 
 | ||||
|    if (indent->empty()) | ||||
|       os << "\n"; | ||||
| } | ||||
| 
 | ||||
| } // namespace json
 | ||||
| } // namespace util
 | ||||
| } // namespace qt
 | ||||
| } // namespace scwx
 | ||||
							
								
								
									
										27
									
								
								scwx-qt/source/scwx/qt/util/json.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								scwx-qt/source/scwx/qt/util/json.hpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,27 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include <boost/json.hpp> | ||||
| 
 | ||||
| namespace scwx | ||||
| { | ||||
| namespace qt | ||||
| { | ||||
| namespace util | ||||
| { | ||||
| namespace json | ||||
| { | ||||
| 
 | ||||
| bool FromJsonString(const boost::json::object& json, | ||||
|                     const std::string&         key, | ||||
|                     std::string&               value, | ||||
|                     const std::string&         defaultValue); | ||||
| 
 | ||||
| boost::json::value ReadJsonFile(const std::string& path); | ||||
| void               WriteJsonFile(const std::string&        path, | ||||
|                                  const boost::json::value& json, | ||||
|                                  bool                      prettyPrint = true); | ||||
| 
 | ||||
| } // namespace json
 | ||||
| } // namespace util
 | ||||
| } // namespace qt
 | ||||
| } // namespace scwx
 | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Dan Paulat
						Dan Paulat