Custom Exception type for openpower-vpd-parser.

This commit implements custom exception type for openpower-vpd-parser
repo.
With this runtime exceptions can be classified into categories and
appropriate actions can be taken based on their type.

Signed-off-by: Sunny Srivastava <sunnsr25@in.ibm.com>
Change-Id: Ia514141e9166a76ba43c536a2e0f1229bd544bae
diff --git a/impl.cpp b/impl.cpp
index c7036b7..b64ba89 100644
--- a/impl.cpp
+++ b/impl.cpp
@@ -3,6 +3,7 @@
 #include "const.hpp"
 #include "defines.hpp"
 #include "utils.hpp"
+#include "vpd_exceptions.hpp"
 
 #include <algorithm>
 #include <exception>
@@ -22,6 +23,7 @@
 namespace parser
 {
 using namespace openpower::vpd::constants;
+using namespace openpower::vpd::exceptions;
 
 static const std::unordered_map<std::string, Record> supportedRecords = {
     {"VINI", Record::VINI}, {"OPFR", Record::OPFR}, {"OSYS", Record::OSYS}};
@@ -71,7 +73,6 @@
 }
 
 #ifdef IPZ_PARSER
-
 int Impl::vhdrEccCheck() const
 {
     int rc = eccStatus::SUCCESS;
@@ -82,6 +83,7 @@
                           lengths::VHDR_RECORD_LENGTH,
                           const_cast<uint8_t*>(&vpdPtr[offsets::VHDR_ECC]),
                           lengths::VHDR_ECC_LENGTH);
