sdbusplus: message: read: add native enum support

Add support for reading enums directly out of a sdbusplus message
and generate the required helper functions in the interface server
templates.

Signed-off-by: Patrick Williams <patrick@stwcx.xyz>
Change-Id: I2c89614edb5981d984660efe06cc4380cb526d9e
diff --git a/sdbusplus/message/read.hpp b/sdbusplus/message/read.hpp
index 31e3753..4c52a71 100644
--- a/sdbusplus/message/read.hpp
+++ b/sdbusplus/message/read.hpp
@@ -41,6 +41,15 @@
 namespace details
 {
 
+/** @brief Convert from a string to a native type.
+ *
+ *  Some C++ types cannot be represented directly on dbus, so we encode
+ *  them as strings.  Enums are the primary example of this.  This is a
+ *  template function prototype for the conversion from string functions.
+ */
+template <typename T>
+auto convert_from_string(const std::string&) = delete;
+
 /** @struct can_read_multiple
  *  @brief Utility to identify C++ types that may not be grouped into a
  *         single sd_bus_message_read call and instead need special
@@ -138,9 +147,9 @@
      *  @param[in] m - sd_bus_message to read from.
      *  @param[out] t - The reference to read item into.
      */
-    template <typename T,
-              typename = std::enable_if_t<std::is_same<S, Td<T>>::value>>
-    static void op(sdbusplus::SdBusInterface* intf, sd_bus_message* m, T&& t)
+    template <typename T>
+    static std::enable_if_t<std::is_same_v<S, Td<T>> && !std::is_enum_v<Td<T>>>
+        op(sdbusplus::SdBusInterface* intf, sd_bus_message* m, T&& t)
     {
         // For this default implementation, we need to ensure that only
         // basic types are used.
@@ -156,6 +165,16 @@
                 -r, "sd_bus_message_read_basic fundamental");
         }
     }
+
+    template <typename T>
+    static std::enable_if_t<std::is_same_v<S, Td<T>> && std::is_enum_v<Td<T>>>
+        op(sdbusplus::SdBusInterface* intf, sd_bus_message* m, T&& t)
+    {
+        std::string value{};
+        sdbusplus::message::read(intf, m, value);
+
+        t = convert_from_string<Td<T>>(value);
+    }
 };
 
 template <typename T>
diff --git a/tools/sdbusplus/templates/interface.mako.server.hpp b/tools/sdbusplus/templates/interface.mako.server.hpp
index 1825d6b..48c7038 100644
--- a/tools/sdbusplus/templates/interface.mako.server.hpp
+++ b/tools/sdbusplus/templates/interface.mako.server.hpp
@@ -16,6 +16,9 @@
     def setOfPropertyTypes():
         return set(p.cppTypeParam(interface.name) for p in
                    interface.properties);
+
+    def cppNamespace():
+        return "::".join(namespaces) + "::server::" + classname
 %>
 namespace sdbusplus
 {
@@ -187,4 +190,19 @@
     % for s in reversed(namespaces):
 } // namespace ${s}
     % endfor
+
+namespace message
+{
+namespace details
+{
+    % for e in interface.enums:
+template <>
+inline auto convert_from_string<${cppNamespace()}::${e.name}>(
+        const std::string& value)
+{
+    return ${cppNamespace()}::convert${e.name}FromString(value);
+}
+    % endfor
+} // namespace details
+} // namespace message
 } // namespace sdbusplus