Refactor: bios_parser: Implement a new parsing process

The new version parses the configuration file before `bios module` is loaded.

Assume that configuration files consist of bios attributes. For each attribute,
Parsing process has the following 3 steps.

1. Get bios strings for BIOS String Table
2. Implement an `attribute to dbus object` mapping
3. Get the type-related values for the attribute

1), 3) are type-related, need to implement handlers for those.

Signed-off-by: John Wang <wangzqbj@inspur.com>
Change-Id: Id7de7f163e75945fb4efdc9b58aa199cb363dd9b
diff --git a/libpldmresponder/bios_parser.cpp b/libpldmresponder/bios_parser.cpp
index 99c1149..30fee04 100644
--- a/libpldmresponder/bios_parser.cpp
+++ b/libpldmresponder/bios_parser.cpp
@@ -15,21 +15,38 @@
 namespace fs = std::filesystem;
 using namespace phosphor::logging;
 
-namespace
-{
-
 const std::vector<Json> emptyJsonList{};
 const Json emptyJson{};
 
-} // namespace
+struct DBusMapping
+{
+    std::string objectPath;   //!< D-Bus object path
+    std::string interface;    //!< D-Bus interface
+    std::string propertyName; //!< D-Bus property name
+};
 
-int parseBiosJsonFile(const char* dirPath, const std::string& fileName,
+using AttrName = std::string;
+using BIOSJsonName = std::string;
+using AttrLookup = std::map<AttrName, std::optional<DBusMapping>>;
+using BIOSStringHandler =
+    std::function<int(const Json& entry, Strings& strings)>;
+using AttrLookupHandler = std::function<int(const Json& entry, AttrLookup)>;
+using typeHandler = std::function<int(const Json& entry)>;
+
+Strings BIOSStrings;
+AttrLookup BIOSAttrLookup;
+
+const Strings& getStrings()
+{
+    return BIOSStrings;
+}
+
+int parseBiosJsonFile(const fs::path& dirPath, const std::string& fileName,
                       Json& fileData)
 {
     int rc = 0;
 
-    fs::path filePath(dirPath);
-    filePath /= fileName;
+    fs::path filePath = dirPath / fileName;
 
     std::ifstream jsonFile(filePath);
     if (!jsonFile.is_open())
@@ -63,45 +80,34 @@
                  uint64_t, double, std::string>;
 using Value = std::string;
 
-/** @struct DBusMapping
- *
- *  Data structure for storing information regarding BIOS enumeration attribute
- *  and the D-Bus object for the attribute.
+/** @brief Map of DBus property value to attribute value
  */
-struct DBusMapping
-{
-    std::string objectPath;   //!< D-Bus object path
-    std::string interface;    //!< D-Bus interface
-    std::string propertyName; //!< D-Bus property name
-    std::map<PropertyValue, Value>
-        dBusValToValMap; //!< Map of D-Bus property
-                         //!< value to attribute value
-};
+using DbusValToValMap = std::map<PropertyValue, Value>;
+
+/** @brief Map containing the DBus property value to attribute value map for the
+ * BIOS enumeration type attributes
+ */
+std::map<AttrName, DbusValToValMap> dbusValToValMaps;
 
 /** @brief Map containing the possible and the default values for the BIOS
  *         enumeration type attributes.
  */
 AttrValuesMap valueMap;
 
-/** @brief Map containing the optional D-Bus property information about the
- *         BIOS enumeration type attributes.
- */
-std::map<AttrName, std::optional<DBusMapping>> attrLookup;
-
 /** @brief Populate the mapping between D-Bus property value and attribute value
  *         for the BIOS enumeration attribute.
  *
  *  @param[in] type - type of the D-Bus property
  *  @param[in] dBusValues - json array of D-Bus property values
  *  @param[in] pv - Possible values for the BIOS enumeration attribute
- *  @param[out] mapping - D-Bus mapping object for the attribute
  *
  */
-void populateMapping(const std::string& type, const Json& dBusValues,
-                     const PossibleValues& pv, DBusMapping& mapping)
+DbusValToValMap populateMapping(const std::string& type, const Json& dBusValues,
+                                const PossibleValues& pv)
 {
     size_t pos = 0;
     PropertyValue value;
+    DbusValToValMap valueMap;
     for (auto it = dBusValues.begin(); it != dBusValues.end(); ++it, ++pos)
     {
         if (type == "uint8_t")
@@ -150,102 +156,57 @@
                             entry("TYPE=%s", type.c_str()));
         }
 
-        mapping.dBusValToValMap.emplace(value, pv[pos]);
-    }
-}
-
-/** @brief Read the possible values for the BIOS enumeration type
- *
- *  @param[in] possibleValues - json array of BIOS enumeration possible values
- */
-PossibleValues readPossibleValues(Json& possibleValues)
-{
-    Strings biosStrings{};
-
-    for (auto& val : possibleValues)
-    {
-        biosStrings.emplace_back(std::move(val));
+        valueMap.emplace(value, pv[pos]);
     }
 
-    return biosStrings;
+    return valueMap;
 }
 
 } // namespace internal
 
