EmbeddedProto 4.0.0b1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. EmbeddedProto/EmbeddedProto.py +122 -0
  2. EmbeddedProto/Features.py +255 -0
  3. EmbeddedProto/Field.py +1295 -0
  4. EmbeddedProto/Oneof.py +74 -0
  5. EmbeddedProto/ProtoFile.py +210 -0
  6. EmbeddedProto/TypeDefinitions.py +367 -0
  7. EmbeddedProto/__init__.py +0 -0
  8. EmbeddedProto/__main__.py +28 -0
  9. EmbeddedProto/config.py +173 -0
  10. EmbeddedProto/custom_header.py +513 -0
  11. EmbeddedProto/embedded_proto_options.proto +62 -0
  12. EmbeddedProto/embedded_proto_options_pb2.py +37 -0
  13. EmbeddedProto/field_options.py +344 -0
  14. EmbeddedProto/main.py +316 -0
  15. EmbeddedProto/src/EmbeddedProto/BytesStringCallback.h +476 -0
  16. EmbeddedProto/src/EmbeddedProto/Defines.h +118 -0
  17. EmbeddedProto/src/EmbeddedProto/EmptyArray.h +81 -0
  18. EmbeddedProto/src/EmbeddedProto/Errors.h +53 -0
  19. EmbeddedProto/src/EmbeddedProto/FieldStringBytes.h +793 -0
  20. EmbeddedProto/src/EmbeddedProto/Fields.h +832 -0
  21. EmbeddedProto/src/EmbeddedProto/Functional.h +412 -0
  22. EmbeddedProto/src/EmbeddedProto/MessageCallback.h +688 -0
  23. EmbeddedProto/src/EmbeddedProto/MessageInterface.h +792 -0
  24. EmbeddedProto/src/EmbeddedProto/MessageSizeCalculator.h +109 -0
  25. EmbeddedProto/src/EmbeddedProto/MessageState.h +207 -0
  26. EmbeddedProto/src/EmbeddedProto/ReadBufferFixedSize.h +204 -0
  27. EmbeddedProto/src/EmbeddedProto/ReadBufferInterface.h +181 -0
  28. EmbeddedProto/src/EmbeddedProto/ReadBufferSection.h +232 -0
  29. EmbeddedProto/src/EmbeddedProto/RepeatedField.h +1053 -0
  30. EmbeddedProto/src/EmbeddedProto/RepeatedFieldCallback.h +432 -0
  31. EmbeddedProto/src/EmbeddedProto/RepeatedFieldFixedSize.h +367 -0
  32. EmbeddedProto/src/EmbeddedProto/Version.h +39 -0
  33. EmbeddedProto/src/EmbeddedProto/WireFormatter.h +852 -0
  34. EmbeddedProto/src/EmbeddedProto/WriteBufferFixedSize.h +114 -0
  35. EmbeddedProto/src/EmbeddedProto/WriteBufferInterface.h +134 -0
  36. EmbeddedProto/src/EmbeddedProto.h +55 -0
  37. EmbeddedProto/templates/FieldBasic_Deserialize.h.jinja2 +26 -0
  38. EmbeddedProto/templates/FieldBasic_GetSet.h.jinja2 +89 -0
  39. EmbeddedProto/templates/FieldBytes_GetSet.h.jinja2 +86 -0
  40. EmbeddedProto/templates/FieldEnum_Deserialize.h.jinja2 +50 -0
  41. EmbeddedProto/templates/FieldEnum_GetSet.h.jinja2 +93 -0
  42. EmbeddedProto/templates/FieldErrorRecursive_GetSet.h.jinja2 +29 -0
  43. EmbeddedProto/templates/FieldMap_GetSet.h.jinja2 +203 -0
  44. EmbeddedProto/templates/FieldMsg_Deserialize.h.jinja2 +44 -0
  45. EmbeddedProto/templates/FieldMsg_GetSet.h.jinja2 +93 -0
  46. EmbeddedProto/templates/FieldRepeated_GetSet.h.jinja2 +94 -0
  47. EmbeddedProto/templates/FieldString_GetSet.h.jinja2 +86 -0
  48. EmbeddedProto/templates/Field_DeserializePartial.h.jinja2 +33 -0
  49. EmbeddedProto/templates/Field_Serialize.h.jinja2 +112 -0
  50. EmbeddedProto/templates/Field_SerializePartial.h.jinja2 +119 -0
  51. EmbeddedProto/templates/Header.h.jinja2 +108 -0
  52. EmbeddedProto/templates/TypeDefEnum.h.jinja2 +45 -0
  53. EmbeddedProto/templates/TypeDefMsg.h.jinja2 +791 -0
  54. EmbeddedProto/templates/TypeOneof.h.jinja2 +178 -0
  55. EmbeddedProto/version.json +3 -0
  56. embeddedproto-4.0.0b1.dist-info/METADATA +141 -0
  57. embeddedproto-4.0.0b1.dist-info/RECORD +63 -0
  58. embeddedproto-4.0.0b1.dist-info/WHEEL +5 -0
  59. embeddedproto-4.0.0b1.dist-info/entry_points.txt +3 -0
  60. embeddedproto-4.0.0b1.dist-info/licenses/LICENSE +30 -0
  61. embeddedproto-4.0.0b1.dist-info/licenses/LICENSES/GPL-3.0-only.txt +674 -0
  62. embeddedproto-4.0.0b1.dist-info/licenses/LICENSES/LicenseRef-EmbeddedProto-Commercial.txt +12 -0
  63. embeddedproto-4.0.0b1.dist-info/top_level.txt +1 -0
