| #pragma once |
| |
| #include <nlohmann/json.hpp> |
| #include <phosphor-logging/elog-errors.hpp> |
| #include <phosphor-logging/elog.hpp> |
| #include <phosphor-logging/lg2.hpp> |
| |
| #include <format> |
| #include <fstream> |
| #include <ranges> |
| #include <string> |
| #include <tuple> |
| #include <type_traits> |
| #include <utility> |
| |
| class JsonSerializer |
| { |
| public: |
| JsonSerializer(std::string path, nlohmann::json js = nlohmann::json()) : |
| mfaConfPath(path), jsonData(std::move(js)) |
| {} |
| |
| inline auto stringSplitter() |
| { |
| return std::views::split('/') | std::views::transform([](auto&& sub) { |
| return std::string(sub.begin(), sub.end()); |
| }); |
| } |
| nlohmann::json makeJson(const std::string& key, const std::string& value) |
| { |
| auto keys = key | stringSplitter(); |
| std::vector v(keys.begin(), keys.end()); |
| auto rv = v | std::views::reverse; |
| nlohmann::json init; |
| init[rv.front()] = value; |
| auto newJson = std::reduce(rv.begin() + 1, rv.end(), init, |
| [](auto sofar, auto currentKey) { |
| nlohmann::json j; |
| j[currentKey] = sofar; |
| return j; |
| }); |
| return newJson; |
| } |
| std::optional<nlohmann::json> getLeafNode(const std::string_view keyPath) |
| { |
| auto keys = keyPath | stringSplitter(); |
| nlohmann::json current = jsonData; |
| for (auto key : keys) |
| { |
| if (!current.contains(key)) |
| { |
| return std::nullopt; |
| } |
| current = current[key]; |
| } |
| return current; |
| } |
| void serialize(std::string key, const std::string value) |
| { |
| jsonData.merge_patch(makeJson(key, value)); |
| } |
| template <typename T> |
| void deserialize(std::string key, T& value) |
| { |
| auto leaf = getLeafNode(key); |
| if (leaf) |
| { |
| value = *leaf; |
| } |
| } |
| void erase(std::string key) |
| { |
| if (jsonData.contains(key)) |
| { |
| jsonData.erase(key); |
| } |
| } |
| bool store() |
| { |
| std::filesystem::path dir = |
| std::filesystem::path(mfaConfPath).parent_path(); |
| |
| // Check if the directory exists, and create it if it does not |
| if (!dir.string().empty() && !std::filesystem::exists(dir)) |
| { |
| std::error_code ec; |
| if (!std::filesystem::create_directories(dir, ec)) |
| { |
| lg2::error("Unable to create directory {DIR}", "DIR", |
| dir.string()); |
| return false; |
| } |
| } |
| std::ofstream file(mfaConfPath.data()); |
| if (file.is_open()) |
| { |
| file << jsonData.dump(4); // Pretty print with 4 spaces |
| file.close(); |
| return true; |
| } |
| else |
| { |
| lg2::error("Unable to open file {FILENAME}", "FILENAME", |
| mfaConfPath); |
| return false; |
| } |
| } |
| void load() |
| { |
| std::ifstream file(mfaConfPath.data()); |
| |
| if (file.is_open()) |
| { |
| file >> jsonData; |
| file.close(); |
| } |
| else |
| { |
| lg2::error("Unable to open file for reading {FILENAME}", "FILENAME", |
| mfaConfPath); |
| } |
| } |
| |
| private: |
| const std::string mfaConfPath; |
| nlohmann::json jsonData; |
| }; |