-int setupValueLookup(const char* dirPath)
+int setupBIOSStrings(const Json& entry, Strings& strings)
 {
-    int rc = 0;
+    Json pvs = entry.value("possible_values", emptyJsonList);
 
-    if (!internal::valueMap.empty() && !internal::attrLookup.empty())
+    for (auto& pv : pvs)
     {
-        return rc;
+        strings.emplace_back(std::move(pv.get<std::string>()));
     }
 
-    // Parse the BIOS enumeration config file
-    fs::path filePath(dirPath);
-    filePath /= bIOSEnumJson;
+    return 0;
+}
 
-    std::ifstream jsonFile(filePath);
-    if (!jsonFile.is_open())
+int setup(const Json& entry)
+{
+    PossibleValues possibleValues;
+    DefaultValues defaultValues;
+
+    std::string attrName = entry.value("attribute_name", "");
+    Json pv = entry["possible_values"];
+    for (auto& val : pv)
     {
-        log<level::ERR>("BIOS enum config file does not exist",
-                        entry("FILE=%s", filePath.c_str()));
-        rc = -1;
-        return rc;
+        possibleValues.emplace_back(std::move(val));
     }
-
-    auto fileData = Json::parse(jsonFile, nullptr, false);
-    if (fileData.is_discarded())
+    Json dv = entry["default_values"];
+    for (auto& val : dv)
     {
-        log<level::ERR>("Parsing config file failed");
-        rc = -1;
-        return rc;
+        defaultValues.emplace_back(std::move(val));
     }
-
-    auto entries = fileData.value("entries", emptyJsonList);
-    // Iterate through each JSON object in the config file
-    for (const auto& entry : entries)
+    if (entry.count("dbus") != 0)
     {
-        std::string attr = entry.value("attribute_name", "");
-        PossibleValues possibleValues;
-        DefaultValues defaultValues;
-
-        Json pv = entry["possible_values"];
-        for (auto& val : pv)
-        {
-            possibleValues.emplace_back(std::move(val));
-        }
-
-        Json dv = entry["default_values"];
-        for (auto& val : dv)
-        {
-            defaultValues.emplace_back(std::move(val));
-        }
-
-        std::optional<internal::DBusMapping> dBusMap = std::nullopt;
-
-        if (entry.count("dbus") != 0)
-        {
-            auto dBusEntry = entry.value("dbus", emptyJson);
-            dBusMap = std::make_optional<internal::DBusMapping>();
-            dBusMap.value().objectPath = dBusEntry.value("object_path", "");
-            dBusMap.value().interface = dBusEntry.value("interface", "");
-            dBusMap.value().propertyName = dBusEntry.value("property_name", "");
-            std::string propType = dBusEntry.value("property_type", "");
-            Json propValues = dBusEntry["property_values"];
-            internal::populateMapping(propType, propValues, possibleValues,
-                                      dBusMap.value());
-        }
-
-        internal::attrLookup.emplace(attr, std::move(dBusMap));
-
-        // Defaulting all the types of attributes to BIOSEnumeration
-        internal::valueMap.emplace(
-            std::move(attr), std::make_tuple(false, std::move(possibleValues),
-                                             std::move(defaultValues)));
+        auto dbusEntry = entry.value("dbus", emptyJson);
+        std::string propertyType = dbusEntry.value("property_type", "");
+        Json propValues = dbusEntry["property_values"];
+        internal::dbusValToValMaps.emplace(
+            attrName, internal::populateMapping(propertyType, propValues,
+                                                possibleValues));
     }
-
-    return rc;
+    // Defaulting all the types of attributes to BIOSEnumeration
+    internal::valueMap.emplace(std::move(attrName),
+                               std::make_tuple(entry.count("dbus") == 0,
+                                               std::move(possibleValues),
+                                               std::move(defaultValues)));
+    return 0;
 }
 
 const AttrValuesMap& getValues()