+
     if (l_status != VPD_ECC_OK)
     {
         rc = eccStatus::FAILED;
@@ -119,6 +121,7 @@
     auto l_status = vpdecc_check_data(
         const_cast<uint8_t*>(&vpdPtr[vtocOffset]), vtocLength,
         const_cast<uint8_t*>(&vpdPtr[vtocECCOffset]), vtocECCLength);
+
     if (l_status != VPD_ECC_OK)
     {
         rc = eccStatus::FAILED;
@@ -142,11 +145,15 @@
     std::advance(iterator, sizeof(ECCOffset));
     auto eccLength = readUInt16LE(iterator);
 
-    if (eccLength == 0 || eccOffset == 0 || recordOffset == 0 ||
-        recordLength == 0)
+    if (eccLength == 0 || eccOffset == 0)
     {
-        throw std::runtime_error("Something went wrong. Could't find Record's "
-                                 "OR its ECC's offset and Length");
+        throw(VpdEccException("Could not find ECC's offset or Length"));
+    }
+
+    if (recordOffset == 0 || recordLength == 0)
+    {
+        throw(VpdDataException(
+            "Could not find VPD record offset or VPD record length"));
     }
 
     auto vpdPtr = vpd.cbegin();
@@ -167,7 +174,7 @@
 {
     if (vpd.empty() || (lengths::RECORD_MIN > vpd.size()))
     {
-        throw std::runtime_error("Malformed VPD");
+        throw(VpdDataException("Malformed VPD"));
     }
     else
     {
@@ -177,7 +184,7 @@
         std::string record(iterator, stop);
         if ("VHDR" != record)
         {
-            throw std::runtime_error("VHDR record not found");
+            throw(VpdDataException("VHDR record not found"));
         }
 
 #ifdef IPZ_PARSER
@@ -186,7 +193,7 @@
         rc = vhdrEccCheck();
         if (rc != eccStatus::SUCCESS)
         {
-            throw std::runtime_error("ERROR: VHDR ECC check Failed");
+            throw(VpdEccException("ERROR: VHDR ECC check Failed"));
         }
 #endif
     }
@@ -208,7 +215,7 @@
     std::string record(iterator, stop);
     if ("VTOC" != record)
     {
-        throw std::runtime_error("VTOC record not found");
+        throw(VpdDataException("VTOC record not found"));
     }
 
 #ifdef IPZ_PARSER
@@ -217,7 +224,7 @@
     rc = vtocEccCheck();
     if (rc != eccStatus::SUCCESS)
     {
-        throw std::runtime_error("ERROR: VTOC ECC check Failed");
+        throw(VpdEccException("ERROR: VTOC ECC check Failed"));
     }
 #endif
     // VTOC record name is good, now read through the TOC, stored in the PT
@@ -246,6 +253,9 @@
     // we care only about the record offset information.
     while (iterator < end)
     {
+#ifdef IPZ_PARSER
+        auto iteratorToRecName = iterator;
+#endif
         // Skip record name and record type
         std::advance(iterator, lengths::RECORD_NAME + sizeof(RecordType));
 
@@ -259,8 +269,13 @@
 
         if (rc != eccStatus::SUCCESS)
         {
-            throw std::runtime_error(
-                "ERROR: ECC check for one of the Record did not Pass.");
+            std::string recordName(iteratorToRecName,
+                                   iteratorToRecName + lengths::RECORD_NAME);
+
+            std::string errorMsg =
+                std::string("ERROR: ECC check did not pass for the Record:") +
+                recordName;
+            throw(VpdEccException(errorMsg));
         }
 #endif
 
@@ -470,24 +485,37 @@
 
 Store Impl::run()
 {
-    // Check if the VHDR record is present
-    checkHeader();
-
-    auto iterator = vpd.cbegin();
-
-    // Read the table of contents record
-    std::size_t ptLen = readTOC(iterator);
-
-    // Read the table of contents record, to get offsets
-    // to other records.
-    auto offsets = readPT(iterator, ptLen);
-    for (const auto& offset : offsets)
+    try
     {
-        processRecord(offset);
+        // Check if the VHDR record is present
+        checkHeader();
+
+        auto iterator = vpd.cbegin();
+
+        // Read the table of contents record
+        std::size_t ptLen = readTOC(iterator);
+
+        // Read the table of contents record, to get offsets
+        // to other records.
+        auto offsets = readPT(iterator, ptLen);
+        for (const auto& offset : offsets)
+        {
+            processRecord(offset);
+        }
+        // Return a Store object, which has interfaces to
+        // access parsed VPD by record:keyword
+        return Store(std::move(out));
     }
-    // Return a Store object, which has interfaces to
-    // access parsed VPD by record:keyword
-    return Store(std::move(out));
+    catch (const VpdEccException& ex)
+    {
+        // TODO: Create PEL
+        throw std::runtime_error(ex.what());
+    }
+    catch (const VpdDataException& ex)
+    {
+        // TODO: Create PEL
+        throw std::runtime_error(ex.what());
+    }
 }
 
 void Impl::checkVPDHeader()
diff --git a/vpd_exceptions.hpp b/vpd_exceptions.hpp
new file mode 100644
index 0000000..35c05d4
--- /dev/null
+++ b/vpd_exceptions.hpp
@@ -0,0 +1,135 @@
+#pragma once
+
+#include <stdexcept>
+
+namespace openpower
+{
+namespace vpd
+{
+namespace exceptions
+{
+
+/** @class VPDException
+ * @brief This class inherits std::runtime_error and overrrides
+ * "what" method to return the description of exception.
+ * This class also works as base class for custom exception
+ * classes of openpower-vpd repository.
+ */
+class VPDException : public std::runtime_error
+{
+  public:
+    // deleted methods
+    VPDException() = delete;
+    VPDException(const VPDException&) = delete;
+    VPDException(VPDException&&) = delete;
+    VPDException& operator=(const VPDException&) = delete;
+
+    // default destructor
+    ~VPDException() = default;
+
+    /** @brief constructor
+     *  @param[in] - string to define exception
+     */
+    explicit VPDException(const std::string& msg) :
+        std::runtime_error(msg), errMsg(msg)
+    {
+    }
+
+    /** @brief inline method to return exception string
+     * This is overridden method of std::runtime class
+     */
+    inline const char* what() const noexcept override
+    {
+        return errMsg.c_str();
+    }
+
+  private:
+    /** @brief string to hold the reason of exception */
+    std::string errMsg;
+
+}; // class VPDException
+
+/** @class VpdEccException
+ *  @brief This class extends Exceptions class and define
+ *  type for ECC related exception in VPD
+ */
+class VpdEccException : public VPDException
+{
+  public:
+    // deleted methods
+    VpdEccException() = delete;
+    VpdEccException(const VpdEccException&) = delete;
+    VpdEccException(VpdEccException&&) = delete;
+    VpdEccException& operator=(const VpdEccException&) = delete;
+
+    // default destructor
+    ~VpdEccException() = default;
+
+    /** @brief constructor
+     *  @param[in] - string to define exception
+     */
+    explicit VpdEccException(const std::string& msg) : VPDException(msg)
+    {
+    }
+
+}; // class VpdEccException
+
+/** @class VpdDataException
+ *  @brief This class extends Exceptions class and define
+ *  type for data related exception in VPD
+ */
+class VpdDataException : public VPDException
+{
+  public:
+    // deleted methods
+    VpdDataException() = delete;
+    VpdDataException(const VpdDataException&) = delete;
+    VpdDataException(VpdDataException&&) = delete;
+    VpdDataException& operator=(const VpdDataException&) = delete;
+
+    // default destructor
+    ~VpdDataException() = default;
+
+    /** @brief constructor
+     *  @param[in] - string to define exception
+     */
+    explicit VpdDataException(const std::string& msg) : VPDException(msg)
+    {
+    }
+
+}; // class VpdDataException
+
+class VpdJsonException : public VPDException
+{
+  public:
+    // deleted methods
+    VpdJsonException() = delete;
+    VpdJsonException(const VpdJsonException&) = delete;
+    VpdJsonException(VpdDataException&&) = delete;
+    VpdJsonException& operator=(const VpdDataException&) = delete;
+
+    // default destructor
+    ~VpdJsonException() = default;
+
+    /** @brief constructor
+     *  @param[in] - string to define exception
+     */
+    explicit VpdJsonException(const std::string& msg, const std::string& Path) :
+        VPDException(msg), jsonPath(Path)
+    {
+    }
+
+    inline std::string getJsonPath() const
+    {
+        return jsonPath;
+    }
+
+  private:
+    /** To hold the path of Json that failed to parse*/
+    std::string jsonPath;
+
+}; // class VpdJSonException
+
+} // namespace exceptions
+} // namespace vpd
+} // namespace openpower
\ No newline at end of file