PEL: Run a user defined function on every PEL

Add a for_each() function to the PEL repository that takes a user
defined function that will be run on every PEL, unless that function
says to stop early.

The user defined function is a std::function<bool>(const PEL&);

For example, to save up to 100 IDs in the repo into a vector:
      std::vector<uint32_t> ids;
      ForEachFunc f = [&ids](const PEL& pel) {
          ids.push_back(pel.id());
          return ids.size() == 100 ? true : false;
      };

      repo.for_each(f);

This will be used to find which PELs still need to be sent up to the
host after a reboot, among other things.

Signed-off-by: Matt Spinler <spinler@us.ibm.com>
Change-Id: Ic60525a8ab3dd593ba37e43a6cb0b3db8dda7cee
diff --git a/extensions/openpower-pels/repository.cpp b/extensions/openpower-pels/repository.cpp
index 81832dd..21b0243 100644
--- a/extensions/openpower-pels/repository.cpp
+++ b/extensions/openpower-pels/repository.cpp
@@ -162,5 +162,41 @@
     return std::nullopt;
 }
 
+void Repository::for_each(ForEachFunc func) const
+{
+    for (const auto& [id, path] : _idsToPELs)
+    {
+        std::ifstream file{path};
+
+        if (!file.good())
+        {
+            auto e = errno;
+            log<level::ERR>("Repository::for_each: Unable to open PEL file",
+                            entry("ERRNO=%d", e),
+                            entry("PATH=%s", path.c_str()));
+            continue;
+        }
+
+        std::vector<uint8_t> data{std::istreambuf_iterator<char>(file),
+                                  std::istreambuf_iterator<char>()};
+        file.close();
+
+        PEL pel{data};
+
+        try
+        {
+            if (func(pel))
+            {
+                break;
+            }
+        }
+        catch (std::exception& e)
+        {
+            log<level::ERR>("Repository::for_each function exception",
+                            entry("ERROR=%s", e.what()));
+        }
+    }
+}
+
 } // namespace pels
 } // namespace openpower
diff --git a/extensions/openpower-pels/repository.hpp b/extensions/openpower-pels/repository.hpp
index d153400..a1d4a24 100644
--- a/extensions/openpower-pels/repository.hpp
+++ b/extensions/openpower-pels/repository.hpp
@@ -148,6 +148,26 @@
      */
     std::optional<std::vector<uint8_t>> getPELData(const LogID& id);
 
+    using ForEachFunc = std::function<bool(const PEL&)>;
+
+    /**
+     * @brief Run a user defined function on every PEL in the repository.
+     *
+     * ForEachFunc takes a const PEL reference, and should return
+     * true to stop iterating and return out of for_each.
+     *
+     * For example, to save up to 100 IDs in the repo into a vector:
+     *
+     *     std::vector<uint32_t> ids;
+     *     ForEachFunc f = [&ids](const PEL& pel) {
+     *         ids.push_back(pel.id());
+     *         return ids.size() == 100 ? true : false;
+     *     };
+     *
+     * @param[in] func - The function to run.
+     */
+    void for_each(ForEachFunc func) const;
+
   private:
     /**
      * @brief Finds an entry in the _idsToPELs map.
diff --git a/test/openpower-pels/repository_test.cpp b/test/openpower-pels/repository_test.cpp
index 118cb12..0c927f6 100644
--- a/test/openpower-pels/repository_test.cpp
+++ b/test/openpower-pels/repository_test.cpp
@@ -176,3 +176,39 @@
     ASSERT_TRUE(pelData);
     EXPECT_EQ(dataCopy, *pelData);
 }
+
+TEST_F(RepositoryTest, TestForEach)
+{
+    Repository repo{repoPath};
+
+    // Add 2 PELs
+    auto data = pelDataFactory(TestPELType::pelSimple);
+    auto pel = std::make_unique<PEL>(data);
+    repo.add(pel);
+
+    pel = std::make_unique<PEL>(data);
+    pel->assignID();
+    pel->setCommitTime();
+    repo.add(pel);
+
+    // Make a function that saves the IDs
+    std::vector<uint32_t> ids;
+    Repository::ForEachFunc f1 = [&ids](const PEL& pel) {
+        ids.push_back(pel.id());
+        return false;
+    };
+
+    repo.for_each(f1);
+
+    EXPECT_EQ(ids.size(), 2);
+
+    // Stop after the first time in.
+    Repository::ForEachFunc f2 = [&ids](const PEL& pel) {
+        ids.push_back(pel.id());
+        return true;
+    };
+
+    ids.clear();
+    repo.for_each(f2);
+    EXPECT_EQ(ids.size(), 1);
+}