sdbus++: change binding namespace format

Change the generated binding namespace name such that the interface
`xyz.openbmc_project.Foo` becomes
`sdbusplus::server::xyz::openbmc_project::Foo` instead of
`sdbusplus::xyz::openbmc_project::server::Foo`.  This both simplifies
the namespace generation and makes the correlation from interface to
binding name much more obvious.

An alias namespace to the old name is still generated so that users
have a chance to migrate.

Signed-off-by: Patrick Williams <patrick@stwcx.xyz>
Change-Id: Id82a5b6dcb26be8644b04bf14ddaf8a58dad8479
diff --git a/example/calculator-server.cpp b/example/calculator-server.cpp
index 2ac6d2d..9e02ba0 100644
--- a/example/calculator-server.cpp
+++ b/example/calculator-server.cpp
@@ -7,7 +7,7 @@
 #include <string_view>
 
 using Calculator_inherit =
-    sdbusplus::server::object_t<sdbusplus::net::poettering::server::Calculator>;
+    sdbusplus::server::object_t<sdbusplus::server::net::poettering::Calculator>;
 
 /** Example implementation of net.poettering.Calculator */
 struct Calculator : Calculator_inherit
@@ -56,7 +56,7 @@
 
     static_assert(
         std::string_view(
-            sdbusplus::net::poettering::client::Calculator::interface) ==
+            sdbusplus::client::net::poettering::Calculator::interface) ==
         std::string_view(Calculator::interface));
 
     // Create a new bus and affix an object manager for the subtree path we
diff --git a/tools/sdbusplus/interface.py b/tools/sdbusplus/interface.py
index f35d0e3..c12c5c7 100644
--- a/tools/sdbusplus/interface.py
+++ b/tools/sdbusplus/interface.py
@@ -31,49 +31,26 @@
 
         super(Interface, self).__init__(**kwargs)
 
-        self.namespaces = self.name.split(".")
-        self.classname = self.namespaces.pop()
-
-    def cppNamespace(self, typename="server"):
-        return "::".join(self.namespaces) + "::" + typename
-
-    def cppNamespacedClass(self, typename="server"):
-        return self.cppNamespace(typename) + "::" + self.classname
-
     def joinedName(self, join_str, append):
         return join_str.join(self.namespaces + [self.classname, append])
 
     def enum_includes(self, inc_list):
         includes = []
-        namespaces = []
         for e in inc_list:
-            namespaces.extend(e.enum_namespaces(self.name))
-        for e in sorted(set(namespaces)):
-            es = e.split("::")
-            # Skip empty, non-enum values and self references like '::'
-            if len(es) < 2:
-                continue
-            # All elements will be formatted (x::)+
-            # If the requested enum is xyz.openbmc_project.Network.IP.Protocol
-            # for a server_* configuration, the enum_namespace will be
-            # xyz::openbmc_project::Network::server::IP:: and we need to
-            # convert to xyz/openbmc_project/Network/IP/server.hpp
-            es.pop()  # Remove trailing empty element
-            e_class = es.pop()  # Remove class name
-            e_type = es.pop()  # Remove injected type namespace
-            es.append(e_class)
-            es.append(e_type)
-            includes.append("/".join(es) + ".hpp")
-        return includes
+            includes.extend(e.enum_headers(self.name, self.typename))
+        return sorted(set(includes))
 
     def markdown(self, loader):
         return self.render(loader, "interface.md.mako", interface=self)
 
     def server_header(self, loader):
+        self.typename = "server"
         return self.render(loader, "interface.server.hpp.mako", interface=self)
 
     def server_cpp(self, loader):
+        self.typename = "server"
         return self.render(loader, "interface.server.cpp.mako", interface=self)
 
     def client_header(self, loader):
+        self.typename = "client"
         return self.render(loader, "interface.client.hpp.mako", interface=self)
diff --git a/tools/sdbusplus/namedelement.py b/tools/sdbusplus/namedelement.py
index c41d451..f91815c 100644
--- a/tools/sdbusplus/namedelement.py
+++ b/tools/sdbusplus/namedelement.py
@@ -15,6 +15,13 @@
                 "missing quotes around original name."
             )
 
+        self.old_namespaces = self.name.split(".")
+        self.old_classname = self.old_namespaces.pop()
+        self.namespaces = [
+            inflection.underscore(x) for x in self.old_namespaces
+        ]
+        self.classname = inflection.camelize(self.old_classname)
+
     def __getattribute__(self, name):
         lam = {
             "CamelCase": lambda: inflection.camelize(self.name),
@@ -32,6 +39,21 @@
                 % (name, self.__module__)
             )
 
+    def old_cppNamespace(self, typename="server"):
+        return "::".join(self.old_namespaces) + "::" + typename
+
+    def old_cppNamespacedClass(self, typename="server"):
+        return self.old_cppNamespace(typename) + "::" + self.old_classname
+
+    def headerFile(self, typename):
+        return self.name.replace(".", "/") + f"/{typename}.hpp"
+
+    def cppNamespace(self):
+        return "::".join(self.namespaces)
+
+    def cppNamespacedClass(self):
+        return self.cppNamespace() + "::" + self.classname
+
     """ Some names are reserved in some languages.  Fixup names to avoid using
         reserved words.
     """
