OEM Route Handling Infrastructure

Goal of the MR is to provide infrastructure support in bmcweb to manage
the OEM fragment handling separately. OEM schema are vendor defined and
per DMTF resource we could have multiple vendor defined OEM schema to be
enabled.

The feature allows registration of route handler per schema per OEM
namespace.
Example
```
REDFISH_SUB_ROUTE<"/redfish/v1/Managers/<str>/#/Oem/OpenBmc">(service,
 HttpVerb::Get)(oemOpenBmcCallback);
REDFISH_SUB_ROUTE<"/redfish/v1/Managers/<str>/#/Oem/Nvidia">(service,
 HttpVerb::Get)(oemNidiaCallback);
```

We can have separate vendor defined route handlers per resource. Each of
these route handlers can populate their own vendor specific OEM data.
The OEM code can be better organized and enabled/disabled as per the
platform needs. The current MR has the code changes related to handling
GET requests alone. The feature only supports requests
where the response payload is JSON.

Tests
- All UT cases passes
- New UT added for RF OEM router passes
- Service Validator passes on qemu
- GET Response on Manager/bmc resource contains the OEM fragment

```
curl -c cjar -b cjar -k -X GET https://127.0.0.1:2443/redfish/v1/Managers/bmc
{
  "@odata.id": "/redfish/v1/Managers/bmc",
  "@odata.type": "#Manager.v1_14_0.Manager",

  "Oem": {
    "OpenBmc": {
      "@odata.id": "/redfish/v1/Managers/bmc#/Oem/OpenBmc",
      "@odata.type": "#OpenBMCManager.v1_0_0.Manager",
      "Certificates": {
        "@odata.id": "/redfish/v1/Managers/bmc/Truststore/Certificates"
      }
    }
  },

  "UUID": "40575e98-90d7-4c10-9eb5-8d8a7156c9b9"
}
```

Change-Id: Ic82aa5fe760eda31e2792fbdfb6884ac3ea613dc
Signed-off-by: Rohit PAI <rohitpai77@gmail.com>
diff --git a/redfish-core/include/redfishoemrule.hpp b/redfish-core/include/redfishoemrule.hpp
new file mode 100644
index 0000000..018a18e
--- /dev/null
+++ b/redfish-core/include/redfishoemrule.hpp
@@ -0,0 +1,104 @@
+#pragma once
+#include "async_resp.hpp"
+#include "http_request.hpp"
+
+#include <nlohmann/json.hpp>
+
+#include <functional>
+#include <memory>
+#include <stdexcept>
+#include <string>
+#include <string_view>
+#include <type_traits>
+#include <vector>
+
+namespace redfish
+{
+class OemBaseRule
+{
+  public:
+    explicit OemBaseRule(std::string_view thisRule) : rule(thisRule) {}
+    virtual ~OemBaseRule() = default;
+    OemBaseRule(const OemBaseRule&) = delete;
+    OemBaseRule(OemBaseRule&&) = delete;
+    OemBaseRule& operator=(const OemBaseRule&) = delete;
+    OemBaseRule& operator=(const OemBaseRule&&) = delete;
+
+    virtual void handle(const crow::Request& /*req*/,
+                        const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+                        const std::vector<std::string>& /*params*/) = 0;
+    std::string rule;
+};
+
+template <typename... Args>
+class OemRule : public OemBaseRule
+{
+  public:
+    using self_t = OemRule<Args...>;
+
+    explicit OemRule(std::string_view ruleIn) : OemBaseRule(ruleIn) {}
+
+    void validate()
+    {
+        if (!handler)
+        {
+            throw std::runtime_error(
+                "no OEM fragment handler for the rule {}" + rule);
+        }
+    }
+
+    template <typename Func>
+    void operator()(Func&& f)
+    {
+        static_assert(
+            std::is_invocable_v<Func, crow::Request,
+                                std::shared_ptr<bmcweb::AsyncResp>&, Args...>,
+            "Handler type is mismatched with URL parameters");
+        static_assert(
+            std::is_same_v<
+                void, std::invoke_result_t<Func, crow::Request,
+                                           std::shared_ptr<bmcweb::AsyncResp>&,
+                                           Args...>>,
+            "Handler function with response argument should have void return type");
+
+        handler = std::forward<Func>(f);
+    }
+
+    void handle(const crow::Request& req,
+                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+                const std::vector<std::string>& params) override
+    {
+        if constexpr (sizeof...(Args) == 0)
+        {
+            handler(req, asyncResp);
+        }
+        else if constexpr (sizeof...(Args) == 1)
+        {
+            handler(req, asyncResp, params[0]);
+        }
+        else if constexpr (sizeof...(Args) == 2)
+        {
+            handler(req, asyncResp, params[0], params[1]);
+        }
+        else if constexpr (sizeof...(Args) == 3)
+        {
+            handler(req, asyncResp, params[0], params[1], params[2]);
+        }
+        else if constexpr (sizeof...(Args) == 4)
+        {
+            handler(req, asyncResp, params[0], params[1], params[2], params[3]);
+        }
+        else if constexpr (sizeof...(Args) == 5)
+        {
+            handler(req, asyncResp, params[0], params[1], params[2], params[3],
+                    params[4]);
+        }
+        static_assert(sizeof...(Args) <= 5, "More args than are supported");
+    }
+
+  private:
+    std::function<void(const crow::Request&,
+                       const std::shared_ptr<bmcweb::AsyncResp>&, Args...)>
+        handler;
+};
+} // namespace redfish