1 #pragma once 2 3 #include <nlohmann/json.hpp> 4 #include <phosphor-logging/elog-errors.hpp> 5 #include <phosphor-logging/elog.hpp> 6 #include <phosphor-logging/lg2.hpp> 7 8 #include <format> 9 #include <fstream> 10 #include <ranges> 11 #include <string> 12 #include <tuple> 13 #include <type_traits> 14 #include <utility> 15 16 class JsonSerializer 17 { 18 public: JsonSerializer(std::string path,nlohmann::json js=nlohmann::json ())19 JsonSerializer(std::string path, nlohmann::json js = nlohmann::json()) : 20 mfaConfPath(path), jsonData(std::move(js)) 21 {} 22 stringSplitter()23 inline auto stringSplitter() 24 { 25 return std::views::split('/') | std::views::transform([](auto&& sub) { 26 return std::string(sub.begin(), sub.end()); 27 }); 28 } makeJson(const std::string & key,const std::string & value)29 nlohmann::json makeJson(const std::string& key, const std::string& value) 30 { 31 auto keys = key | stringSplitter(); 32 std::vector v(keys.begin(), keys.end()); 33 auto rv = v | std::views::reverse; 34 nlohmann::json init; 35 init[rv.front()] = value; 36 auto newJson = std::reduce(rv.begin() + 1, rv.end(), init, 37 [](auto sofar, auto currentKey) { 38 nlohmann::json j; 39 j[currentKey] = sofar; 40 return j; 41 }); 42 return newJson; 43 } getLeafNode(const std::string_view keyPath)44 std::optional<nlohmann::json> getLeafNode(const std::string_view keyPath) 45 { 46 auto keys = keyPath | stringSplitter(); 47 nlohmann::json current = jsonData; 48 for (auto key : keys) 49 { 50 if (!current.contains(key)) 51 { 52 return std::nullopt; 53 } 54 current = current[key]; 55 } 56 return current; 57 } serialize(std::string key,const std::string value)58 void serialize(std::string key, const std::string value) 59 { 60 jsonData.merge_patch(makeJson(key, value)); 61 } 62 template <typename T> deserialize(std::string key,T & value)63 void deserialize(std::string key, T& value) 64 { 65 auto leaf = getLeafNode(key); 66 if (leaf) 67 { 68 value = *leaf; 69 } 70 } erase(std::string key)71 void erase(std::string key) 72 { 73 if (jsonData.contains(key)) 74 { 75 jsonData.erase(key); 76 } 77 } store()78 bool store() 79 { 80 std::filesystem::path dir = 81 std::filesystem::path(mfaConfPath).parent_path(); 82 83 // Check if the directory exists, and create it if it does not 84 if (!dir.string().empty() && !std::filesystem::exists(dir)) 85 { 86 std::error_code ec; 87 if (!std::filesystem::create_directories(dir, ec)) 88 { 89 lg2::error("Unable to create directory {DIR}", "DIR", 90 dir.string()); 91 return false; 92 } 93 } 94 std::ofstream file(mfaConfPath.data()); 95 if (file.is_open()) 96 { 97 file << jsonData.dump(4); // Pretty print with 4 spaces 98 file.close(); 99 return true; 100 } 101 else 102 { 103 lg2::error("Unable to open file {FILENAME}", "FILENAME", 104 mfaConfPath); 105 return false; 106 } 107 } load()108 void load() 109 { 110 std::ifstream file(mfaConfPath.data()); 111 112 if (file.is_open()) 113 { 114 file >> jsonData; 115 file.close(); 116 } 117 else 118 { 119 lg2::error("Unable to open file for reading {FILENAME}", "FILENAME", 120 mfaConfPath); 121 } 122 } 123 124 private: 125 const std::string mfaConfPath; 126 nlohmann::json jsonData; 127 }; 128