diff --git a/tools/sdbusplus/property.py b/tools/sdbusplus/property.py
index 77af136..58b92f9 100644
--- a/tools/sdbusplus/property.py
+++ b/tools/sdbusplus/property.py
@@ -83,8 +83,8 @@
         Currently only 'enum' requires conversion.
     """
 
-    def cppTypeParam(self, interface, full=False, server=True):
-        return self.__cppTypeParam(interface, self.cppTypeName, full, server)
+    def cppTypeParam(self, interface, full=False, typename="server"):
+        return self.__cppTypeParam(interface, self.cppTypeName, full, typename)
 
     def default_value(self):
         if self.defaultValue is not None:
@@ -92,14 +92,10 @@
         else:
             return ""
 
-    def __cppTypeParam(self, interface, cppTypeName, full=False, server=True):
-        ns_type = "server" if server else "client"
-
-        iface = interface.split(".")
-        iface.insert(-1, ns_type)
-        iface = "::".join(iface)
-        iface = "sdbusplus::" + iface
-
+    def __cppTypeParam(
+        self, interface, cppTypeName, full=False, typename="server"
+    ):
+        iface = NamedElement(name=interface).cppNamespacedClass()
         r = cppTypeName
 
         # Fix up local enum placeholders.
@@ -109,27 +105,29 @@
             r = r.replace(self.LOCAL_ENUM_MAGIC + "::", "")
 
         # Fix up non-local enum placeholders.
-        r = r.replace(self.NONLOCAL_ENUM_MAGIC, ns_type)
+        r = r.replace(self.NONLOCAL_ENUM_MAGIC, typename)
 
         return r
 
-    """ Determine the C++ namespaces of an enumeration-type property.
+    """ Determine the C++ header of an enumeration-type property.
     """
 
-    def enum_namespaces(self, interface):
+    def enum_headers(self, interface, typename="server"):
         typeTuple = self.__type_tuple()
-        return self.__enum_namespaces(interface, typeTuple)
+        return self.__enum_headers(interface, typeTuple, typename)
 
-    def __enum_namespaces(self, interface, typeTuple):
+    def __enum_headers(self, interface, typeTuple, typename):
         # Enums can be processed directly.
         if "enum" == typeTuple[0]:
-            cppType = self.__cppTypeParam(
-                interface, self.__parse_cpp_type__(typeTuple)
-            )
-            ns = cppType.split("::")[0:-1]
-            if len(ns) != 0:
-                return ["::".join(ns) + "::"]
-            return []
+            # Get enum type from typeTuple.
+            enumType = typeTuple[1][0][0]
+
+            # Local enums don't need a header.
+            if "self." in enumType:
+                return []
+
+            enumType = ".".join(enumType.split(".")[0:-1])
+            return [NamedElement(name=enumType).headerFile(typename)]
 
         # If the details part of the tuple has zero entries, no enums are
         # present
@@ -140,7 +138,7 @@
         # them recursively.
         r = []
         for t in typeTuple[1]:
