property: Add errors support

Add errors support for properties.

Closes openbmc/openbmc#1641

Tested: Verified that when an interface threw a defined property
error, the generated code caught it and returned an error to
the REST call.

Change-Id: I8cb8c77995026f12e373291822e4e0a623a84d81
Signed-off-by: Adriana Kobylak <anoo@us.ibm.com>
diff --git a/docs/interface.md b/docs/interface.md
index 89820b1..1613021 100644
--- a/docs/interface.md
+++ b/docs/interface.md
@@ -141,8 +141,9 @@
 ## Properties
 
 A property must have the YAML property `name` and `type` and may optionally
-have `description` and `default`.  The `default` defines the default value of
-the property.
+have `description`, `default`, and `errors`.  The `default` defines the default
+value of the property. See the `Methods` section above for more information on
+errors.
 
 Example:
 ```
@@ -152,6 +153,8 @@
       default: 52
       description: >
         The number of cards remaining in the deck.
+      errors:
+        - self.Error.InvalidNumber
 ```
 
 ## Signals
diff --git a/example/net/poettering/Calculator.errors.yaml b/example/net/poettering/Calculator.errors.yaml
index 730e2a3..b2f6ee3 100644
--- a/example/net/poettering/Calculator.errors.yaml
+++ b/example/net/poettering/Calculator.errors.yaml
@@ -1,3 +1,6 @@
 - name: DivisionByZero
   description: >
     An attempt to divide by zero was attempted.
+- name: PermissionDenied
+  description: >
+    The user does not have enough privileges to perform the operation.
diff --git a/example/net/poettering/Calculator.interface.yaml b/example/net/poettering/Calculator.interface.yaml
index d289c21..af4dd1b 100644
--- a/example/net/poettering/Calculator.interface.yaml
+++ b/example/net/poettering/Calculator.interface.yaml
@@ -55,6 +55,12 @@
       default: Success
       description: >
         The current state of the Calculator.
+    - name: Owner
+      type: string
+      description: >
+        The name of the owner of the Calculator.
+      errors:
+        - self.Error.PermissionDenied
 signals:
     - name: Cleared
       description: >
diff --git a/tools/sdbusplus/property.py b/tools/sdbusplus/property.py
index c6444c8..8465315 100644
--- a/tools/sdbusplus/property.py
+++ b/tools/sdbusplus/property.py
@@ -8,6 +8,7 @@
         self.typeName = kwargs.pop('type', None)
         self.cppTypeName = self.parse_cpp_type(self.typeName)
         self.defaultValue = kwargs.pop('default', None)
+        self.errors = kwargs.pop('errors', [])
 
         super(Property, self).__init__(**kwargs)
 
diff --git a/tools/sdbusplus/templates/interface.mako.server.cpp.in b/tools/sdbusplus/templates/interface.mako.server.cpp.in
index 4b46711..5fa46a9 100644
--- a/tools/sdbusplus/templates/interface.mako.server.cpp.in
+++ b/tools/sdbusplus/templates/interface.mako.server.cpp.in
@@ -6,6 +6,9 @@
 % for m in interface.methods:
 ${ m.cpp_prototype(loader, interface=interface, ptype='callback-cpp-includes') }
 % endfor
+% for p in interface.properties:
+${ p.cpp_prototype(loader, interface=interface, ptype='callback-cpp-includes') }
+% endfor
 <%
     namespaces = interface.name.split('.')
     classname = namespaces.pop()
diff --git a/tools/sdbusplus/templates/property.mako.prototype.hpp.in b/tools/sdbusplus/templates/property.mako.prototype.hpp.in
index fd4a28e..163f6e9 100644
--- a/tools/sdbusplus/templates/property.mako.prototype.hpp.in
+++ b/tools/sdbusplus/templates/property.mako.prototype.hpp.in
@@ -4,7 +4,23 @@
 
     def interface_instance():
         return "_".join(interface.name.split('.') + ['interface'])
+
+    def error_namespace(e):
+        n = e.split('.');
+        n.pop(); # Remove error name.
+
+        n = map((lambda x: interface.name if x == "self" else x), n);
+        return '::'.join('.'.join(n).split('.'));
+
+    def error_name(e):
+        return e.split('.').pop();
+
+    def error_include(e):
+        l = error_namespace(e).split('::')
+        l.pop() # Remove "Error"
+        return '/'.join(l) + '/error.hpp';
 %>
+% if ptype == 'callback-cpp':
 auto ${classname}::${property.camelCase}() const ->
         ${property.cppTypeParam(interface.name)}
 {
@@ -41,6 +57,13 @@
         o->_intf->sd_bus_error_set_const(error, e.name(), e.description());
         return -EINVAL;
     }
+    % for e in property.errors:
+    catch(sdbusplus::${error_namespace(e)}::${error_name(e)}& e)
+    {
+        o->_intf->sd_bus_error_set_const(error, e.name(), e.description());
+        return -EINVAL;
+    }
+    % endfor
 
     return true;
 }
@@ -91,6 +114,13 @@
         o->_intf->sd_bus_error_set_const(error, e.name(), e.description());
         return -EINVAL;
     }
+    % for e in property.errors:
+    catch(sdbusplus::${error_namespace(e)}::${error_name(e)}& e)
+    {
+        o->_intf->sd_bus_error_set_const(error, e.name(), e.description());
+        return -EINVAL;
+    }
+    % endfor
 
     return true;
 }
@@ -104,3 +134,8 @@
             ${property.cppTypeMessage(interface.name)}>());
 }
 }
+% elif ptype == 'callback-cpp-includes':
+        % for e in property.errors:
+#include <${error_include(e)}>
+        % endfor
+% endif