sdbus++: events: generate exception constructors

Generate constructors for exceptions using the listed metadata.

Signed-off-by: Patrick Williams <patrick@stwcx.xyz>
Change-Id: I99dc912e4ad0746a14d3053a5653327ae12440dd
diff --git a/include/sdbusplus/utility/consteval_string.hpp b/include/sdbusplus/utility/consteval_string.hpp
new file mode 100644
index 0000000..fbb67b4
--- /dev/null
+++ b/include/sdbusplus/utility/consteval_string.hpp
@@ -0,0 +1,50 @@
+#pragma once
+
+#include <algorithm>
+#include <cstdint>
+
+namespace sdbusplus::utility
+{
+namespace details
+{
+
+template <std::size_t N>
+struct consteval_string_holder
+{
+    char value[N] = {};
+
+    consteval consteval_string_holder(const char (&str)[N])
+    {
+        std::ranges::copy(str, value);
+    }
+};
+
+} // namespace details
+
+/** Type to allow compile-time parameter string comparisons.
+ *
+ *  Example:
+ *      void foo(consteval_string<"FOO">);
+ *
+ *  If the function is not called with foo("FOO"), it will be a compile error.
+ */
+template <details::consteval_string_holder V>
+struct consteval_string
+{
+    std::string_view value;
+
+    template <typename T>
+    consteval consteval_string(const T& s) : value(s)
+    {
+        if (value != V.value)
+        {
+            report_error("String mismatch; check parameter name.");
+        }
+    }
+
+    // This is not a real function but used to trigger compile errors due to
+    // calling a non-constexpr function in a constexpr context.
+    static void report_error(const char*);
+};
+
+} // namespace sdbusplus::utility
diff --git a/tools/sdbusplus/event.py b/tools/sdbusplus/event.py
index 84fda77..03137c5 100644
--- a/tools/sdbusplus/event.py
+++ b/tools/sdbusplus/event.py
@@ -4,12 +4,12 @@
 import yaml
 
 from .namedelement import NamedElement
+from .property import Property
 from .renderer import Renderer
 
 
-class EventMetadata(NamedElement):
+class EventMetadata(Property):
     def __init__(self, **kwargs):
-        self.type = kwargs.pop("type")
         self.primary = kwargs.pop("primary", False)
         super(EventMetadata, self).__init__(**kwargs)
 
@@ -47,6 +47,12 @@
 
         super(EventElement, self).__init__(**kwargs)
 
+    def cpp_includes(self, interface):
+        includes = []
+        for m in self.metadata:
+            includes.extend(m.enum_headers(interface))
+        return sorted(set(includes))
+
     def __getattribute__(self, name):
         lam = {"description": lambda: self.__description()}.get(name)
 
@@ -115,6 +121,14 @@
 
         super(Event, self).__init__(**kwargs)
 
+    def cpp_includes(self):
+        includes = []
+        for e in self.errors:
+            includes.extend(e.cpp_includes(self.name))
+        for e in self.events:
+            includes.extend(e.cpp_includes(self.name))
+        return sorted(set(includes))
+
     def markdown(self, loader):
         return self.render(loader, "events.md.mako", events=self)
 
diff --git a/tools/sdbusplus/interface.py b/tools/sdbusplus/interface.py
index eade1de..0e3d3e5 100644
--- a/tools/sdbusplus/interface.py
+++ b/tools/sdbusplus/interface.py
@@ -65,7 +65,7 @@
     def enum_includes(self, inc_list):
         includes = []
         for e in inc_list:
-            includes.extend(e.enum_headers(self.name))
+            includes.extend(e.enum_headers())
         return sorted(set(includes))
 
     def markdown(self, loader):
diff --git a/tools/sdbusplus/property.py b/tools/sdbusplus/property.py
index 63d7282..898aa28 100644
--- a/tools/sdbusplus/property.py
+++ b/tools/sdbusplus/property.py
@@ -112,19 +112,22 @@
     """ Determine the C++ header of an enumeration-type property.
     """
 
-    def enum_headers(self, interface):
+    def enum_headers(self, interface=None):
         typeTuple = self.__type_tuple()
-        return self.__enum_headers(interface, typeTuple)
+        return self.__enum_headers(typeTuple, interface)
 
-    def __enum_headers(self, interface, typeTuple):
+    def __enum_headers(self, typeTuple, interface=None):
         # Enums can be processed directly.
         if "enum" == typeTuple[0]:
             # Get enum type from typeTuple.
             enumType = typeTuple[1][0][0]
 
-            # Local enums don't need a header.
+            # Local enums don't need a header, unless interface is provided.
             if "self." in enumType:
-                return []
+                if interface:
+                    enumType = enumType.replace("self.", interface + ".")
+                else:
+                    return []
 
             enumType = ".".join(enumType.split(".")[0:-1])
             return [NamedElement(name=enumType).headerFile()]
@@ -138,7 +141,7 @@
         # them recursively.
         r = []
         for t in typeTuple[1]:
-            r.extend(self.__enum_headers(interface, t))
+            r.extend(self.__enum_headers(t, interface))
         return r
 
     """ Convert the property type into a C++ type.
diff --git a/tools/sdbusplus/templates/event.hpp.mako b/tools/sdbusplus/templates/event.hpp.mako
index 78a31d9..674c9dc 100644
--- a/tools/sdbusplus/templates/event.hpp.mako
+++ b/tools/sdbusplus/templates/event.hpp.mako
@@ -9,4 +9,19 @@
         "${events.name}.${event.name}: ${event.description}";
 
     static constexpr auto errErrno = ${event.errno};
+
+%if len(event.metadata) > 0:
+    ${event.CamelCase}() = delete;
+    ${event.CamelCase}(
+        ${", ".join([
+            f"utility::consteval_string<\"{m.SNAKE_CASE}\">, {m.cppTypeParam(events.name)} {m.camelCase}_"
+            for m in event.metadata ])}) :
+        ${", ".join([
+            f"{m.camelCase}({m.camelCase}_)" for m in event.metadata ])}
+    {}
+
+%for m in event.metadata:
+    ${m.cppTypeParam(events.name)} ${m.camelCase};
+%endfor
+%endif
 };
diff --git a/tools/sdbusplus/templates/event.md.mako b/tools/sdbusplus/templates/event.md.mako
index de6a69b..9cbf44d 100644
--- a/tools/sdbusplus/templates/event.md.mako
+++ b/tools/sdbusplus/templates/event.md.mako
@@ -15,7 +15,7 @@
 % if event.metadata:
 - metadata:
 % for m in event.metadata:
-  - `${m.SNAKE_CASE}` as `${m.type}` \
+  - `${m.SNAKE_CASE}` as `${m.typeName}` \
 % if m.primary:
 **[PRIMARY]**
 % else:
diff --git a/tools/sdbusplus/templates/events.hpp.mako b/tools/sdbusplus/templates/events.hpp.mako
index 434cc0b..c2e945c 100644
--- a/tools/sdbusplus/templates/events.hpp.mako
+++ b/tools/sdbusplus/templates/events.hpp.mako
@@ -3,9 +3,13 @@
  */
 #pragma once
 #include <sdbusplus/exception.hpp>
+#include <sdbusplus/utility/consteval_string.hpp>
 
 #include <cerrno>
 
+% for h in events.cpp_includes():
+#include <${h}>
+% endfor
 %if events.errors:
 
 namespace sdbusplus::error::${events.cppNamespacedClass()}