config_parser: Add type checking to config map
In order to guarantee the output file is consistent, the
constructed values are checked for safety.
Change-Id: Ib70e369471e9f2f47a1cdb5522f4a3bebc37805e
Signed-off-by: William A. Kennington III <wak@google.com>
diff --git a/src/config_parser.cpp b/src/config_parser.cpp
index 80f71e7..867d611 100644
--- a/src/config_parser.cpp
+++ b/src/config_parser.cpp
@@ -5,6 +5,7 @@
#include <functional>
#include <iterator>
+#include <stdexcept>
#include <stdplus/exception.hpp>
#include <stdplus/fd/create.hpp>
#include <stdplus/fd/line.hpp>
@@ -67,7 +68,7 @@
{
continue;
}
- return &kit->second.back();
+ return &kit->second.back().get();
}
return nullptr;
}
@@ -79,6 +80,42 @@
[](const Value& v) { return std::string(v); });
}
+void KeyCheck::operator()(const std::string& s)
+{
+ for (auto c : s)
+ {
+ if (c == '\n' || c == '=')
+ {
+ throw std::invalid_argument(
+ fmt::format(FMT_COMPILE("Invalid Config Key: {}"), s));
+ }
+ }
+}
+
+void SectionCheck::operator()(const std::string& s)
+{
+ for (auto c : s)
+ {
+ if (c == '\n' || c == ']')
+ {
+ throw std::invalid_argument(
+ fmt::format(FMT_COMPILE("Invalid Config Section: {}"), s));
+ }
+ }
+}
+
+void ValueCheck::operator()(const std::string& s)
+{
+ for (auto c : s)
+ {
+ if (c == '\n')
+ {
+ throw std::invalid_argument(
+ fmt::format(FMT_COMPILE("Invalid Config Value: {}"), s));
+ }
+ }
+}
+
Parser::Parser(const fs::path& filename)
{
setFile(filename);
@@ -145,8 +182,8 @@
auto it = sections.find(s);
if (it == sections.end())
{
- std::tie(it, std::ignore) =
- sections.emplace(Section(s), KeyValuesMapList{});
+ std::tie(it, std::ignore) = sections.emplace(
+ Section(Section::unchecked(), s), KeyValuesMapList{});
}
section = &it->second.emplace_back();
}
@@ -181,9 +218,10 @@
auto it = section->find(k);
if (it == section->end())
{
- std::tie(it, std::ignore) = section->emplace(Key(k), ValueList{});
+ std::tie(it, std::ignore) =
+ section->emplace(Key(Key::unchecked(), k), ValueList{});
}
- it->second.emplace_back(v);
+ it->second.emplace_back(Value::unchecked(), v);
}
void pump(std::string_view line)
diff --git a/src/config_parser.hpp b/src/config_parser.hpp
index 9b203c2..6b633e9 100644
--- a/src/config_parser.hpp
+++ b/src/config_parser.hpp
@@ -3,6 +3,7 @@
#include <filesystem>
#include <functional>
#include <optional>
+#include <ostream>
#include <string>
#include <string_view>
#include <unordered_map>
@@ -25,14 +26,98 @@
fs::path pathForIntfConf(const fs::path& dir, std::string_view intf);
fs::path pathForIntfDev(const fs::path& dir, std::string_view intf);
+template <typename T, typename Check>
+class Checked
+{
+ public:
+ struct unchecked
+ {
+ };
+
+ template <typename... Args>
+ inline constexpr Checked(Args&&... args) :
+ t(conCheck(std::forward<Args>(args)...))
+ {
+ }
+
+ template <typename... Args>
+ inline constexpr Checked(unchecked, Args&&... args) :
+ t(std::forward<Args>(args)...)
+ {
+ }
+
+ inline const T& get() const noexcept
+ {
+ return t;
+ }
+
+ inline constexpr operator const T&() const noexcept
+ {
+ return t;
+ }
+
+ inline constexpr bool operator==(const auto& rhs) const
+ {
+ return t == rhs;
+ }
+
+ private:
+ T t;
+
+ template <typename... Args>
+ inline static constexpr T conCheck(Args&&... args)
+ {
+ T t(std::forward<Args>(args)...);
+ Check{}(t);
+ return t;
+ }
+};
+
+template <typename T, typename Check>
+inline constexpr bool operator==(const auto& lhs, const Checked<T, Check>& rhs)
+{
+ return lhs == rhs.get();
+}
+
+template <typename T, typename Check>
+inline constexpr std::ostream& operator<<(std::ostream& s,
+ const Checked<T, Check>& rhs)
+{
+ return s << rhs.get();
+}
+
+struct KeyCheck
+{
+ void operator()(const std::string& s);
+};
+struct SectionCheck
+{
+ void operator()(const std::string& s);
+};
+struct ValueCheck
+{
+ void operator()(const std::string& s);
+};
+
struct string_hash : public std::hash<std::string_view>
{
using is_transparent = void;
+
+ template <typename T>
+ inline size_t operator()(const Checked<std::string, T>& t) const
+ {
+ return static_cast<const std::hash<std::string_view>&>(*this)(t.get());
+ }
+ template <typename T>
+ inline size_t operator()(const T& t) const
+ {
+ return static_cast<const std::hash<std::string_view>&>(*this)(t);
+ }
};
-using Key = std::string;
-using Section = std::string;
-using Value = std::string;
+using Key = Checked<std::string, KeyCheck>;
+using Section = Checked<std::string, SectionCheck>;
+using Value = Checked<std::string, ValueCheck>;
using ValueList = std::vector<Value>;
using KeyValuesMap =
std::unordered_map<Key, ValueList, string_hash, std::equal_to<>>;