xref: /openbmc/phosphor-user-manager/json_serializer.hpp (revision 93804eba13ade6aebfa38eaac5349b560b5cae33)
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