blob: 8882cc52e07a8657188f52d00b1a282ece544ffb [file] [log] [blame]
Abhilash Raju93804eb2024-10-01 00:24:43 -05001#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
16class JsonSerializer
17{
18 public:
19 JsonSerializer(std::string path, nlohmann::json js = nlohmann::json()) :
20 mfaConfPath(path), jsonData(std::move(js))
21 {}
22
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 }
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 }
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 }
58 void serialize(std::string key, const std::string value)
59 {
60 jsonData.merge_patch(makeJson(key, value));
61 }
62 template <typename T>
63 void deserialize(std::string key, T& value)
64 {
65 auto leaf = getLeafNode(key);
66 if (leaf)
67 {
68 value = *leaf;
69 }
70 }
71 void erase(std::string key)
72 {
73 if (jsonData.contains(key))
74 {
75 jsonData.erase(key);
76 }
77 }
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 }
Patrick Williamsf2196652025-07-02 16:06:56 -0400108 bool load()
Abhilash Raju93804eb2024-10-01 00:24:43 -0500109 {
110 std::ifstream file(mfaConfPath.data());
111
112 if (file.is_open())
113 {
Patrick Williamsf2196652025-07-02 16:06:56 -0400114 try
115 {
116 file >> jsonData;
117 file.close();
118 return true;
119 }
120 catch (const nlohmann::json::parse_error& e)
121 {
122 lg2::error("JSON parsing error: {MSG} in file {FILENAME}",
123 "MSG", e.what(), "FILENAME", mfaConfPath);
124 if (std::filesystem::exists(mfaConfPath))
125 {
126 std::filesystem::remove(mfaConfPath);
127 }
128 file.close();
129 return false;
130 }
Abhilash Raju93804eb2024-10-01 00:24:43 -0500131 }
132 else
133 {
134 lg2::error("Unable to open file for reading {FILENAME}", "FILENAME",
135 mfaConfPath);
Patrick Williamsf2196652025-07-02 16:06:56 -0400136 return false;
Abhilash Raju93804eb2024-10-01 00:24:43 -0500137 }
138 }
139
140 private:
141 const std::string mfaConfPath;
142 nlohmann::json jsonData;
143};