@@ -255,7 +216,7 @@
 
 CurrentValues getAttrValue(const AttrName& attrName)
 {
-    const auto& dBusMap = internal::attrLookup.at(attrName);
+    const auto& dBusMap = BIOSAttrLookup.at(attrName);
     CurrentValues currentValues;
     internal::PropertyValue propValue;
 
@@ -266,18 +227,19 @@
         return currentValues;
     }
 
+    const auto& dbusValToValMap = internal::dbusValToValMaps.at(attrName);
     auto bus = sdbusplus::bus::new_default();
     auto service = pldm::responder::getService(bus, dBusMap.value().objectPath,
                                                dBusMap.value().interface);
     auto method =
         bus.new_method_call(service.c_str(), dBusMap.value().objectPath.c_str(),
                             "org.freedesktop.DBus.Properties", "Get");
-    method.append(dBusMap.value().interface, dBusMap.value().propertyName);
+    method.append(dBusMap->interface, dBusMap->propertyName);
     auto reply = bus.call(method);
     reply.read(propValue);
 
-    auto iter = dBusMap.value().dBusValToValMap.find(propValue);
-    if (iter != dBusMap.value().dBusValToValMap.end())
+    auto iter = dbusValToValMap.find(propValue);
+    if (iter != dbusValToValMap.end())
     {
         currentValues.push_back(iter->second);
     }
@@ -315,118 +277,56 @@
 namespace internal
 {
 
-/** @struct DBusMapping
- *
- *  Data structure for storing information regarding BIOS string attribute
- *  and the D-Bus object for the attribute.
- */
-struct DBusMapping
-{
-    std::string objectPath;   //!< D-Bus object path
-    std::string interface;    //!< D-Bus interface
-    std::string propertyName; //!< D-Bus property name
-};
-
 /** @brief Map containing the possible and the default values for the BIOS
  *         string type attributes.
  */
 AttrValuesMap valueMap;
 
-/** @brief Map containing the optional D-Bus property information about the
- *         BIOS string type attributes.
- */
-std::map<AttrName, std::optional<DBusMapping>> attrLookup;
-
 } // namespace internal
 
