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