EmbeddedProto/Oneof.py ADDED
@@ -0,0 +1,74 @@
1
+ #
2
+ # Copyright (C) 2020-2026 Embedded AMS B.V. - All Rights Reserved
3
+ #
4
+ # This file is part of Embedded Proto.
5
+ #
6
+ # Embedded Proto is dual licensed. You may use it under the terms of the
7
+ # GNU General Public License version 3 (GPLv3) as published by the Free
8
+ # Software Foundation, or under a commercial license from Embedded AMS B.V.
9
+ #
10
+ # Under the GPLv3 you must release the source code of
11
+ # any product you distribute that includes Embedded Proto or code
12
+ # generated by it. A commercial license removes that obligation.
13
+ # See <https://embeddedproto.com/pricing/>.
14
+ #
15
+ # Embedded Proto is distributed WITHOUT ANY WARRANTY; without even the
16
+ # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17
+ # See the LICENSE file in the root of the repository for details.
18
+ #
19
+ # SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-EmbeddedProto-Commercial
20
+ #
21
+ # Embedded AMS B.V., Hoorn, the Netherlands - info at EmbeddedProto dot com
22
+ #
23
+
24
+ from .Field import Field
25
+
26
+
27
+ class Oneof:
28
+ def __init__(self, oneof_proto_descriptor, index, msg_descriptor, parent_msg):
29
+ # A reference to the OneofDescriptorProto object which defines this field.
30
+ self.descriptor = oneof_proto_descriptor
31
+
32
+ # A reference to the parent message in which this oneof is defined.
33
+ self.parent = parent_msg
34
+
35
+ self.fields = []
36
+ # Loop over all the fields in this oneof
37
+ for f in msg_descriptor.field:
38
+ if f.HasField('oneof_index') and index == f.oneof_index:
39
+ new_field = Field.factory(f, self.parent, oneof=self)
40
+ self.fields.append(new_field)
41
+
42
+ def get_name(self):
43
+ return self.descriptor.name
44
+
45
+ def get_variable_name(self):
46
+ return self.get_name() + "_"
47
+
48
+ def get_which_oneof(self):
49
+ return "which_" + self.get_name()
50
+
51
+ def get_which_method(self):
52
+ return "get_" + self.get_which_oneof() + "()"
53
+
54
+ def get_fields(self):
55
+ return self.fields
56
+
57
+ def match_field_with_definitions(self, all_types_definitions):
58
+ for field in self.fields:
59
+ field.match_field_with_definitions(all_types_definitions)
60
+
61
+ def register_template_parameters(self):
62
+ all_parameters_registered = True
63
+ for field in self.fields:
64
+ all_parameters_registered = field.register_template_parameters() and all_parameters_registered
65
+ return all_parameters_registered
66
+
67
+ # Returns true if in oneof.init the new& function needs to be call to initialize already allocated memory.
68
+ def oneof_allocation_required(self):
69
+ result = False
70
+ for field in self.fields:
71
+ result = field.oneof_allocation_required()
72
+ if result:
73
+ break
74
+ return result
@@ -0,0 +1,210 @@
1
+ #
2
+ # Copyright (C) 2020-2026 Embedded AMS B.V. - All Rights Reserved
3
+ #
4
+ # This file is part of Embedded Proto.
5
+ #
6
+ # Embedded Proto is dual licensed. You may use it under the terms of the
7
+ # GNU General Public License version 3 (GPLv3) as published by the Free
8
+ # Software Foundation, or under a commercial license from Embedded AMS B.V.
9
+ #
10
+ # Under the GPLv3 you must release the source code of
11
+ # any product you distribute that includes Embedded Proto or code
12
+ # generated by it. A commercial license removes that obligation.
13
+ # See <https://embeddedproto.com/pricing/>.
14
+ #
15
+ # Embedded Proto is distributed WITHOUT ANY WARRANTY; without even the
16
+ # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17
+ # See the LICENSE file in the root of the repository for details.
18
+ #
19
+ # SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-EmbeddedProto-Commercial
20
+ #
21
+ # Embedded AMS B.V., Hoorn, the Netherlands - info at EmbeddedProto dot com
22
+ #
23
+
24
+ from .TypeDefinitions import *
25
+ from .Features import FeatureResolver
26
+ from . import field_options
27
+ import os
28
+ import sys
29
+ from toposort import CircularDependencyError, toposort_flatten
30
+ from google.protobuf import descriptor_pb2
31
+ from google.protobuf.descriptor_pb2 import FieldDescriptorProto
32
+
33
+
34
+ # Proto files for which no C++ code is generated. Our own options file only carries generator settings. The google
35
+ # descriptor file only carries the definitions required to declare custom options, those definitions are used by protoc
36
+ # and other plugins, not by the embedded target.
37
+ EXCLUDED_PROTO_FILES = ("embedded_proto_options.proto", "google/protobuf/descriptor.proto")
38
+
39
+
40
+ # Return true if no C++ code should be generated for the given proto file name.
41
+ def is_excluded_proto_file(proto_filename):
42
+ return any(excluded in proto_filename for excluded in EXCLUDED_PROTO_FILES)
43
+
44
+
45
+ # -----------------------------------------------------------------------------
46
+
47
+ def toposort_add_msg(msg, namespace, dependency_data):
48
+
49
+ local_definitions = []
50
+ local_namespace = namespace + "." + msg.name
51
+
52
+ dependencies = {namespace}
53
+
54
+ for nested_msg in msg.nested_type:
55
+ dependency_data = toposort_add_msg(nested_msg, local_namespace, dependency_data)
56
+ full_msg_type = local_namespace + "." + nested_msg.name
57
+ local_definitions.append(full_msg_type)
58
+ for dep in dependency_data[full_msg_type]:
59
+ # Add requirements on other namespaces.
60
+ if not dep.startswith(local_namespace):
61
+ dependencies.add(dep)
62
+
63
+ for nested_enum in msg.enum_type:
64
+ full_enum_type = local_namespace + "." + nested_enum.name
65
+ dependency_data[full_enum_type] = {local_namespace}
66
+ local_definitions.append(full_enum_type)
67
+
68
+ for f in msg.field:
69
+ if ((FieldDescriptorProto.TYPE_MESSAGE == f.type) or (FieldDescriptorProto.TYPE_ENUM == f.type)) \
70
+ and (f.type_name not in local_definitions) \
71
+ and not f.type_name.startswith(local_namespace + "."):
72
+ dependencies.add(f.type_name)
73
+
74
+ # If we have any dependencies add them
75
+ if 0 < len(dependencies):
76
+ dependency_data[local_namespace] = dependencies
77
+
78
+ return dependency_data
79
+
80
+
81
+ # -----------------------------------------------------------------------------
82
+
83
+ class ProtoFile:
84
+ def __init__(self, proto_descriptor, options_file=None):
85
+ self.descriptor = proto_descriptor
86
+
87
+ # The external field options file, shared by every field in this run. None when no file was given.
88
+ self.options_file = options_file
89
+
90
+ if "proto2" == proto_descriptor.syntax:
91
+ raise Exception(proto_descriptor.name + ": Sorry, proto2 is not supported, please use proto3.")
92
+
93
+ # Select the edition feature profile for this file and resolve the
94
+ # file-scope feature set. proto3 maps to the proto3 profile so the existing
95
+ # generation path is a special case of the same machinery.
96
+ self.feature_resolver = FeatureResolver(proto_descriptor)
97
+ self.file_features = self.feature_resolver.file_features
98
+
99
+ # These file names are the ones used for creating the C++ files.
100
+ self.filename_with_folder = os.path.splitext(proto_descriptor.name)[0]
101
+ self.filename_without_folder = os.path.basename(self.filename_with_folder)
102
+
103
+ # Construct the base scope used in this file.
104
+ self.scope = None
105
+ if self.descriptor.package:
106
+ package_list = self.descriptor.package.split(".")
107
+ # The first element is the base scope.
108
+ self.scope = Scope(package_list[0], None)
109
+ # Next add additional scopes nesting the previous one.
110
+ for package in package_list[1:]:
111
+ self.scope = Scope(package, self.scope)
112
+
113
+ self.enum_definitions = [EnumDefinition(enum, self.scope, self.feature_resolver, self.file_features)
114
+ for enum in self.descriptor.enum_type]
115
+ self.msg_definitions = [MessageDefinition(msg, self.scope, self.feature_resolver, self.file_features,
116
+ self.options_file)
117
+ for msg in self.descriptor.message_type]
118
+
119
+ self.all_parameters_registered = False
120
+
121
+ # Sort the message definitions such that the dependencies work out.
122
+ dependency_data = {}
123
+ toposort_namespace = ""
124
+ if self.descriptor.package:
125
+ toposort_namespace = "." + self.descriptor.package
126
+
127
+ for msg in self.descriptor.message_type:
128
+ dependency_data = toposort_add_msg(msg, toposort_namespace, dependency_data)
129
+
130
+ try:
131
+ # Sort the message in the order they should appear in the code.
132
+ message_order = toposort_flatten(dependency_data)
133
+
134
+ # If we are in a namespace this appears in the list and should be removed.
135
+ my_scope = ""
136
+ if self.scope and (0 < len(message_order)):
137
+ my_scope = "." + self.scope.get_scope_str().replace("::", ".")
138
+ message_order.remove(my_scope)
139
+
140
+ # Based on the desired order assign each message definition an index.
141
+ for index, msg_name in enumerate(message_order):
142
+ for msg_def in self.msg_definitions:
143
+ if msg_def.name == msg_name.replace(my_scope + ".", ''):
144
+ msg_def.sorted_index = index
145
+ break
146
+
147
+ # Next sort the messages based on their index.
148
+ self.msg_definitions.sort(key=lambda msg: msg.sorted_index)
149
+
150
+ # Sort al the nested message definitions.
151
+ for msg in self.msg_definitions:
152
+ msg.sort_nested_msg_definitions(message_order)
153
+
154
+ except CircularDependencyError as e:
155
+ raise Exception("There are possible circular dependencies in the message definitions of "
156
+ + proto_descriptor.name + ". Embedded Proto is not able to support this. "
157
+ "Please remove these dependencies.")
158
+
159
+ def get_dependencies(self):
160
+ imported_dependencies = []
161
+ if self.descriptor.dependency:
162
+ extension = self.options_file.header_extension() if self.options_file is not None else field_options.DEFAULT_HEADER_EXTENSION
163
+ imported_dependencies = [os.path.splitext(dependency)[0] + extension for dependency in
164
+ self.descriptor.dependency if not is_excluded_proto_file(dependency)]
165
+ return imported_dependencies
166
+
167
+ def get_namespaces(self):
168
+ if self.scope:
169
+ result = self.scope.get_list_of_scope_str()
170
+ else:
171
+ result = []
172
+ return result
173
+
174
+ def get_header_guard(self):
175
+ return self.filename_with_folder.replace("/", "_").upper()
176
+
177
+ # Obtain a dictionary with references to all nested enums and messages
178
+ def get_all_nested_types(self):
179
+ nested_types = {"enums": [], "messages": []}
180
+ nested_types["enums"].extend(self.enum_definitions)
181
+ for msg in self.msg_definitions:
182
+ nt = msg.get_all_nested_types()
183
+ nested_types["enums"].extend(nt["enums"])
184
+ nested_types["messages"].extend(nt["messages"])
185
+ nested_types["messages"].append(msg)
186
+
187
+ return nested_types
188
+
189
+ def match_fields_with_definitions(self, all_types_definitions):
190
+ for msg in self.msg_definitions:
191
+ msg.match_fields_with_definitions(all_types_definitions)
192
+
193
+ def register_template_parameters(self):
194
+ all_parameters_registered = True
195
+ for msg in self.msg_definitions:
196
+ all_parameters_registered = msg.register_template_parameters() and all_parameters_registered
197
+ return all_parameters_registered
198
+
199
+ def render(self, jinja_environment):
200
+ template_file = "Header.h.jinja2"
201
+ template = jinja_environment.get_template(template_file)
202
+ file_str = template.render(proto_file=self, environment=jinja_environment)
203
+ return file_str
204
+
205
+ def print_template_data(self, indent):
206
+ print(indent + "File: " + self.filename_without_folder)
207
+ if self.msg_definitions:
208
+ for msg in self.msg_definitions:
209
+ msg.print_template_data(indent + "\t")
210
+
@@ -0,0 +1,367 @@
1
+ #
2
+ # Copyright (C) 2020-2026 Embedded AMS B.V. - All Rights Reserved
3
+ #
4
+ # This file is part of Embedded Proto.
5
+ #
6
+ # Embedded Proto is dual licensed. You may use it under the terms of the
7
+ # GNU General Public License version 3 (GPLv3) as published by the Free
8
+ # Software Foundation, or under a commercial license from Embedded AMS B.V.
9
+ #
10
+ # Under the GPLv3 you must release the source code of
11
+ # any product you distribute that includes Embedded Proto or code
12
+ # generated by it. A commercial license removes that obligation.
13
+ # See <https://embeddedproto.com/pricing/>.
14
+ #
15
+ # Embedded Proto is distributed WITHOUT ANY WARRANTY; without even the
16
+ # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17
+ # See the LICENSE file in the root of the repository for details.
18
+ #
19
+ # SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-EmbeddedProto-Commercial
20
+ #
21
+ # Embedded AMS B.V., Hoorn, the Netherlands - info at EmbeddedProto dot com
22
+ #
23
+
24
+ from .Field import Field
25
+ from .Oneof import Oneof
26
+ import jinja2
27
+
28
+
29
+ # This class deal with the scope in which definitions and field are located. It is used to keep track of template
30
+ # parameters required for a given scope.
31
+ class Scope:
32
+ def __init__(self, scope_name, parent):
33
+ # The name of this scope/namespace
34
+ self.name = scope_name
35
+
36
+ # A reference to the scope above this scope. If we are at the top level this will be None.
37
+ self.parent = parent
38
+
39
+ # Start of without any children.
40
+ self.child_scopes = []
41
+
42
+ # Register this object with it's parent.
43
+ if self.parent:
44
+ self.parent.child_scopes.append(self)
45
+
46
+ self.fields_with_templates = []
47
+
48
+ # This function is used
49
+ def get_list_of_scope_str(self):
50
+ if self.parent:
51
+ result = self.parent.get_list_of_scope_str()
52
+ result.append(self.name)
53
+ else:
54
+ result = [self.name]
55
+ return result
56
+
57
+ # When searching for the definition of a field this function returns a scope string equal to the type defined by
58
+ # protobuf.
59
+ def get_scope_str(self):
60
+ if self.parent:
61
+ scope_str = self.parent.get_scope_str() + "::" + self.name
62
+ else:
63
+ scope_str = self.name
64
+
65
+ return scope_str
66
+
67
+ def register_template_parameters(self, field):
68
+ # Guard against duplicate registration. This can happen because FieldRepeated now unconditionally registers
69
+ # itself, and a nested message field may trigger a second registration through its own register chain.
70
+ if field not in self.fields_with_templates:
71
+ self.fields_with_templates.append(field)
72
+
73
+ # Return the list of template parameters required for this scope alone.
74
+ def get_template_parameters(self):
75
+ # Collect all template parameters from every field that registered with this scope.
76
+ result = []
77
+ for field in self.fields_with_templates:
78
+ result.extend(field.get_template_parameters())
79
+
80
+ # Multiple fields may contribute parameters with the same name (e.g. when a field registers both through
81
+ # the repeated path and the nested message path). De-duplicate by name, keeping first-seen order.
82
+ unique = []
83
+ seen = set()
84
+ for param in result:
85
+ if param["name"] not in seen:
86
+ unique.append(param)
87
+ seen.add(param["name"])
88
+
89
+ # In C++ template parameter lists, parameters with default values must appear after those without. Separate
90
+ # them to enforce this ordering.
91
+ non_default = [param for param in unique if "default" not in param]
92
+ defaulted = [param for param in unique if "default" in param]
93
+
94
+ # Backward compatibility: if a message only has defaulted template parameters (e.g. a message where all
95
+ # repeated fields have a fixed maxLength and only contribute the storage type parameter), suppress them
96
+ # entirely. This prevents turning a previously non-templated message into a template class, which would
97
+ # break existing user code that instantiates it without template arguments.
98
+ if not non_default:
99
+ return []
100
+
101
+ return non_default + defaulted
102
+
103
+ # Return a full list of the scope, parent scopes and their templates
104
+ def get(self):
105
+ result = []
106
+ if self.parent:
107
+ result.extend(self.parent.get())
108
+ result.extend([{"name": self.name, "templates": self.get_template_parameters()}])
109
+ return result
110
+
111
+ # -----------------------------------------------------------------------------
112
+
113
+
114
+ class TypeDefinition:
115
+ def __init__(self, proto_descriptor, parent_scope, template_filename,
116
+ feature_resolver=None, enclosing_features=None):
117
+ self.descriptor = proto_descriptor
118
+ self.name = proto_descriptor.name
119
+ self.scope = Scope(self.name, parent_scope)
120
+ self.template_file = template_filename
121
+
122
+ # Editions feature resolution. The resolver picks the edition profile; the
123
+ # enclosing features are the resolved feature set of the parent scope (file
124
+ # or enclosing message). This type's own explicit overrides are merged on
125
+ # top to obtain its resolved feature set. proto3 files use the proto3
126
+ # profile so this is always populated.
127
+ self.feature_resolver = feature_resolver
128
+ if feature_resolver is not None:
129
+ base = enclosing_features if enclosing_features is not None \
130
+ else feature_resolver.file_features
131
+ self.features = feature_resolver.merge(base, proto_descriptor.options,
132
+ self.scope.get_scope_str())
133
+ else:
134
+ self.features = None
135
+
136
+ def get_name(self):
137
+ return self.name
138
+
139
+ def render(self, jinja_environment):
140
+ template = jinja_environment.get_template(self.template_file)
141
+ render_result = template.render(typedef=self, environment=jinja_environment)
142
+ return render_result
143
+
144
+
145
+ # -----------------------------------------------------------------------------
146
+
147
+ class EnumDefinition(TypeDefinition):
148
+ def __init__(self, proto_descriptor, parent_scope, feature_resolver=None, enclosing_features=None):
149
+ super().__init__(proto_descriptor, parent_scope, "TypeDefEnum.h.jinja2",
150
+ feature_resolver, enclosing_features)
151
+
152
+ # Loop through the values defined in the enum.
153
+ def values(self):
154
+ for value in self.descriptor.value:
155
+ yield value
156
+
157
+ # Whether this enum is CLOSED (editions enum_type feature). Closed enums
158
+ # validate decoded values and drop unknown ones; open enums store any value.
159
+ def is_closed(self):
160
+ from .Features import EnumType
161
+ if self.features is not None:
162
+ return EnumType.CLOSED == self.features["enum_type"]
163
+ return False
164
+
165
+ # The distinct value numbers declared in this enum (aliases collapsed), used to
166
+ # generate the CLOSED-enum validation helper without duplicate switch labels.
167
+ def unique_value_numbers(self):
168
+ seen = []
169
+ for value in self.descriptor.value:
170
+ if value.number not in seen:
171
+ seen.append(value.number)
172
+ return seen
173
+
174
+
175
+ # -----------------------------------------------------------------------------
176
+
177
+ class MessageDefinition(TypeDefinition):
178
+ def __init__(self, proto_descriptor, parent_scope, feature_resolver=None, enclosing_features=None,
179
+ options_file=None):
180
+ super().__init__(proto_descriptor, parent_scope, "TypeDefMsg.h.jinja2",
181
+ feature_resolver, enclosing_features)
182
+
183
+ # The external field options file. The fields of this message read their options from it, they reach it
184
+ # through their parent message the same way they reach the feature resolver.
185
+ self.options_file = options_file
186
+
187
+ self.nested_enum_definitions = [EnumDefinition(enum, self.scope, self.feature_resolver, self.features)
188
+ for enum in self.descriptor.enum_type]
189
+ self.nested_msg_definitions = [MessageDefinition(msg, self.scope, self.feature_resolver, self.features,
190
+ self.options_file)
191
+ for msg in self.descriptor.nested_type]
192
+
193
+ # Store the id numbers of all the fields to create the ID enum.
194
+ self.field_ids = []
195
+
196
+ # This message contains optional fields, or not.
197
+ self.optional_fields = []
198
+
199
+ # Store all the variable fields in this message.
200
+ self.fields = []
201
+ for f in self.descriptor.field:
202
+ if (not f.HasField('oneof_index')) or f.proto3_optional:
203
+ new_field = Field.factory(f, self)
204
+ self.fields.append(new_field)
205
+ self.field_ids.append((new_field.variable_id, new_field.variable_id_name))
206
+
207
+ # Store for which fields presence needs to be tracked. This now
208
+ # follows the resolved editions field_presence feature (EXPLICIT
209
+ # fields are optional) of which proto3_optional is a special case.
210
+ if new_field.optional:
211
+ self.optional_fields.append(new_field)
212
+
213
+ # Store all the oneof definitions in this message.
214
+ self.oneofs = []
215
+ for index, oneof in enumerate(self.descriptor.oneof_decl):
216
+ new_oneof = Oneof(oneof, index, proto_descriptor, self)
217
+ oneof_fields = new_oneof.get_fields()
218
+ # For backwards compatibility proto3 optional fields are also include as oneof's with a single field. These
219
+ # dummy oneof's should not actually end up in code. Lets filter them out.
220
+ if oneof_fields[0].optional:
221
+ continue
222
+ else:
223
+ self.oneofs.append(new_oneof)
224
+ for oneof_field in new_oneof.get_fields():
225
+ self.field_ids.append((oneof_field.variable_id, oneof_field.variable_id_name))
226
+
227
+ # Sort the field id's such they will appear in order in the id enum.
228
+ self.field_ids.sort()
229
+
230
+ # Not all template parameters for this message definition have been registered with the scope. This is
231
+ # most likely due to a message defined later in the proto file.
232
+ self.all_parameters_registered = False
233
+
234
+ # Does this message definition contains fields with template parameters.
235
+ self.contains_template_parameters = False
236
+
237
+ # The index where in the source file this definition should be placed. This is later set by the ProtoFile class
238
+ # when the dependencies on other messages has been sorted.
239
+ self.sorted_index = 0
240
+
241
+ # Sort the nested message defintions based on the dependencies found by the sort algorithms.
242
+ def sort_nested_msg_definitions(self, message_order):
243
+ my_scope = "." + self.scope.get_scope_str().replace("::", ".")
244
+
245
+ for index, msg_name in enumerate(message_order):
246
+ for msg_def in self.nested_msg_definitions:
247
+ if msg_def.name == msg_name.replace(my_scope + ".", ''):
248
+ msg_def.sorted_index = index
249
+ break
250
+
251
+ # Next sort the messages based on their index.
252
+ self.nested_msg_definitions.sort(key=lambda msg: msg.sorted_index)
253
+
254
+ # Sort al the nested message definitions in the nested message definitions.
255
+ for nested_msg in self.nested_msg_definitions:
256
+ nested_msg.sort_nested_msg_definitions(message_order)
257
+
258
+ # Obtain a dictionary with references to all nested enums and messages
259
+ def get_all_nested_types(self):
260
+ nested_types = {"enums": [], "messages": []}
261
+ nested_types["enums"].extend(self.nested_enum_definitions)
262
+ for msg in self.nested_msg_definitions:
263
+ nt = msg.get_all_nested_types()
264
+ nested_types["enums"].extend(nt["enums"])
265
+ nested_types["messages"].extend(nt["messages"])
266
+ nested_types["messages"].append(msg)
267
+
268
+ return nested_types
269
+
270
+ def match_fields_with_definitions(self, all_types_definitions):
271
+ # Resolve the types of the nested messages.
272
+ for msg in self.nested_msg_definitions:
273
+ msg.match_fields_with_definitions(all_types_definitions)
274
+
275
+ # Resolve the types of the fields defined in this message.
276
+ for field in self.fields:
277
+ field.match_field_with_definitions(all_types_definitions)
278
+
279
+ for oneof in self.oneofs:
280
+ oneof.match_field_with_definitions(all_types_definitions)
281
+
282
+ # TODO This function will fail to return True if this definitions contains it self as a nested field.
283
+ def register_template_parameters(self):
284
+ self.all_parameters_registered = True
285
+ # First resolve the template parameters for all nested message definitions.
286
+ for nested_msg in self.nested_msg_definitions:
287
+ self.all_parameters_registered = nested_msg.register_template_parameters() \
288
+ and self.all_parameters_registered
289
+
290
+ # Next see if we our self have any fields that have template parameters.
291
+ for field in self.fields:
292
+ self.all_parameters_registered = field.register_template_parameters() and self.all_parameters_registered
293
+
294
+ for oneof in self.oneofs:
295
+ self.all_parameters_registered = oneof.register_template_parameters() and self.all_parameters_registered
296
+
297
+ return self.all_parameters_registered
298
+
299
+ def register_child_with_template(self, child):
300
+ self.scope.register_template_parameters(child)
301
+ self.contains_template_parameters = True
302
+
303
+ def get_templates(self):
304
+ return self.scope.get_template_parameters()
305
+
306
+ def get_type(self):
307
+ return self.scope.get_scope_str()
308
+
309
+ # The full protobuf name of this message, package and enclosing messages included, as it appears in the type url of
310
+ # a google.protobuf.Any.
311
+ def get_full_name(self):
312
+ return self.scope.get_scope_str().replace("::", ".")
313
+
314
+ def get_state_depth(self):
315
+ """
316
+ Calculate the required state depth for this message.
317
+
318
+ Returns:
319
+ int: Minimum 1, plus maximum depth of nested message fields.
320
+
321
+ Examples:
322
+ - message Simple { int32 a = 1; } -> depth = 1
323
+ - message A { B b = 1; } where B has no nested -> depth = 2
324
+ - message A { B b = 1; } where B { C c = 1; } -> depth = 3
325
+ """
326
+ max_nested_depth = 0
327
+
328
+ for field in self.fields:
329
+ # Check if this is a message field
330
+ if hasattr(field, 'definition') and field.definition is not None:
331
+ if isinstance(field.definition, MessageDefinition):
332
+ nested_depth = field.definition.get_state_depth()
333
+ if nested_depth > max_nested_depth:
334
+ max_nested_depth = nested_depth
335
+ # Check for repeated message fields
336
+ elif hasattr(field, 'actual_type') and hasattr(field.actual_type, 'definition'):
337
+ if field.actual_type.definition is not None and isinstance(field.actual_type.definition, MessageDefinition):
338
+ nested_depth = field.actual_type.definition.get_state_depth()
339
+ if nested_depth > max_nested_depth:
340
+ max_nested_depth = nested_depth
341
+
342
+ # Check oneof fields as well
343
+ for oneof in self.oneofs:
344
+ for field in oneof.get_fields():
345
+ if hasattr(field, 'definition') and field.definition is not None:
346
+ if isinstance(field.definition, MessageDefinition):
347
+ nested_depth = field.definition.get_state_depth()
348
+ if nested_depth > max_nested_depth:
349
+ max_nested_depth = nested_depth
350
+ elif hasattr(field, 'actual_type') and hasattr(field.actual_type, 'definition'):
351
+ if field.actual_type.definition is not None and isinstance(field.actual_type.definition, MessageDefinition):
352
+ nested_depth = field.actual_type.definition.get_state_depth()
353
+ if nested_depth > max_nested_depth:
354
+ max_nested_depth = nested_depth
355
+
356
+ return 1 + max_nested_depth # +1 for this message itself
357
+
358
+ def print_template_data(self, indent):
359
+ print(indent + "Message definition: " + self.name)
360
+ if self.nested_msg_definitions:
361
+ for msg in self.nested_msg_definitions:
362
+ msg.print_template_data(indent + "\t")
363
+
364
+ if self.fields:
365
+ for field in self.fields:
366
+ print(indent + "Field: " + field.name, end='')
367
+ print(field.get_template_parameters())
File without changes
@@ -0,0 +1,28 @@
1
+ #
2
+ # Copyright (C) 2020-2026 Embedded AMS B.V. - All Rights Reserved
3
+ #
4
+ # This file is part of Embedded Proto.
5
+ #
6
+ # Embedded Proto is dual licensed. You may use it under the terms of the
7
+ # GNU General Public License version 3 (GPLv3) as published by the Free
8
+ # Software Foundation, or under a commercial license from Embedded AMS B.V.
9
+ #
10
+ # Under the GPLv3 you must release the source code of
11
+ # any product you distribute that includes Embedded Proto or code
12
+ # generated by it. A commercial license removes that obligation.
13
+ # See <https://embeddedproto.com/pricing/>.
14
+ #
15
+ # Embedded Proto is distributed WITHOUT ANY WARRANTY; without even the
16
+ # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17
+ # See the LICENSE file in the root of the repository for details.
18
+ #
19
+ # SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-EmbeddedProto-Commercial
20
+ #
21
+ # Embedded AMS B.V., Hoorn, the Netherlands - info at EmbeddedProto dot com
22
+ #
23
+
24
+ from .main import main
25
+
26
+
27
+ if __name__ == '__main__':
28
+ main()