config_parser: Cleanups and modern c++ standards

This was exposing many functions and semantics that are unused by the
application. The goal is to simplify the interface and convert to using
types like `string_view` and referenceable lists where possible.

Change-Id: I4cba6326f9a96a943d384165e656f8589f931959
Signed-off-by: William A. Kennington III <wak@google.com>
diff --git a/src/config_parser.cpp b/src/config_parser.cpp
index 1525c5c..a6b7e36 100644
--- a/src/config_parser.cpp
+++ b/src/config_parser.cpp
@@ -1,12 +1,8 @@
 #include "config_parser.hpp"
 
-#include <algorithm>
 #include <fstream>
-#include <list>
-#include <phosphor-logging/log.hpp>
 #include <regex>
 #include <string>
-#include <unordered_map>
 
 namespace phosphor
 {
@@ -15,126 +11,61 @@
 namespace config
 {
 
-using namespace phosphor::logging;
-
-Parser::Parser(const fs::path& filePath)
+Parser::Parser(const fs::path& filename)
 {
-    setFile(filePath);
+    setFile(filename);
 }
 
-std::tuple<ReturnCode, KeyValueMap>
-    Parser::getSection(const std::string& section)
+const ValueList& Parser::getValues(std::string_view section,
+                                   std::string_view key) const noexcept
 {
-    auto it = sections.find(section);
-    if (it == sections.end())
+    static const ValueList empty;
+    auto sit = sections.find(section);
+    if (sit == sections.end())
     {
-        KeyValueMap keyValues;
-        return std::make_tuple(ReturnCode::SECTION_NOT_FOUND,
-                               std::move(keyValues));
+        return empty;
     }
 
-    return std::make_tuple(ReturnCode::SUCCESS, it->second);
-}
-
-std::tuple<ReturnCode, ValueList> Parser::getValues(const std::string& section,
-                                                    const std::string& key)
-{
-    ValueList values;
-    KeyValueMap keyValues{};
-    auto rc = ReturnCode::SUCCESS;
-
-    std::tie(rc, keyValues) = getSection(section);
-    if (rc != ReturnCode::SUCCESS)
+    auto kit = sit->second.find(key);
+    if (kit == sit->second.end())
     {
-        return std::make_tuple(rc, std::move(values));
+        return empty;
     }
 
-    auto it = keyValues.find(key);
-    if (it == keyValues.end())
-    {
-        return std::make_tuple(ReturnCode::KEY_NOT_FOUND, std::move(values));
-    }
-
-    for (; it != keyValues.end() && key == it->first; it++)
-    {
-        values.push_back(it->second);
-    }
-
-    return std::make_tuple(ReturnCode::SUCCESS, std::move(values));
-}
-
-bool Parser::isValueExist(const std::string& section, const std::string& key,
-                          const std::string& value)
-{
-    auto rc = ReturnCode::SUCCESS;
-    ValueList values;
-    std::tie(rc, values) = getValues(section, key);
-
-    if (rc != ReturnCode::SUCCESS)
-    {
-        return false;
-    }
-    auto it = std::find(values.begin(), values.end(), value);
-    return it != std::end(values) ? true : false;
+    return kit->second;
 }
 
 void Parser::setValue(const std::string& section, const std::string& key,
                       const std::string& value)
 {
-    KeyValueMap values;
-    auto it = sections.find(section);
-    if (it != sections.end())
+    auto sit = sections.find(section);
+    if (sit == sections.end())
     {
-        values = std::move(it->second);
+        std::tie(sit, std::ignore) = sections.emplace(section, KeyValuesMap{});
     }
-    values.insert(std::make_pair(key, value));
-
-    if (it != sections.end())
+    auto kit = sit->second.find(key);
+    if (kit == sit->second.end())
     {
-        it->second = std::move(values);
+        std::tie(kit, std::ignore) = sit->second.emplace(key, ValueList{});
     }
-    else
-    {
-        sections.insert(std::make_pair(section, std::move(values)));
-    }
+    kit->second.push_back(value);
 }
 
-#if 0
-void Parser::print()
+void Parser::setFile(const fs::path& filename)
 {
-    for (auto section : sections)
-    {
-        std::cout << "[" << section.first << "]\n\n";
-        for (auto keyValue : section.second)
-        {
-            std::cout << keyValue.first << "=" << keyValue.second << "\n";
-        }
-    }
-}
-#endif
-
-void Parser::setFile(const fs::path& filePath)
-{
-    this->filePath = filePath;
-    std::fstream stream(filePath, std::fstream::in);
-
+    std::fstream stream(filename, std::fstream::in);
     if (!stream.is_open())
     {
         return;
     }
     // clear all the section data.
     sections.clear();
-    parse(stream);
-}
-
-void Parser::parse(std::istream& in)
-{
     static const std::regex commentRegex{R"x(\s*[;#])x"};
     static const std::regex sectionRegex{R"x(\s*\[([^\]]+)\])x"};
     static const std::regex valueRegex{R"x(\s*(\S[^ \t=]*)\s*=\s*(\S+)\s*$)x"};
     std::string section;
     std::smatch pieces;
-    for (std::string line; std::getline(in, line);)
+    for (std::string line; std::getline(stream, line);)
     {
         if (line.empty() || std::regex_match(line, pieces, commentRegex))
         {
diff --git a/src/config_parser.hpp b/src/config_parser.hpp
index 8af4a34..3065162 100644
--- a/src/config_parser.hpp
+++ b/src/config_parser.hpp
@@ -1,9 +1,8 @@
 #pragma once
 
 #include <filesystem>
-#include <map>
 #include <string>
-#include <tuple>
+#include <string_view>
 #include <unordered_map>
 #include <vector>
 
@@ -14,82 +13,56 @@
 namespace config
 {
 
+struct string_hash : public std::hash<std::string_view>
+{
+    using is_transparent = void;
+};
+
+using Key = std::string;
 using Section = std::string;
-using KeyValueMap = std::multimap<std::string, std::string>;
-using ValueList = std::vector<std::string>;
+using Value = std::string;
+using ValueList = std::vector<Value>;
+using KeyValuesMap =
+    std::unordered_map<Key, ValueList, string_hash, std::equal_to<>>;
+using SectionMap =
+    std::unordered_map<Section, KeyValuesMap, string_hash, std::equal_to<>>;
 
 namespace fs = std::filesystem;
 
-enum class ReturnCode
-{
-    SUCCESS = 0x0,
-    SECTION_NOT_FOUND = 0x1,
-    KEY_NOT_FOUND = 0x2,
-};
-
 class Parser
 {
   public:
     Parser() = default;
 
     /** @brief Constructor
-     *  @param[in] fileName - Absolute path of the file which will be parsed.
+     *  @param[in] filename - Absolute path of the file which will be parsed.
      */
 
-    Parser(const fs::path& fileName);
+    Parser(const fs::path& filename);
 
     /** @brief Get the values of the given key and section.
      *  @param[in] section - section name.
      *  @param[in] key - key to look for.
-     *  @returns the tuple of return code and the
-     *           values associated with the key.
+     *  @returns   The ValueList or nullptr if no key + section exists.
      */
-
-    std::tuple<ReturnCode, ValueList> getValues(const std::string& section,
-                                                const std::string& key);
+    const ValueList& getValues(std::string_view section,
+                               std::string_view key) const noexcept;
 
     /** @brief Set the value of the given key and section.
      *  @param[in] section - section name.
      *  @param[in] key - key name.
      *  @param[in] value - value.
      */
-
     void setValue(const std::string& section, const std::string& key,
                   const std::string& value);
 
     /** @brief Set the file name and parse it.
-     *  @param[in] fileName - Absolute path of the file.
+     *  @param[in] filename - Absolute path of the file.
      */
-
-    void setFile(const fs::path& fileName);
+    void setFile(const fs::path& filename);
 
   private:
-    /** @brief Parses the given file and fills the data.
-     *  @param[in] stream - inputstream.
-     */
-
-    void parse(std::istream& stream);
-
-    /** @brief Get all the key values of the given section.
-     *  @param[in] section - section name.
-     *  @returns the tuple of return code and the map of (key,value).
-     */
-
-    std::tuple<ReturnCode, KeyValueMap> getSection(const std::string& section);
-
-    /** @brief checks that whether the value exist in the
-     *         given section.
-     *  @param[in] section - section name.
-     *  @param[in] key - key name.
-     *  @param[in] value - value.
-     *  @returns true if exist otherwise false.
-     */
-
-    bool isValueExist(const std::string& section, const std::string& key,
-                      const std::string& value);
-
-    std::unordered_map<Section, KeyValueMap> sections;
-    fs::path filePath;
+    SectionMap sections;
 };
 
 } // namespace config
diff --git a/src/dhcp_configuration.cpp b/src/dhcp_configuration.cpp
index 6fb24d0..0605a6a 100644
--- a/src/dhcp_configuration.cpp
+++ b/src/dhcp_configuration.cpp
@@ -4,6 +4,8 @@
 
 #include "network_manager.hpp"
 
+#include <fmt/format.h>
+
 #include <phosphor-logging/elog-errors.hpp>
 #include <phosphor-logging/log.hpp>
 #include <xyz/openbmc_project/Common/error.hpp>
@@ -87,25 +89,16 @@
     confPath /= fileName;
     // systemd default behaviour is all DHCP fields should be enabled by
     // default.
-    auto propValue = true;
     config::Parser parser(confPath);
 
-    auto rc = config::ReturnCode::SUCCESS;
-    config::ValueList values{};
-    std::tie(rc, values) = parser.getValues("DHCP", prop);
-
-    if (rc != config::ReturnCode::SUCCESS)
+    const auto& values = parser.getValues("DHCP", prop);
+    if (values.empty())
     {
-        log<level::DEBUG>("Unable to get the value from section DHCP",
-                          entry("PROP=%s", prop.c_str()), entry("RC=%d", rc));
-        return propValue;
+        auto msg = fmt::format("Missing config section DHCP[{}]", prop);
+        log<level::NOTICE>(msg.c_str(), entry("PROP=%s", prop.c_str()));
+        return true;
     }
-
-    if (values[0] == "false")
-    {
-        propValue = false;
-    }
-    return propValue;
+    return values.back() != "false";
 }
 } // namespace dhcp
 } // namespace network
diff --git a/src/ethernet_interface.cpp b/src/ethernet_interface.cpp
index 7f4f930..825ce62 100644
--- a/src/ethernet_interface.cpp
+++ b/src/ethernet_interface.cpp
@@ -809,17 +809,8 @@
     std::string fileName = systemd::config::networkFilePrefix +
                            interfaceName() + systemd::config::networkFileSuffix;
     confPath /= fileName;
-    ServerList servers;
     config::Parser parser(confPath.string());
-    auto rc = config::ReturnCode::SUCCESS;
-
-    std::tie(rc, servers) = parser.getValues("Network", "DNS");
-    if (rc != config::ReturnCode::SUCCESS)
-    {
-        log<level::DEBUG>("Unable to get the value for network[DNS]",
-                          entry("RC=%d", rc));
-    }
-    return servers;
+    return parser.getValues("Network", "DNS");
 }
 
 ServerList EthernetInterface::getNameServerFromResolvd()
@@ -963,17 +954,14 @@
     std::string fileName = systemd::config::networkFilePrefix +
                            interfaceName() + systemd::config::networkFileSuffix;
     confPath /= fileName;
-    config::ValueList values;
-    config::Parser parser(confPath.string());
-    auto rc = config::ReturnCode::SUCCESS;
-    std::tie(rc, values) = parser.getValues("Network", "IPv6AcceptRA");
-    if (rc != config::ReturnCode::SUCCESS)
+    config::Parser parser(confPath);
+    const auto& values = parser.getValues("Network", "IPv6AcceptRA");
+    if (values.empty())
     {
-        log<level::DEBUG>("Unable to get the value for Network[IPv6AcceptRA]",
-                          entry("rc=%d", rc));
+        log<level::NOTICE>("Unable to get the value for Network[IPv6AcceptRA]");
         return false;
     }
-    return (values[0] == "true");
+    return values.back() == "true";
 }
 
 ServerList EthernetInterface::getNTPServersFromConf()
