1 #pragma once
2 
3 #include <filesystem>
4 #include <functional>
5 #include <optional>
6 #include <string>
7 #include <string_view>
8 #include <unordered_map>
9 #include <vector>
10 
11 namespace phosphor
12 {
13 namespace network
14 {
15 namespace config
16 {
17 
18 /** @brief Compare in (case insensitive) vs expected (sensitive) */
19 bool icaseeq(std::string_view in, std::string_view expected) noexcept;
20 /** @brief Turns a systemd bool string into a c++ bool */
21 std::optional<bool> parseBool(std::string_view in) noexcept;
22 
23 namespace fs = std::filesystem;
24 
25 fs::path pathForIntfConf(const fs::path& dir, std::string_view intf);
26 fs::path pathForIntfDev(const fs::path& dir, std::string_view intf);
27 
28 template <typename T, typename Check>
29 class Checked
30 {
31   public:
32     struct unchecked
33     {};
34 
35     template <typename... Args>
Checked(Args &&...args)36     constexpr Checked(Args&&... args) : t(conCheck(std::forward<Args>(args)...))
37     {}
38 
39     template <typename... Args>
Checked(unchecked,Args &&...args)40     constexpr Checked(unchecked, Args&&... args) :
41         t(std::forward<Args>(args)...)
42     {}
43 
get() const44     constexpr const T& get() const noexcept
45     {
46         return t;
47     }
48 
operator const T&() const49     constexpr operator const T&() const noexcept
50     {
51         return t;
52     }
53 
54     template <typename T2, typename Check2>
operator ==(const Checked<T2,Check2> & rhs) const55     constexpr bool operator==(const Checked<T2, Check2>& rhs) const noexcept
56     {
57         static_assert(std::is_same_v<Check2, Check>);
58         return t == rhs.t;
59     }
60 
operator ==(const auto & rhs) const61     constexpr bool operator==(const auto& rhs) const noexcept
62     {
63         return t == rhs;
64     }
65 
66   private:
67     T t;
68 
69     template <typename... Args>
conCheck(Args &&...args)70     static constexpr T conCheck(Args&&... args)
71     {
72         T t(std::forward<Args>(args)...);
73         Check{}(t);
74         return t;
75     }
76 };
77 
78 struct KeyCheck
79 {
80     void operator()(std::string_view s);
81 };
82 struct SectionCheck
83 {
84     void operator()(std::string_view s);
85 };
86 struct ValueCheck
87 {
88     void operator()(std::string_view s);
89 };
90 
91 struct string_hash : public std::hash<std::string_view>
92 {
93     using is_transparent = void;
94 
95     template <typename T>
operator ()phosphor::network::config::string_hash96     inline size_t operator()(const Checked<std::string, T>& t) const
97     {
98         return static_cast<const std::hash<std::string_view>&>(*this)(t.get());
99     }
100     template <typename T>
operator ()phosphor::network::config::string_hash101     inline size_t operator()(const T& t) const
102     {
103         return static_cast<const std::hash<std::string_view>&>(*this)(t);
104     }
105 };
106 
107 using Key = Checked<std::string, KeyCheck>;
108 using Section = Checked<std::string, SectionCheck>;
109 using Value = Checked<std::string, ValueCheck>;
110 using ValueList = std::vector<Value>;
111 using KeyValuesMap =
112     std::unordered_map<Key, ValueList, string_hash, std::equal_to<>>;
113 using KeyValuesMapList = std::vector<KeyValuesMap>;
114 using SectionMapInt =
115     std::unordered_map<Section, KeyValuesMapList, string_hash, std::equal_to<>>;
116 
117 class SectionMap : public SectionMapInt
118 {
119   public:
120     const std::string* getLastValueString(std::string_view section,
121                                           std::string_view key) const noexcept;
getValues(std::string_view section,std::string_view key,auto && conv) const122     inline auto getValues(std::string_view section, std::string_view key,
123                           auto&& conv) const
124     {
125         std::vector<std::invoke_result_t<decltype(conv), const Value&>> values;
126         auto sit = find(section);
127         if (sit == end())
128         {
129             return values;
130         }
131         for (const auto& secv : sit->second)
132         {
133             auto kit = secv.find(key);
134             if (kit == secv.end())
135             {
136                 continue;
137             }
138             for (auto v : kit->second)
139             {
140                 values.push_back(conv(v));
141             }
142         }
143         return values;
144     }
145     std::vector<std::string> getValueStrings(std::string_view section,
146                                              std::string_view key) const;
147 };
148 
149 class Parser
150 {
151   public:
152     SectionMap map;
153 
154     Parser() = default;
155 
156     /** @brief Constructor
157      *  @param[in] filename - Absolute path of the file which will be parsed.
158      */
159     Parser(const fs::path& filename);
160 
161     /** @brief Determine if the loaded file exists */
getFileExists() const162     inline bool getFileExists() const noexcept
163     {
164         return fileExists;
165     }
166 
167     /** @brief Determine if there were warnings parsing the file
168      *  @return The number of parsing issues in the file
169      */
getWarnings() const170     inline const std::vector<std::string>& getWarnings() const noexcept
171     {
172         return warnings;
173     }
174 
175     /** @brief Get the filename last parsed successfully
176      *  @return file path
177      */
getFilename() const178     inline const fs::path& getFilename() const noexcept
179     {
180         return filename;
181     }
182 
183     /** @brief Set the file name and parse it.
184      *  @param[in] filename - Absolute path of the file.
185      */
186     void setFile(const fs::path& filename);
187 
188     /** @brief Write the current config to a file */
189     void writeFile() const;
190     void writeFile(const fs::path& filename);
191 
192   private:
193     bool fileExists = false;
194     fs::path filename;
195     std::vector<std::string> warnings;
196 };
197 
198 } // namespace config
199 } // namespace network
200 } // namespace phosphor
201