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