@@ -984,18 +972,8 @@
                            interfaceName() + systemd::config::networkFileSuffix;
     confPath /= fileName;
 
-    ServerList servers;
     config::Parser parser(confPath.string());
-    auto rc = config::ReturnCode::SUCCESS;
-
-    std::tie(rc, servers) = parser.getValues("Network", "NTP");
-    if (rc != config::ReturnCode::SUCCESS)
-    {
-        log<level::DEBUG>("Unable to get the value for Network[NTP]",
-                          entry("rc=%d", rc));
-    }
-
-    return servers;
+    return parser.getValues("Network", "NTP");
 }
 
 ServerList EthernetInterface::ntpServers(ServerList servers)
diff --git a/src/util.cpp b/src/util.cpp
index 803d26e..de0bc9d 100644
--- a/src/util.cpp
+++ b/src/util.cpp
@@ -376,27 +376,22 @@
                            systemd::config::networkFileSuffix;
     confPath /= fileName;
 
-    auto rc = config::ReturnCode::SUCCESS;
-    config::ValueList values;
     config::Parser parser(confPath.string());
-
-    std::tie(rc, values) = parser.getValues("Network", "DHCP");
-    if (rc != config::ReturnCode::SUCCESS)
+    const auto& values = parser.getValues("Network", "DHCP");
+    if (values.empty())
     {
-        log<level::DEBUG>("Unable to get the value for Network[DHCP]",
-                          entry("RC=%d", rc));
+        log<level::NOTICE>("Unable to get the value for Network[DHCP]");
         return dhcp;
     }
-    // There will be only single value for DHCP key.
-    if (values[0] == "true")
+    if (values.back() == "true")
     {
         dhcp = EthernetInterfaceIntf::DHCPConf::both;
     }
-    else if (values[0] == "ipv4")
+    else if (values.back() == "ipv4")
     {
         dhcp = EthernetInterfaceIntf::DHCPConf::v4;
     }
-    else if (values[0] == "ipv6")
+    else if (values.back() == "ipv6")
     {
         dhcp = EthernetInterfaceIntf::DHCPConf::v6;
     }