blob: 6dceca1286a4fb32aeab39b2af69e7b8493b3509 [file] [log] [blame]
Patrick Williamsf79f3582016-10-09 16:12:26 -05001from .namedelement import NamedElement
Patrick Williams52dd41e2016-10-14 18:41:27 -05002from .renderer import Renderer
Patrick Williamsd0cd9662016-11-14 21:55:07 -06003import yaml
Patrick Williamsf79f3582016-10-09 16:12:26 -05004
Patrick Williamsf5c3cde2016-10-21 11:49:01 -05005
Patrick Williams52dd41e2016-10-14 18:41:27 -05006class Property(NamedElement, Renderer):
Patrick Williams7eb0d252020-03-13 14:33:34 -05007
8 LOCAL_ENUM_MAGIC = "<LOCAL_ENUM>"
9 NONLOCAL_ENUM_MAGIC = "<NONLOCAL_ENUM>"
10
Patrick Williamsf79f3582016-10-09 16:12:26 -050011 def __init__(self, **kwargs):
Patrick Williams18b29ba2020-01-24 16:09:06 -060012 self.enum = False
Patrick Williamsf79f3582016-10-09 16:12:26 -050013 self.typeName = kwargs.pop('type', None)
Patrick Williamsd0cd9662016-11-14 21:55:07 -060014 self.cppTypeName = self.parse_cpp_type(self.typeName)
Patrick Williamsf79f3582016-10-09 16:12:26 -050015 self.defaultValue = kwargs.pop('default', None)
Adriana Kobylak69425eb2018-08-17 13:55:19 -050016 self.flags = kwargs.pop('flags', [])
Adriana Kobylak75a2e252018-06-06 10:49:24 -050017 self.errors = kwargs.pop('errors', [])
Patrick Williamsf79f3582016-10-09 16:12:26 -050018
Matthew Barthf0dd3b52019-02-07 11:08:15 -060019 if (self.defaultValue is not None):
20 if (isinstance(self.defaultValue, bool)):
21 # Convert True/False to 'true'/'false'
22 # because it will be rendered as C++ code
23 self.defaultValue = 'true' if self.defaultValue else 'false'
24 elif(isinstance(self.defaultValue, str) and
25 self.typeName.lower() == "string"):
26 # Wrap string type default values with double-quotes
27 self.defaultValue = "\"" + self.defaultValue + "\""
Lei YU4d741892019-01-14 09:59:10 +080028
Patrick Williamsf79f3582016-10-09 16:12:26 -050029 super(Property, self).__init__(**kwargs)
Patrick Williams62c3f272016-10-09 16:21:34 -050030
Patrick Williams2adcefa2016-11-16 08:23:12 -060031 def is_enum(self):
Patrick Williams18b29ba2020-01-24 16:09:06 -060032 return self.enum
Patrick Williams2adcefa2016-11-16 08:23:12 -060033
Patrick Williams0aa0dde2016-11-16 08:14:33 -060034 """ Return a conversion of the cppTypeName valid as a function parameter.
35 Currently only 'enum' requires conversion.
36 """
Patrick Williamsf7e21592020-01-24 15:43:08 -060037 def cppTypeParam(self, interface, full=False, server=True):
Patrick Williams7eb0d252020-03-13 14:33:34 -050038
39 ns_type = "server" if server else "client"
40
41 iface = interface.split(".")
42 iface.insert(-1, ns_type)
43 iface = "::".join(iface)
44
Patrick Williamsa163b172020-01-24 16:34:02 -060045 r = self.cppTypeName
Patrick Williams0aa0dde2016-11-16 08:14:33 -060046
Patrick Williams7eb0d252020-03-13 14:33:34 -050047 # Fix up local enum placeholders.
48 if full:
49 r = r.replace(self.LOCAL_ENUM_MAGIC, iface)
50 else:
51 r = r.replace(self.LOCAL_ENUM_MAGIC + "::", "")
52
53 # Fix up non-local enum placeholders.
54 r = r.replace(self.NONLOCAL_ENUM_MAGIC, ns_type)
Patrick Williamsa163b172020-01-24 16:34:02 -060055
56 return r
Patrick Williams0aa0dde2016-11-16 08:14:33 -060057
Patrick Williams0ad505c2016-11-16 16:17:02 -060058 def enum_namespace(self, interface):
59 if not self.is_enum():
60 return ""
61 l = self.cppTypeParam(interface).split("::")[0:-1]
62 if len(l) != 0:
63 return "::".join(l) + "::"
64 return ""
65
66 def enum_name(self, interface):
67 if not self.is_enum():
68 return ""
69 return self.cppTypeParam(interface).split("::")[-1]
70
Patrick Williamsd0cd9662016-11-14 21:55:07 -060071 def parse_cpp_type(self, typeName):
72 if not typeName:
73 return None
74
75 """ typeNames are _almost_ valid YAML. Insert a , before each [
76 and then wrap it in a [ ] and it becomes valid YAML. (assuming
77 the user gave us a valid typename)
78 """
Christian Andersene4163982016-12-09 11:24:03 +010079 typeArray = yaml.safe_load("[" + ",[".join(typeName.split("[")) + "]")
80 typeTuple = self.__preprocess_yaml_type_array(typeArray).pop(0)
Patrick Williams18b29ba2020-01-24 16:09:06 -060081 return self.__parse_cpp_type__(typeTuple, top_type=True)
Christian Andersene4163982016-12-09 11:24:03 +010082
83 """ Take a list of dbus types from YAML and convert it to a recursive data
84 structure. Each entry of the original list gets converted into a tuple
85 consisting of the type name and a list with the params for this type,
86 e.g.
87 ['dict', ['string', 'dict', ['string', 'int64']]]
88 is converted to
Patrick Williams3f67eed2016-12-12 12:22:03 -060089 [('dict', [('string', []), ('dict', [('string', []),
90 ('int64', [])]]]
Christian Andersene4163982016-12-09 11:24:03 +010091 """
92 def __preprocess_yaml_type_array(self, typeArray):
93 result = []
94
95 for i in range(len(typeArray)):
96 # Ignore lists because we merge them with the previous element
97 if type(typeArray[i]) is list:
98 continue
99
Patrick Williams3f67eed2016-12-12 12:22:03 -0600100 # If there is a next element and it is a list, merge it with the
101 # current element.
Christian Andersene4163982016-12-09 11:24:03 +0100102 if i < len(typeArray)-1 and type(typeArray[i+1]) is list:
Patrick Williams3f67eed2016-12-12 12:22:03 -0600103 result.append(
104 (typeArray[i],
105 self.__preprocess_yaml_type_array(typeArray[i+1])))
Christian Andersene4163982016-12-09 11:24:03 +0100106 else:
107 result.append((typeArray[i], []))
108
109 return result
Patrick Williamsd0cd9662016-11-14 21:55:07 -0600110
111 """ Take a list of dbus types and perform validity checking, such as:
112 [ variant [ dict [ int32, int32 ], double ] ]
113 This function then converts the type-list into a C++ type string.
114 """
Patrick Williams18b29ba2020-01-24 16:09:06 -0600115 def __parse_cpp_type__(self, typeTuple, top_type=False):
Patrick Williamsd0cd9662016-11-14 21:55:07 -0600116 propertyMap = {
117 'byte': {'cppName': 'uint8_t', 'params': 0},
118 'boolean': {'cppName': 'bool', 'params': 0},
119 'int16': {'cppName': 'int16_t', 'params': 0},
120 'uint16': {'cppName': 'uint16_t', 'params': 0},
121 'int32': {'cppName': 'int32_t', 'params': 0},
122 'uint32': {'cppName': 'uint32_t', 'params': 0},
123 'int64': {'cppName': 'int64_t', 'params': 0},
124 'uint64': {'cppName': 'uint64_t', 'params': 0},
125 'double': {'cppName': 'double', 'params': 0},
Waqar Hameed596a2232018-10-05 15:42:20 +0200126 'unixfd': {'cppName': 'sdbusplus::message::unix_fd', 'params': 0},
Patrick Williamsd0cd9662016-11-14 21:55:07 -0600127 'string': {'cppName': 'std::string', 'params': 0},
Patrick Williams8a30a8f2017-01-06 16:29:05 -0600128 'path': {'cppName': 'sdbusplus::message::object_path',
129 'params': 0},
130 'signature': {'cppName': 'sdbusplus::message::signature',
131 'params': 0},
Patrick Williamsd0cd9662016-11-14 21:55:07 -0600132 'array': {'cppName': 'std::vector', 'params': 1},
133 'struct': {'cppName': 'std::tuple', 'params': -1},
William A. Kennington III4274c112018-11-26 09:50:13 -0800134 'variant': {'cppName': 'std::variant', 'params': -1},
Patrick Williamsd0cd9662016-11-14 21:55:07 -0600135 'dict': {'cppName': 'std::map', 'params': 2},
Patrick Williams18b29ba2020-01-24 16:09:06 -0600136 'enum': {'cppName': 'enum', 'params': 1}}
Patrick Williamsd0cd9662016-11-14 21:55:07 -0600137
Christian Andersene4163982016-12-09 11:24:03 +0100138 if len(typeTuple) != 2:
139 raise RuntimeError("Invalid typeTuple %s" % typeTuple)
140
141 first = typeTuple[0]
Patrick Williamsd0cd9662016-11-14 21:55:07 -0600142 entry = propertyMap[first]
143
144 result = entry['cppName']
145
146 # Handle 0-entry parameter lists.
147 if (entry['params'] == 0):
Christian Andersene4163982016-12-09 11:24:03 +0100148 if (len(typeTuple[1]) != 0):
149 raise RuntimeError("Invalid typeTuple %s" % typeTuple)
Patrick Williamsd0cd9662016-11-14 21:55:07 -0600150 else:
151 return result
152
Christian Andersene4163982016-12-09 11:24:03 +0100153 # Get the parameter list
154 rest = typeTuple[1]
Patrick Williamsd0cd9662016-11-14 21:55:07 -0600155
156 # Confirm parameter count matches.
157 if (entry['params'] != -1) and (entry['params'] != len(rest)):
158 raise RuntimeError("Invalid entry count for %s : %s" %
159 (first, rest))
160
Patrick Williams18b29ba2020-01-24 16:09:06 -0600161 # Switch enum for proper type.
162 if result == 'enum':
163 if top_type:
164 self.enum = True
Patrick Williamsa163b172020-01-24 16:34:02 -0600165 result = rest[0][0]
166
167 # self. means local type.
168 if result.startswith("self."):
Patrick Williams7eb0d252020-03-13 14:33:34 -0500169 return result.replace("self.", self.LOCAL_ENUM_MAGIC + "::")
170
171 # Insert place-holder for header-type namespace (ex. "server")
172 result = result.split('.')
173 result.insert(-2, self.NONLOCAL_ENUM_MAGIC)
174 result = "::".join(result)
Patrick Williamsa163b172020-01-24 16:34:02 -0600175 return result
Patrick Williams18b29ba2020-01-24 16:09:06 -0600176
Patrick Williamsd0cd9662016-11-14 21:55:07 -0600177 # Parse each parameter entry, if appropriate, and create C++ template
178 # syntax.
179 result += '<'
Waqar Hameed67c79b02020-02-19 23:25:14 +0100180 result += ", ".join([self.__parse_cpp_type__(e) for e in rest])
Patrick Williamsd0cd9662016-11-14 21:55:07 -0600181 result += '>'
182
183 return result
184
Patrick Williams62c3f272016-10-09 16:21:34 -0500185 def markdown(self, loader):
Patrick Williamse70f0e82016-10-14 22:24:17 -0500186 return self.render(loader, "property.mako.md", property=self,
Patrick Williamsf5c3cde2016-10-21 11:49:01 -0500187 post=str.strip)
Adriana Kobylakee6ac692018-06-06 09:36:51 -0500188
189 def cpp_prototype(self, loader, interface, ptype):
190 return self.render(loader, "property.mako.prototype.hpp", property=self,
191 interface=interface, ptype=ptype, post=str.rstrip)