-            r.extend(self.__enum_namespaces(interface, t))
+            r.extend(self.__enum_headers(interface, t, typename))
         return r
 
     """ Convert the property type into a C++ type.
@@ -271,8 +269,16 @@
 
             # Insert place-holder for header-type namespace (ex. "server")
             result = result.split(".")
-            result.insert(-2, self.NONLOCAL_ENUM_MAGIC)
-            result = "::".join(result)
+            result = "::".join(
+                [
+                    "sdbusplus",
+                    self.NONLOCAL_ENUM_MAGIC,
+                    NamedElement(
+                        name=".".join(result[0:-1])
+                    ).cppNamespacedClass(),
+                    NamedElement(name=result[-1]).classname,
+                ]
+            )
             return result
 
         # Parse each parameter entry, if appropriate, and create C++ template
diff --git a/tools/sdbusplus/templates/interface.client.hpp.mako b/tools/sdbusplus/templates/interface.client.hpp.mako
index 38c5be0..d7cc84e 100644
--- a/tools/sdbusplus/templates/interface.client.hpp.mako
+++ b/tools/sdbusplus/templates/interface.client.hpp.mako
@@ -1,8 +1,17 @@
 #pragma once
 
-namespace sdbusplus::${interface.cppNamespacedClass("client")}
+namespace sdbusplus::client::${interface.cppNamespacedClass()}
 {
 
 static constexpr auto interface = "${interface.name}";
 
-} // namespace sdbusplus::${interface.cppNamespacedClass("client")}
+} // namespace sdbusplus::client::${interface.cppNamespacedClass()}
+
+#ifndef SDBUSPP_REMOVE_DEPRECATED_NAMESPACE
+namespace sdbusplus::${interface.old_cppNamespacedClass("client")}
+{
+
+using sdbusplus::client::${interface.cppNamespacedClass()}::interface;
+
+} // namespace sdbusplus::${interface.old_cppNamespacedClass("client")}
+#endif
diff --git a/tools/sdbusplus/templates/interface.server.cpp.mako b/tools/sdbusplus/templates/interface.server.cpp.mako
index f544ed7..ac698db 100644
--- a/tools/sdbusplus/templates/interface.server.cpp.mako
+++ b/tools/sdbusplus/templates/interface.server.cpp.mako
@@ -7,11 +7,11 @@
 #include <string>
 #include <tuple>
 
-#include <${interface.joinedName("/", "server.hpp")}>
+#include <${interface.headerFile("server")}>
 % for m in interface.methods + interface.properties + interface.signals:
 ${ m.cpp_prototype(loader, interface=interface, ptype='callback-cpp-includes') }
 % endfor
-namespace sdbusplus::${interface.cppNamespace()}
+namespace sdbusplus::server::${interface.cppNamespace()}
 {
 
     % for m in interface.methods:
@@ -72,8 +72,8 @@
 
 } // anonymous namespace
 
-auto ${interface.classname}::convertStringTo${e.name}(const std::string& s) noexcept ->
-        std::optional<${e.name}>
+auto ${interface.classname}::convertStringTo${e.name}(
+    const std::string& s) noexcept -> std::optional<${e.name}>
 {
     auto i = std::find_if(
             std::begin(mapping${interface.classname}${e.name}),
@@ -90,7 +90,7 @@
 }
 
 auto ${interface.classname}::convert${e.name}FromString(const std::string& s) ->
-        ${e.name}
+    ${e.name}
 {
     auto r = convertStringTo${e.name}(s);
 
@@ -104,7 +104,8 @@
     }
 }
 
-std::string ${interface.classname}::convert${e.name}ToString(${interface.classname}::${e.name} v)
+std::string ${interface.classname}::convert${e.name}ToString(
+    ${interface.classname}::${e.name} v)
 {
     auto i = std::find_if(
             std::begin(mapping${interface.classname}${e.name}),
@@ -143,4 +144,4 @@
     vtable::end()
 };
 
-} // namespace sdbusplus::${interface.cppNamespace()}
+} // namespace sdbusplus::server::${interface.cppNamespace()}
diff --git a/tools/sdbusplus/templates/interface.server.hpp.mako b/tools/sdbusplus/templates/interface.server.hpp.mako
index bedf382..7b9d660 100644
--- a/tools/sdbusplus/templates/interface.server.hpp.mako
+++ b/tools/sdbusplus/templates/interface.server.hpp.mako
@@ -17,7 +17,7 @@
                    interface.properties);
 %>
 
-namespace sdbusplus::${interface.cppNamespace()}
+namespace sdbusplus::server::${interface.cppNamespace()}
 {
 
 class ${interface.classname}
@@ -208,26 +208,42 @@
 }
     % endfor
 
-} // namespace sdbusplus::${interface.cppNamespace()}
+} // namespace sdbusplus::server::${interface.cppNamespace()}
+
+#ifndef SDBUSPP_REMOVE_DEPRECATED_NAMESPACE
+namespace sdbusplus::${interface.old_cppNamespace()} {
+
+using sdbusplus::server::${interface.cppNamespacedClass()};
+    % if interface.enums:
+using sdbusplus::server::${interface.cppNamespace()}::convertForMessage;
+    % endif
+
+} // namespace sdbusplus::${interface.old_cppNamespace()}
+#endif
 
 namespace sdbusplus::message::details
 {
     % for e in interface.enums:
 template <>
-struct convert_from_string<${interface.cppNamespacedClass()}::${e.name}>
+struct convert_from_string<
+    server::${interface.cppNamespacedClass()}::${e.name}>
 {
     static auto op(const std::string& value) noexcept
     {
-        return ${interface.cppNamespacedClass()}::convertStringTo${e.name}(value);
+        return server::
+            ${interface.cppNamespacedClass()}::convertStringTo${e.name}(value);
     }
 };
 
 template <>
-struct convert_to_string<${interface.cppNamespacedClass()}::${e.name}>
+struct convert_to_string<
+    server::${interface.cppNamespacedClass()}::${e.name}>
 {
-    static std::string op(${interface.cppNamespacedClass()}::${e.name} value)
+    static std::string op(
+        server::${interface.cppNamespacedClass()}::${e.name} value)
     {
-        return ${interface.cppNamespacedClass()}::convert${e.name}ToString(value);
+        return server::
+            ${interface.cppNamespacedClass()}::convert${e.name}ToString(value);
     }
 };
     % endfor
diff --git a/tools/sdbusplus/templates/signal.prototype.hpp.mako b/tools/sdbusplus/templates/signal.prototype.hpp.mako
index 33bcf27..b0bd891 100644
--- a/tools/sdbusplus/templates/signal.prototype.hpp.mako
+++ b/tools/sdbusplus/templates/signal.prototype.hpp.mako
@@ -53,7 +53,7 @@
 void ${interface.classname}::${ signal.camelCase }(
             ${ parameters() })
 {
-    auto& i = _${"_".join(interface.name.split('.'))}_interface;
+    auto& i = _${interface.joinedName("_", "interface")};
     auto m = i.new_signal("${ signal.name }");
 
     m.append(${ parameters_as_list() });