-int setupValueLookup(const char* dirPath)
+int setup(const Json& entry)
 {
-    int rc = 0;
 
-    if (!internal::valueMap.empty() && !internal::attrLookup.empty())
+    std::string attr = entry.value("attribute_name", "");
+    // Transfer string type from string to enum
+    std::string strTypeTmp = entry.value("string_type", "Unknown");
+    auto iter = strTypeMap.find(strTypeTmp);
+    if (iter == strTypeMap.end())
     {
-        return rc;
+        log<level::ERR>(
+            "Wrong string type",
+            phosphor::logging::entry("STRING_TYPE=%s", strTypeTmp.c_str()),
+            phosphor::logging::entry("ATTRIBUTE_NAME=%s", attr.c_str()));
+        return -1;
+    }
+    uint8_t strType = iter->second;
+
+    uint16_t minStrLen = entry.value("minimum_string_length", 0);
+    uint16_t maxStrLen = entry.value("maximum_string_length", 0);
+    if (maxStrLen - minStrLen < 0)
+    {
+        log<level::ERR>(
+            "Maximum string length is smaller than minimum string length",
+            phosphor::logging::entry("ATTRIBUTE_NAME=%s", attr.c_str()));
+        return -1;
+    }
+    uint16_t defaultStrLen = entry.value("default_string_length", 0);
+    std::string defaultStr = entry.value("default_string", "");
+    if ((defaultStrLen == 0) && (defaultStr.size() > 0))
+    {
+        log<level::ERR>(
+            "Default string length is 0, but default string is existing",
+            phosphor::logging::entry("ATTRIBUTE_NAME=%s", attr.c_str()));
+        return -1;
     }
 
-    Json fileData;
-    rc = parseBiosJsonFile(dirPath, bIOSStrJson, fileData);
-    if (rc != 0)
-    {
-        return rc;
-    }
+    // Defaulting all the types of attributes to BIOSString
+    internal::valueMap.emplace(
+        std::move(attr),
+        std::make_tuple(entry.count("dbus") == 0, strType, minStrLen, maxStrLen,
+                        defaultStrLen, std::move(defaultStr)));
 
-    auto entries = fileData.value("entries", emptyJsonList);
-    // Iterate through each JSON object in the config file
-    for (const auto& entry : entries)
-    {
-        std::string attr = entry.value("attribute_name", "");
-        // Transfer string type from string to enum
-        std::string strTypeTmp = entry.value("string_type", "Unknown");
-        auto iter = strTypeMap.find(strTypeTmp);
-        if (iter == strTypeMap.end())
-        {
-            log<level::ERR>(
-                "Wrong string type",
-                phosphor::logging::entry("STRING_TYPE=%s", strTypeTmp.c_str()),
-                phosphor::logging::entry("ATTRIBUTE_NAME=%s", attr.c_str()));
-            return -1;
-        }
-        uint8_t strType = iter->second;
-
-        uint16_t minStrLen = entry.value("minimum_string_length", 0);
-        uint16_t maxStrLen = entry.value("maximum_string_length", 0);
-        if (maxStrLen - minStrLen < 0)
-        {
-            log<level::ERR>(
-                "Maximum string length is smaller than minimum string length",
-                phosphor::logging::entry("ATTRIBUTE_NAME=%s", attr.c_str()));
-            return -1;
-        }
-        uint16_t defaultStrLen = entry.value("default_string_length", 0);
-        std::string defaultStr = entry.value("default_string", "");
-        if ((defaultStrLen == 0) && (defaultStr.size() > 0))
-        {
-            log<level::ERR>(
-                "Default string length is 0, but default string is existing",
-                phosphor::logging::entry("ATTRIBUTE_NAME=%s", attr.c_str()));
-            return -1;
-        }
-
-        // dbus handling
-        std::optional<internal::DBusMapping> dBusMap;
-
-        if (entry.count("dbus") != 0)
-        {
-            auto dBusEntry = entry.value("dbus", emptyJson);
-            dBusMap = std::make_optional<internal::DBusMapping>();
-            dBusMap->objectPath = dBusEntry.value("object_path", "");
-            dBusMap->interface = dBusEntry.value("interface", "");
-            dBusMap->propertyName = dBusEntry.value("property_name", "");
-            if (dBusMap->objectPath.empty() || dBusMap->interface.empty() ||
-                dBusMap->propertyName.empty())
-            {
-                log<level::ERR>(
-                    "Invalid dbus config",
-                    phosphor::logging::entry("OBJPATH=%s",
-                                             dBusMap->objectPath.c_str()),
-                    phosphor::logging::entry("INTERFACE=%s",
-                                             dBusMap->interface.c_str()),
-                    phosphor::logging::entry("PROPERTY_NAME=%s",
-                                             dBusMap->propertyName.c_str()));
-                return -1;
-            }
-        }
-
-        internal::attrLookup.emplace(attr, std::move(dBusMap));
-
-        // Defaulting all the types of attributes to BIOSString
-        internal::valueMap.emplace(
-            std::move(attr),
-            std::make_tuple(entry.count("dbus") == 0, strType, minStrLen,
-                            maxStrLen, defaultStrLen, std::move(defaultStr)));
-    }
-
-    return rc;
+    return 0;
 }
 
 const AttrValuesMap& getValues()
@@ -436,7 +336,7 @@
 
 std::string getAttrValue(const AttrName& attrName)
 {
-    const auto& dBusMap = internal::attrLookup.at(attrName);
+    const auto& dBusMap = BIOSAttrLookup.at(attrName);
     std::variant<std::string> propValue;
 
     if (dBusMap == std::nullopt)
@@ -460,53 +360,105 @@
 
 } // namespace bios_string
 
