blob: 777025c631adee7df1c94b05ebf2357453e1bba8 [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):
12 self.typeName = kwargs.pop('type', None)
Patrick Williamsd24bf312020-09-11 14:23:39 -050013 self.cppTypeName = self.parse_cpp_type()
Patrick Williamsf79f3582016-10-09 16:12:26 -050014 self.defaultValue = kwargs.pop('default', None)
Adriana Kobylak69425eb2018-08-17 13:55:19 -050015 self.flags = kwargs.pop('flags', [])
Waqar Hameed20255a52020-06-04 12:38:48 +020016 self.cpp_flags = self.or_cpp_flags(self.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 Williamsd24bf312020-09-11 14:23:39 -050032 if not self.typeName:
33 return false
34 return 'enum' == self.__type_tuple()[0]
Patrick Williams2adcefa2016-11-16 08:23:12 -060035
Patrick Williams0aa0dde2016-11-16 08:14:33 -060036 """ Return a conversion of the cppTypeName valid as a function parameter.
37 Currently only 'enum' requires conversion.
38 """
Patrick Williamsf7e21592020-01-24 15:43:08 -060039 def cppTypeParam(self, interface, full=False, server=True):
Patrick Williamsd24bf312020-09-11 14:23:39 -050040 return self.__cppTypeParam(interface, self.cppTypeName, full, server)
41
42 def __cppTypeParam(self, interface, cppTypeName, full=False, server=True):
Patrick Williams7eb0d252020-03-13 14:33:34 -050043
44 ns_type = "server" if server else "client"
45
46 iface = interface.split(".")
47 iface.insert(-1, ns_type)
48 iface = "::".join(iface)
49
Patrick Williamsd24bf312020-09-11 14:23:39 -050050 r = cppTypeName
Patrick Williams0aa0dde2016-11-16 08:14:33 -060051
Patrick Williams7eb0d252020-03-13 14:33:34 -050052 # Fix up local enum placeholders.
53 if full:
54 r = r.replace(self.LOCAL_ENUM_MAGIC, iface)
55 else:
56 r = r.replace(self.LOCAL_ENUM_MAGIC + "::", "")
57
58 # Fix up non-local enum placeholders.
59 r = r.replace(self.NONLOCAL_ENUM_MAGIC, ns_type)
Patrick Williamsa163b172020-01-24 16:34:02 -060060
61 return r
Patrick Williams0aa0dde2016-11-16 08:14:33 -060062
Patrick Williamsd24bf312020-09-11 14:23:39 -050063 """ Determine the C++ namespaces of an enumeration-type property.
64 """
65 def enum_namespaces(self, interface):
66 typeTuple = self.__type_tuple()
67 return self.__enum_namespaces(interface, typeTuple)
Patrick Williams0ad505c2016-11-16 16:17:02 -060068
Patrick Williamsd24bf312020-09-11 14:23:39 -050069 def __enum_namespaces(self, interface, typeTuple):
70 # Enums can be processed directly.
71 if 'enum' == typeTuple[0]:
72 cppType = self.__cppTypeParam(interface,
73 self.__parse_cpp_type__(typeTuple))
74 ns = cppType.split("::")[0:-1]
75 if len(ns) != 0:
76 return ["::".join(ns) + "::"]
77 return []
Patrick Williams0ad505c2016-11-16 16:17:02 -060078
Patrick Williamsd24bf312020-09-11 14:23:39 -050079 # If the details part of the tuple has zero entries, no enums are
80 # present
81 if len(typeTuple[1]) == 0:
82 return []
83
84 # Otherwise, the tuple-type might have enums embedded in it. Handle
85 # them recursively.
86 r = []
87 for t in typeTuple[1]:
88 r.extend(self.__enum_namespaces(interface, t))
89 return r
90
91 """ Convert the property type into a C++ type.
92 """
93 def parse_cpp_type(self):
94 if not self.typeName:
95 return None
96
97 typeTuple = self.__type_tuple()
98 return self.__parse_cpp_type__(typeTuple)
99
100 """ Convert the 'typeName' into a tuple of ('type', [ details ]) where
101 'details' is a recursive type-tuple.
102 """
103 def __type_tuple(self):
104 if not self.typeName:
Patrick Williamsd0cd9662016-11-14 21:55:07 -0600105 return None
106
107 """ typeNames are _almost_ valid YAML. Insert a , before each [
108 and then wrap it in a [ ] and it becomes valid YAML. (assuming
109 the user gave us a valid typename)
110 """
Patrick Williamsd24bf312020-09-11 14:23:39 -0500111 typeName = self.typeName
Christian Andersene4163982016-12-09 11:24:03 +0100112 typeArray = yaml.safe_load("[" + ",[".join(typeName.split("[")) + "]")
Patrick Williamsd24bf312020-09-11 14:23:39 -0500113 return self.__preprocess_yaml_type_array(typeArray).pop(0)
Christian Andersene4163982016-12-09 11:24:03 +0100114
115 """ Take a list of dbus types from YAML and convert it to a recursive data
116 structure. Each entry of the original list gets converted into a tuple
117 consisting of the type name and a list with the params for this type,
118 e.g.
119 ['dict', ['string', 'dict', ['string', 'int64']]]
120 is converted to
Patrick Williams3f67eed2016-12-12 12:22:03 -0600121 [('dict', [('string', []), ('dict', [('string', []),
122 ('int64', [])]]]
Christian Andersene4163982016-12-09 11:24:03 +0100123 """
124 def __preprocess_yaml_type_array(self, typeArray):
125 result = []
126
127 for i in range(len(typeArray)):
128 # Ignore lists because we merge them with the previous element
129 if type(typeArray[i]) is list:
130 continue
131
Patrick Williams3f67eed2016-12-12 12:22:03 -0600132 # If there is a next element and it is a list, merge it with the
133 # current element.
Christian Andersene4163982016-12-09 11:24:03 +0100134 if i < len(typeArray)-1 and type(typeArray[i+1]) is list:
Patrick Williams3f67eed2016-12-12 12:22:03 -0600135 result.append(
136 (typeArray[i],
137 self.__preprocess_yaml_type_array(typeArray[i+1])))
Christian Andersene4163982016-12-09 11:24:03 +0100138 else:
139 result.append((typeArray[i], []))
140
141 return result
Patrick Williamsd0cd9662016-11-14 21:55:07 -0600142
143 """ Take a list of dbus types and perform validity checking, such as:
144 [ variant [ dict [ int32, int32 ], double ] ]
145 This function then converts the type-list into a C++ type string.
146 """
Patrick Williamsd24bf312020-09-11 14:23:39 -0500147 def __parse_cpp_type__(self, typeTuple):
Patrick Williamsd0cd9662016-11-14 21:55:07 -0600148 propertyMap = {
149 'byte': {'cppName': 'uint8_t', 'params': 0},
150 'boolean': {'cppName': 'bool', 'params': 0},
151 'int16': {'cppName': 'int16_t', 'params': 0},
152 'uint16': {'cppName': 'uint16_t', 'params': 0},
153 'int32': {'cppName': 'int32_t', 'params': 0},
154 'uint32': {'cppName': 'uint32_t', 'params': 0},
155 'int64': {'cppName': 'int64_t', 'params': 0},
156 'uint64': {'cppName': 'uint64_t', 'params': 0},
Patrick Williams4ac7e562020-09-10 08:06:11 -0500157 'size': {'cppName': 'size_t', 'params': 0},
158 'ssize': {'cppName': 'ssize_t', 'params': 0},
Patrick Williamsd0cd9662016-11-14 21:55:07 -0600159 'double': {'cppName': 'double', 'params': 0},
Waqar Hameed596a2232018-10-05 15:42:20 +0200160 'unixfd': {'cppName': 'sdbusplus::message::unix_fd', 'params': 0},
Patrick Williamsd0cd9662016-11-14 21:55:07 -0600161 'string': {'cppName': 'std::string', 'params': 0},
Patrick Williams8a30a8f2017-01-06 16:29:05 -0600162 'path': {'cppName': 'sdbusplus::message::object_path',
163 'params': 0},
164 'signature': {'cppName': 'sdbusplus::message::signature',
165 'params': 0},
Patrick Williamsd0cd9662016-11-14 21:55:07 -0600166 'array': {'cppName': 'std::vector', 'params': 1},
Patrick Williamsb98bdc62020-06-16 16:15:40 -0500167 'set': {'cppName': 'std::set', 'params': 1},
Patrick Williamsd0cd9662016-11-14 21:55:07 -0600168 'struct': {'cppName': 'std::tuple', 'params': -1},
William A. Kennington III4274c112018-11-26 09:50:13 -0800169 'variant': {'cppName': 'std::variant', 'params': -1},
Patrick Williamsd0cd9662016-11-14 21:55:07 -0600170 'dict': {'cppName': 'std::map', 'params': 2},
Patrick Williams18b29ba2020-01-24 16:09:06 -0600171 'enum': {'cppName': 'enum', 'params': 1}}
Patrick Williamsd0cd9662016-11-14 21:55:07 -0600172
Christian Andersene4163982016-12-09 11:24:03 +0100173 if len(typeTuple) != 2:
174 raise RuntimeError("Invalid typeTuple %s" % typeTuple)
175
176 first = typeTuple[0]
Patrick Williamsd0cd9662016-11-14 21:55:07 -0600177 entry = propertyMap[first]
178
179 result = entry['cppName']
180
181 # Handle 0-entry parameter lists.
182 if (entry['params'] == 0):
Christian Andersene4163982016-12-09 11:24:03 +0100183 if (len(typeTuple[1]) != 0):
184 raise RuntimeError("Invalid typeTuple %s" % typeTuple)
Patrick Williamsd0cd9662016-11-14 21:55:07 -0600185 else:
186 return result
187
Christian Andersene4163982016-12-09 11:24:03 +0100188 # Get the parameter list
189 rest = typeTuple[1]
Patrick Williamsd0cd9662016-11-14 21:55:07 -0600190
191 # Confirm parameter count matches.
192 if (entry['params'] != -1) and (entry['params'] != len(rest)):
193 raise RuntimeError("Invalid entry count for %s : %s" %
194 (first, rest))
195
Patrick Williams18b29ba2020-01-24 16:09:06 -0600196 # Switch enum for proper type.
197 if result == 'enum':
Patrick Williamsa163b172020-01-24 16:34:02 -0600198 result = rest[0][0]
199
200 # self. means local type.
201 if result.startswith("self."):
Patrick Williams7eb0d252020-03-13 14:33:34 -0500202 return result.replace("self.", self.LOCAL_ENUM_MAGIC + "::")
203
204 # Insert place-holder for header-type namespace (ex. "server")
205 result = result.split('.')
206 result.insert(-2, self.NONLOCAL_ENUM_MAGIC)
207 result = "::".join(result)
Patrick Williamsa163b172020-01-24 16:34:02 -0600208 return result
Patrick Williams18b29ba2020-01-24 16:09:06 -0600209
Patrick Williamsd0cd9662016-11-14 21:55:07 -0600210 # Parse each parameter entry, if appropriate, and create C++ template
211 # syntax.
212 result += '<'
Waqar Hameed67c79b02020-02-19 23:25:14 +0100213 result += ", ".join([self.__parse_cpp_type__(e) for e in rest])
Patrick Williamsd0cd9662016-11-14 21:55:07 -0600214 result += '>'
215
216 return result
217
Patrick Williams62c3f272016-10-09 16:21:34 -0500218 def markdown(self, loader):
Patrick Williams56216242020-05-21 15:33:50 -0500219 return self.render(loader, "property.md.mako", property=self,
Patrick Williamsf5c3cde2016-10-21 11:49:01 -0500220 post=str.strip)
Adriana Kobylakee6ac692018-06-06 09:36:51 -0500221
222 def cpp_prototype(self, loader, interface, ptype):
Patrick Williams9f77a472020-07-16 14:23:51 -0500223 return self.render(loader, "property.prototype.hpp.mako",
224 property=self, interface=interface, ptype=ptype,
225 post=str.rstrip)
Waqar Hameed20255a52020-06-04 12:38:48 +0200226
227 def or_cpp_flags(self, flags):
228 """Return the corresponding ORed cpp flags."""
229 flags_dict = {
Waqar Hameed20255a52020-06-04 12:38:48 +0200230 "const": "vtable::property_::const_",
Patrick Williamse1c73d32020-07-10 16:02:27 -0500231 "deprecated": "vtable::common_::deprecated",
Waqar Hameed20255a52020-06-04 12:38:48 +0200232 "emits_change": "vtable::property_::emits_change",
233 "emits_invalidation": "vtable::property_::emits_invalidation",
Patrick Williamse1c73d32020-07-10 16:02:27 -0500234 "explicit": "vtable::property_::explicit_",
235 "hidden": "vtable::common_::hidden",
236 "readonly": False,
237 "unprivileged": "vtable::common_::unprivileged",
238 }
Waqar Hameed20255a52020-06-04 12:38:48 +0200239
240 cpp_flags = []
241 for flag in flags:
242 try:
Patrick Williamse1c73d32020-07-10 16:02:27 -0500243 if flags_dict[flag]:
244 cpp_flags.append(flags_dict[flag])
Waqar Hameed20255a52020-06-04 12:38:48 +0200245 except KeyError:
246 raise ValueError("Invalid flag \"{}\"".format(flag))
247
248 return " | ".join(cpp_flags)