-Strings getStrings(const char* dirPath)
-{
-    Strings biosStrings{};
-    fs::path dir(dirPath);
+const std::map<BIOSJsonName, BIOSStringHandler> BIOSStringHandlers = {
+    {bIOSEnumJson, bios_enum::setupBIOSStrings},
+};
 
+const std::map<BIOSJsonName, typeHandler> BIOSTypeHandlers = {
+    {bIOSEnumJson, bios_enum::setup},
+    {bIOSStrJson, bios_string::setup},
+};
+
+void setupBIOSStrings(const BIOSJsonName& jsonName, const Json& entry,
+                      Strings& strings)
+{
+    strings.emplace_back(entry.value("attribute_name", ""));
+    auto iter = BIOSStringHandlers.find(jsonName);
+    if (iter != BIOSStringHandlers.end())
+    {
+        iter->second(entry, strings);
+    }
+}
+
+void setupBIOSAttrLookup(const Json& entry, AttrLookup& lookup)
+{
+    std::optional<DBusMapping> dBusMap;
+    std::string attrName = entry.value("attribute_name", "");
+
+    if (entry.count("dbus") != 0)
+    {
+        auto dBusEntry = entry.value("dbus", emptyJson);
+        std::string objectPath = dBusEntry.value("object_path", "");
+        std::string interface = dBusEntry.value("interface", "");
+        std::string propertyName = dBusEntry.value("property_name", "");
+        if (!objectPath.empty() && !interface.empty() && !propertyName.empty())
+        {
+            dBusMap = std::optional<DBusMapping>(
+                {objectPath, interface, propertyName});
+        }
+        else
+        {
+            log<level::ERR>(
+                "Invalid dbus config",
+                phosphor::logging::entry("OBJPATH=%s",
+                                         dBusMap->objectPath.c_str()),
+                phosphor::logging::entry("INTERFACE=%s",
+                                         dBusMap->interface.c_str()),
+                phosphor::logging::entry("PROPERTY_NAME=%s",
+                                         dBusMap->propertyName.c_str()));
+        }
+    }
+    lookup.emplace(attrName, dBusMap);
+}
+
+int setupBIOSType(const BIOSJsonName& jsonName, const Json& entry)
+{
+    auto iter = BIOSTypeHandlers.find(jsonName);
+    if (iter != BIOSTypeHandlers.end())
+    {
+        iter->second(entry);
+    }
+    return 0;
+}
+
+const std::vector<BIOSJsonName> BIOSConfigFiles = {bIOSEnumJson, bIOSStrJson};
+
+int setupConfig(const char* dirPath)
+{
+    if (!BIOSStrings.empty() && !BIOSAttrLookup.empty())
+    {
+        return 0;
+    }
+
+    fs::path dir(dirPath);
     if (!fs::exists(dir) || fs::is_empty(dir))
     {
-        return biosStrings;
+        log<level::ERR>("BIOS config directory does not exist or empty",
+                        entry("DIR=%s", dirPath));
+        return -1;
     }
-
-    for (const auto& file : fs::directory_iterator(dir))
+    for (auto jsonName : BIOSConfigFiles)
     {
-        std::ifstream jsonFile(file.path().c_str());
-        if (!jsonFile.is_open())
+        Json json;
+        if (parseBiosJsonFile(dir, jsonName, json) < 0)
         {
-            log<level::ERR>("JSON BIOS config file does not exist",
-                            entry("FILE=%s", file.path().filename().c_str()));
             continue;
         }
-
-        auto fileData = Json::parse(jsonFile, nullptr, false);
-        if (fileData.is_discarded())
-        {
-            log<level::ERR>("Parsing config file failed",
-                            entry("FILE=%s", file.path().filename().c_str()));
-            continue;
-        }
-
-        auto entries = fileData.value("entries", emptyJsonList);
-
-        // Iterate through each entry in the config file
+        auto entries = json.value("entries", emptyJsonList);
         for (auto& entry : entries)
         {
-            biosStrings.emplace_back(entry.value("attribute_name", ""));
-
-            // For BIOS enumeration attributes the possible values are strings
-            if (file.path().filename() == bIOSEnumJson)
-            {
-                auto possibleValues = bios_enum::internal::readPossibleValues(
-                    entry["possible_values"]);
-                std::move(possibleValues.begin(), possibleValues.end(),
-                          std::back_inserter(biosStrings));
-            }
+            setupBIOSStrings(jsonName, entry, BIOSStrings);
+            setupBIOSAttrLookup(entry, BIOSAttrLookup);
+            setupBIOSType(jsonName, entry);
         }
     }
-
-    return biosStrings;
+    if (BIOSStrings.empty())
+    { // means there is no attribute
+        log<level::ERR>("No attribute is found in the config directory",
+                        entry("DIR=%s", dirPath));
+        return -1;
+    }
+    return 0;
 }
 
 } // namespace bios_parser