structurize 2.19.0__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 (70) hide show
  1. avrotize/__init__.py +64 -0
  2. avrotize/__main__.py +6 -0
  3. avrotize/_version.py +34 -0
  4. avrotize/asn1toavro.py +160 -0
  5. avrotize/avrotize.py +152 -0
  6. avrotize/avrotocpp.py +483 -0
  7. avrotize/avrotocsharp.py +1075 -0
  8. avrotize/avrotocsv.py +121 -0
  9. avrotize/avrotodatapackage.py +173 -0
  10. avrotize/avrotodb.py +1383 -0
  11. avrotize/avrotogo.py +476 -0
  12. avrotize/avrotographql.py +197 -0
  13. avrotize/avrotoiceberg.py +210 -0
  14. avrotize/avrotojava.py +2156 -0
  15. avrotize/avrotojs.py +250 -0
  16. avrotize/avrotojsons.py +481 -0
  17. avrotize/avrotojstruct.py +345 -0
  18. avrotize/avrotokusto.py +364 -0
  19. avrotize/avrotomd.py +137 -0
  20. avrotize/avrotools.py +168 -0
  21. avrotize/avrotoparquet.py +208 -0
  22. avrotize/avrotoproto.py +359 -0
  23. avrotize/avrotopython.py +624 -0
  24. avrotize/avrotorust.py +435 -0
  25. avrotize/avrotots.py +598 -0
  26. avrotize/avrotoxsd.py +344 -0
  27. avrotize/cddltostructure.py +1841 -0
  28. avrotize/commands.json +3337 -0
  29. avrotize/common.py +834 -0
  30. avrotize/constants.py +72 -0
  31. avrotize/csvtoavro.py +132 -0
  32. avrotize/datapackagetoavro.py +76 -0
  33. avrotize/dependencies/cpp/vcpkg/vcpkg.json +19 -0
  34. avrotize/dependencies/typescript/node22/package.json +16 -0
  35. avrotize/dependency_resolver.py +348 -0
  36. avrotize/dependency_version.py +432 -0
  37. avrotize/jsonstoavro.py +2167 -0
  38. avrotize/jsonstostructure.py +2642 -0
  39. avrotize/jstructtoavro.py +878 -0
  40. avrotize/kstructtoavro.py +93 -0
  41. avrotize/kustotoavro.py +455 -0
  42. avrotize/parquettoavro.py +157 -0
  43. avrotize/proto2parser.py +498 -0
  44. avrotize/proto3parser.py +403 -0
  45. avrotize/prototoavro.py +382 -0
  46. avrotize/structuretocddl.py +597 -0
  47. avrotize/structuretocpp.py +697 -0
  48. avrotize/structuretocsharp.py +2295 -0
  49. avrotize/structuretocsv.py +365 -0
  50. avrotize/structuretodatapackage.py +659 -0
  51. avrotize/structuretodb.py +1125 -0
  52. avrotize/structuretogo.py +720 -0
  53. avrotize/structuretographql.py +502 -0
  54. avrotize/structuretoiceberg.py +355 -0
  55. avrotize/structuretojava.py +853 -0
  56. avrotize/structuretojsons.py +498 -0
  57. avrotize/structuretokusto.py +639 -0
  58. avrotize/structuretomd.py +322 -0
  59. avrotize/structuretoproto.py +764 -0
  60. avrotize/structuretopython.py +772 -0
  61. avrotize/structuretorust.py +714 -0
  62. avrotize/structuretots.py +653 -0
  63. avrotize/structuretoxsd.py +679 -0
  64. avrotize/xsdtoavro.py +413 -0
  65. structurize-2.19.0.dist-info/METADATA +107 -0
  66. structurize-2.19.0.dist-info/RECORD +70 -0
  67. structurize-2.19.0.dist-info/WHEEL +5 -0
  68. structurize-2.19.0.dist-info/entry_points.txt +2 -0
  69. structurize-2.19.0.dist-info/licenses/LICENSE +201 -0
  70. structurize-2.19.0.dist-info/top_level.txt +1 -0
avrotize/avrotocpp.py ADDED
@@ -0,0 +1,483 @@
1
+ # pylint: disable=too-many-arguments, too-many-locals, too-many-branches, too-many-statements, line-too-long
2
+
3
+ """Generates C++ code from Avro schema"""
4
+ import json
5
+ import os
6
+ from typing import Dict, List, Union
7
+
8
+ from avrotize.common import is_generic_avro_type, pascal, process_template
9
+
10
+ INDENT = ' '
11
+
12
+ JsonNode = Dict[str, 'JsonNode'] | List['JsonNode'] | str | None
13
+
14
+
15
+ class AvroToCpp:
16
+ """Converts Avro schema to C++ code, including JSON and Avro methods"""
17
+
18
+ def __init__(self, base_namespace: str = '') -> None:
19
+ self.base_namespace = base_namespace
20
+ self.output_dir = os.getcwd()
21
+ self.generated_types_avro_namespace: Dict[str, str] = {}
22
+ self.generated_types_cpp_namespace: Dict[str, str] = {}
23
+ self.avro_annotation = False
24
+ self.json_annotation = False
25
+ self.generated_files: List[str] = []
26
+ self.test_files: List[str] = []
27
+
28
+ def safe_identifier(self, name: str) -> str:
29
+ """Converts a name to a safe C++ identifier"""
30
+ reserved_words = [
31
+ 'alignas', 'alignof', 'and', 'and_eq', 'asm', 'atomic_cancel', 'atomic_commit', 'atomic_noexcept', 'auto',
32
+ 'bitand', 'bitor', 'bool', 'break', 'case', 'catch', 'char', 'char8_t', 'char16_t', 'char32_t', 'class',
33
+ 'compl', 'concept', 'const', 'consteval', 'constexpr', 'constinit', 'const_cast', 'continue', 'co_await',
34
+ 'co_return', 'co_yield', 'decltype', 'default', 'delete', 'do', 'double', 'dynamic_cast', 'else', 'enum',
35
+ 'explicit', 'export', 'extern', 'false', 'float', 'for', 'friend', 'goto', 'if', 'inline', 'int', 'long',
36
+ 'mutable', 'namespace', 'new', 'noexcept', 'not', 'not_eq', 'nullptr', 'operator', 'or', 'or_eq', 'private',
37
+ 'protected', 'public', 'reflexpr', 'register', 'reinterpret_cast', 'requires', 'return', 'short', 'signed',
38
+ 'sizeof', 'static', 'static_assert', 'static_cast', 'struct', 'switch', 'synchronized', 'template', 'this',
39
+ 'thread_local', 'throw', 'true', 'try', 'typedef', 'typeid', 'typename', 'union', 'unsigned', 'using',
40
+ 'virtual', 'void', 'volatile', 'wchar_t', 'while', 'xor', 'xor_eq'
41
+ ]
42
+ if name in reserved_words:
43
+ return f"{name}_"
44
+ return name
45
+
46
+ def map_primitive_to_cpp(self, avro_type: str, is_optional: bool) -> str:
47
+ """Maps Avro primitive types to C++ types"""
48
+ optional_mapping = {
49
+ 'null': 'std::optional<std::monostate>',
50
+ 'boolean': 'std::optional<bool>',
51
+ 'int': 'std::optional<int>',
52
+ 'long': 'std::optional<long long>',
53
+ 'float': 'std::optional<float>',
54
+ 'double': 'std::optional<double>',
55
+ 'bytes': 'std::optional<std::vector<uint8_t>>',
56
+ 'string': 'std::optional<std::string>'
57
+ }
58
+ required_mapping = {
59
+ 'null': 'std::monostate',
60
+ 'boolean': 'bool',
61
+ 'int': 'int',
62
+ 'long': 'long long',
63
+ 'float': 'float',
64
+ 'double': 'double',
65
+ 'bytes': 'std::vector<uint8_t>',
66
+ 'string': 'std::string'
67
+ }
68
+ if '.' in avro_type:
69
+ type_name = avro_type.split('.')[-1]
70
+ package_name = '::'.join(avro_type.split('.')[:-1]).lower()
71
+ avro_type = self.get_qualified_name(package_name, type_name)
72
+ if avro_type in self.generated_types_avro_namespace:
73
+ kind = self.generated_types_avro_namespace[avro_type]
74
+ qualified_class_name = self.get_qualified_name(self.base_namespace, avro_type)
75
+ return qualified_class_name
76
+ else:
77
+ return required_mapping.get(avro_type, avro_type) if not is_optional else optional_mapping.get(avro_type, avro_type)
78
+
79
+ def get_qualified_name(self, namespace: str, name: str) -> str:
80
+ """Concatenates namespace and name using a double colon separator"""
81
+ return f"{namespace}::{name}" if namespace else name
82
+
83
+ def concat_namespace(self, namespace: str, name: str) -> str:
84
+ """Concatenates namespace and name using a double colon separator"""
85
+ if namespace and name:
86
+ return f"{namespace}::{name}"
87
+ elif namespace:
88
+ return namespace
89
+ return name
90
+
91
+ def convert_avro_type_to_cpp(self, field_name: str, avro_type: Union[str, Dict, List], nullable: bool = False, parent_namespace: str = '') -> str:
92
+ """Converts Avro type to C++ type"""
93
+ if isinstance(avro_type, str):
94
+ return self.map_primitive_to_cpp(avro_type, nullable)
95
+ elif isinstance(avro_type, list):
96
+ if is_generic_avro_type(avro_type):
97
+ return 'nlohmann::json'
98
+ non_null_types = [t for t in avro_type if t != 'null']
99
+ if len(non_null_types) == 1:
100
+ if isinstance(non_null_types[0], str):
101
+ return self.map_primitive_to_cpp(non_null_types[0], True)
102
+ else:
103
+ return self.convert_avro_type_to_cpp(field_name, non_null_types[0], parent_namespace=parent_namespace)
104
+ else:
105
+ types: List[str] = [self.convert_avro_type_to_cpp(field_name, t, parent_namespace=parent_namespace) for t in non_null_types]
106
+ return 'std::variant<' + ', '.join(types) + '>'
107
+ elif isinstance(avro_type, dict):
108
+ if avro_type['type'] in ['record', 'enum']:
109
+ return self.generate_class_or_enum(avro_type, parent_namespace)
110
+ elif avro_type['type'] == 'fixed' or avro_type['type'] == 'bytes' and 'logicalType' in avro_type:
111
+ if avro_type['logicalType'] == 'decimal':
112
+ return 'std::string' # Handle decimal as string for simplicity
113
+ elif avro_type['type'] == 'array':
114
+ item_type = self.convert_avro_type_to_cpp(field_name, avro_type['items'], nullable=True, parent_namespace=parent_namespace)
115
+ return f"std::vector<{item_type}>"
116
+ elif avro_type['type'] == 'map':
117
+ values_type = self.convert_avro_type_to_cpp(field_name, avro_type['values'], nullable=True, parent_namespace=parent_namespace)
118
+ return f"std::map<std::string, {values_type}>"
119
+ elif 'logicalType' in avro_type:
120
+ if avro_type['logicalType'] == 'date':
121
+ return 'std::chrono::system_clock::time_point'
122
+ elif avro_type['logicalType'] == 'time-millis' or avro_type['logicalType'] == 'time-micros':
123
+ return 'std::chrono::milliseconds'
124
+ elif avro_type['logicalType'] == 'timestamp-millis' or avro_type['logicalType'] == 'timestamp-micros':
125
+ return 'std::chrono::system_clock::time_point'
126
+ elif avro_type['logicalType'] == 'uuid':
127
+ return 'boost::uuids::uuid'
128
+ return self.convert_avro_type_to_cpp(field_name, avro_type['type'], parent_namespace=parent_namespace)
129
+ return 'nlohmann::json'
130
+
131
+ def generate_class_or_enum(self, avro_schema: Dict, parent_namespace: str) -> str:
132
+ """Generates a C++ class or enum from an Avro schema"""
133
+ if avro_schema['type'] == 'record':
134
+ return self.generate_class(avro_schema, parent_namespace)
135
+ elif avro_schema['type'] == 'enum':
136
+ return self.generate_enum(avro_schema, parent_namespace)
137
+ return 'nlohmann::json'
138
+
139
+ def generate_class(self, avro_schema: Dict, parent_namespace: str) -> str:
140
+ """Generates a C++ class from an Avro record schema"""
141
+ class_definition = ''
142
+ if 'doc' in avro_schema:
143
+ class_definition += f"// {avro_schema['doc']}\n"
144
+ avro_namespace = avro_schema.get('namespace', parent_namespace)
145
+ namespace = self.concat_namespace(self.base_namespace, avro_namespace.replace('.', '::'))
146
+ class_name = self.safe_identifier(avro_schema['name'])
147
+ qualified_class_name = self.get_qualified_name(namespace, class_name)
148
+ if qualified_class_name in self.generated_types_avro_namespace:
149
+ return qualified_class_name
150
+ self.generated_types_avro_namespace[qualified_class_name] = avro_namespace
151
+ self.generated_types_cpp_namespace[qualified_class_name] = "class"
152
+
153
+ # Track the includes for member types
154
+ member_includes = set()
155
+
156
+ class_definition += f"class {class_name} {{\n"
157
+ class_definition += "public:\n"
158
+ for field in avro_schema.get('fields', []):
159
+ field_name = self.safe_identifier(field['name'])
160
+
161
+ # Track the Avro type before conversion to C++ type
162
+ avro_field_type = field['type']
163
+
164
+ # Convert to C++ type
165
+ field_type = self.convert_avro_type_to_cpp(field_name, avro_field_type, parent_namespace=avro_namespace)
166
+
167
+ # Check if the field_type is a custom type that requires an include
168
+ if isinstance(avro_field_type, dict) and avro_field_type['type'] in ['record', 'enum']:
169
+ include_namespace = self.concat_namespace(
170
+ self.base_namespace,
171
+ avro_field_type.get('namespace', avro_namespace).replace('.', '::')
172
+ )
173
+ include_name = avro_field_type['name']
174
+ member_includes.add(self.get_qualified_name(include_namespace, include_name))
175
+
176
+ class_definition += f"{INDENT}{field_type} {field_name};\n"
177
+ class_definition += "public:\n"
178
+ class_definition += f"{INDENT}{class_name}() = default;\n"
179
+
180
+ class_definition += process_template("avrotocpp/dataclass_body.jinja", class_name=class_name, avro_annotation=self.avro_annotation, json_annotation=self.json_annotation)
181
+ if self.json_annotation:
182
+ class_definition += self.generate_is_json_match_method(class_name, avro_schema)
183
+ class_definition += self.generate_to_json_method(class_name)
184
+ class_definition += "};\n\n"
185
+
186
+ # Create includes
187
+ includes = self.generate_includes(member_includes)
188
+
189
+ self.write_to_file(namespace, class_name, includes, class_definition)
190
+ self.generate_unit_test(class_name, avro_schema.get('fields', []), namespace)
191
+ return qualified_class_name
192
+
193
+ def generate_enum(self, avro_schema: Dict, parent_namespace: str) -> str:
194
+ """Generates a C++ enum from an Avro enum schema"""
195
+ enum_definition = ''
196
+ if 'doc' in avro_schema:
197
+ enum_definition += f"// {avro_schema['doc']}\n"
198
+ avro_namespace = avro_schema.get('namespace', parent_namespace)
199
+ namespace = self.concat_namespace(self.base_namespace, avro_namespace.replace('.', '::'))
200
+ enum_name = self.safe_identifier(avro_schema['name'])
201
+ qualified_enum_name = self.get_qualified_name(namespace, enum_name)
202
+ self.generated_types_avro_namespace[qualified_enum_name] = avro_namespace
203
+ self.generated_types_cpp_namespace[qualified_enum_name] = "enum"
204
+ symbols = avro_schema.get('symbols', [])
205
+ enum_definition += f"enum class {enum_name} {{\n"
206
+ for symbol in symbols:
207
+ enum_definition += f"{INDENT}{symbol},\n"
208
+ enum_definition += "};\n\n"
209
+ self.write_to_file(namespace, enum_name, "", enum_definition)
210
+ return qualified_enum_name
211
+
212
+ def generate_to_byte_array_method(self, class_name: str) -> str:
213
+ """Generates the to_byte_array method for the class"""
214
+ return process_template("avrotocpp/to_byte_array_method.jinja", class_name=class_name, avro_annotation=self.avro_annotation, json_annotation=self.json_annotation)
215
+
216
+ def generate_from_data_method(self, class_name: str) -> str:
217
+ """Generates the from_data method for the class"""
218
+ return process_template("avrotocpp/from_data_method.jinja", class_name=class_name, avro_annotation=self.avro_annotation, json_annotation=self.json_annotation)
219
+
220
+ def generate_is_json_match_method(self, class_name: str, avro_schema: Dict) -> str:
221
+ """Generates the is_json_match method for the class"""
222
+ method_definition = f"\nstatic bool is_json_match(const nlohmann::json& node) {{\n"
223
+ predicates = []
224
+ for field in avro_schema.get('fields', []):
225
+ field_name = self.safe_identifier(field['name'])
226
+ field_type = self.convert_avro_type_to_cpp(field_name, field['type'])
227
+ predicates.append(self.get_is_json_match_clause(field_name, field_type))
228
+ method_definition += f"{INDENT}return " + " && ".join(predicates) + ";\n"
229
+ method_definition += f"}}\n"
230
+ return method_definition
231
+
232
+ def get_is_json_match_clause(self, field_name: str, field_type: str) -> str:
233
+ """Generates the is_json_match clause for a field"""
234
+ if field_type == 'std::string' or field_type == 'std::optional<std::string>':
235
+ return f"node.contains(\"{field_name}\") && node[\"{field_name}\"].is_string()"
236
+ elif field_type == 'bool' or field_type == 'std::optional<bool>':
237
+ return f"node.contains(\"{field_name}\") && node[\"{field_name}\"].is_boolean()"
238
+ elif field_type == 'int' or field_type == 'std::optional<int>':
239
+ return f"node.contains(\"{field_name}\") && node[\"{field_name}\"].is_number_integer()"
240
+ elif field_type == 'long long' or field_type == 'std::optional<long long>':
241
+ return f"node.contains(\"{field_name}\") && node[\"{field_name}\"].is_number_integer()"
242
+ elif field_type == 'float' or field_type == 'std::optional<float>':
243
+ return f"node.contains(\"{field_name}\") && node[\"{field_name}\"].is_number_float()"
244
+ elif field_type == 'double' or field_type == 'std::optional<double>':
245
+ return f"node.contains(\"{field_name}\") && node[\"{field_name}\"].is_number_float()"
246
+ elif field_type == 'std::vector<uint8_t>' or field_type == 'std::optional<std::vector<uint8_t>>':
247
+ return f"node.contains(\"{field_name}\") && node[\"{field_name}\"].is_binary()"
248
+ elif field_type == 'nlohmann::json':
249
+ return f"node.contains(\"{field_name}\") && node[\"{field_name}\"].is_object()"
250
+ elif field_type.startswith('std::map<std::string, '):
251
+ return f"node.contains(\"{field_name}\") && node[\"{field_name}\"].is_object()"
252
+ elif field_type.startswith('std::vector<'):
253
+ return f"node.contains(\"{field_name}\") && node[\"{field_name}\"].is_array()"
254
+ else:
255
+ return f"{field_type}::is_json_match(node[\"{field_name}\"])"
256
+
257
+ def generate_to_json_method(self, class_name: str) -> str:
258
+ """Generates the to_object method for the class"""
259
+ method_definition = f"\nnlohmann::json to_json() const {{\n"
260
+ method_definition += f"{INDENT}return nlohmann::json(*this);\n"
261
+ method_definition += f"}}\n"
262
+ return method_definition
263
+
264
+ def generate_avro_schema(self, class_name: str, avro_schema: Dict) -> str:
265
+ """Generates the AVRO_SCHEMA static variable and initialization code"""
266
+ schema_json = json.dumps(avro_schema, indent=4)
267
+ return process_template("avrotocpp/avro_schema.jinja", class_name=class_name, schema_json=schema_json)
268
+
269
+ def generate_serialize_avro_method(self, class_name: str) -> str:
270
+ """Generates the serialize_avro method for the class"""
271
+ return process_template("avrotocpp/serialize_avro_method.jinja", class_name=class_name)
272
+
273
+ def generate_deserialize_avro_method(self, class_name: str) -> str:
274
+ """Generates the deserialize_avro method for the class"""
275
+ return process_template("avrotocpp/deserialize_avro_method.jinja", class_name=class_name)
276
+
277
+ def generate_union_class(self, class_name: str, field_name: str, avro_type: List) -> str:
278
+ """Generates a union class for C++"""
279
+ union_class_name = class_name + pascal(field_name) + 'Union'
280
+ class_definition = f"class {union_class_name} {{\n"
281
+ class_definition += "public:\n"
282
+ union_types = [self.convert_avro_type_to_cpp(field_name + "Option" + str(i), t) for i, t in enumerate(avro_type)]
283
+ for union_type in union_types:
284
+ field_name = self.safe_identifier(union_type.split('::')[-1])
285
+ class_definition += f"{INDENT}{union_type} get_{field_name}() const;\n"
286
+ class_definition += "};\n\n"
287
+
288
+ # Write the union class to a separate file
289
+ namespace = self.get_qualified_name(self.base_namespace, class_name.lower())
290
+ includes = self.generate_includes(set(union_types))
291
+ self.write_to_file(namespace, union_class_name, includes, class_definition)
292
+
293
+ return union_class_name
294
+
295
+ def generate_includes(self, member_includes: set) -> str:
296
+ """Generates the include statements for the member types"""
297
+ includes = '\n'.join([f'#include "{include.replace("::", "/")}.hpp"' for include in member_includes])
298
+ return includes
299
+
300
+ def generate_unit_test(self, class_name: str, fields: List[Dict[str, str]], namespace: str):
301
+ """Generates a unit test for a given class"""
302
+ test_definition = f'#include <gtest/gtest.h>\n'
303
+ test_definition += f'#include "{namespace.replace("::", "/")}/{class_name}.hpp"\n\n'
304
+ test_definition += f'TEST({class_name}Test, PropertiesTest) {{\n'
305
+ test_definition += f'{INDENT}{namespace}::{class_name} instance;\n'
306
+
307
+ for field in fields:
308
+ field_name = self.safe_identifier(field['name'])
309
+ test_value = self.get_test_value(field['type'])
310
+ test_definition += f'{INDENT}instance.{field_name} = {test_value};\n'
311
+ test_definition += f'{INDENT}EXPECT_EQ(instance.{field_name}, {test_value});\n'
312
+
313
+ test_definition += '}\n'
314
+
315
+ test_dir = os.path.join(self.output_dir, "tests")
316
+ if not os.path.exists(test_dir):
317
+ os.makedirs(test_dir, exist_ok=True)
318
+
319
+ test_file_path = os.path.join(test_dir, f"{class_name}_test.cpp")
320
+ with open(test_file_path, 'w', encoding='utf-8') as file:
321
+ file.write(test_definition)
322
+
323
+ self.test_files.append(test_file_path.replace(os.sep, '/'))
324
+
325
+ def get_test_value(self, avro_type: Union[str, Dict, List]) -> str:
326
+ """Returns a default test value based on the Avro type"""
327
+ if isinstance(avro_type, str):
328
+ test_values = {
329
+ 'string': '"test_string"',
330
+ 'boolean': 'true',
331
+ 'int': '42',
332
+ 'long': '42LL',
333
+ 'float': '3.14f',
334
+ 'double': '3.14',
335
+ 'bytes': '{0x01, 0x02, 0x03}',
336
+ 'null': 'std::monostate()',
337
+ }
338
+ return test_values.get(avro_type, '/* Unknown type */')
339
+
340
+ elif isinstance(avro_type, list):
341
+ # For unions, use the first non-null type
342
+ non_null_types = [t for t in avro_type if t != 'null']
343
+ if non_null_types:
344
+ return self.get_test_value(non_null_types[0])
345
+ return '/* Unknown union type */'
346
+
347
+ elif isinstance(avro_type, dict):
348
+ avro_type_name = avro_type['type']
349
+ if avro_type_name == 'record':
350
+ return f"{avro_type['name']}()"
351
+ elif avro_type_name == 'enum':
352
+ return f"{avro_type['name']}::{avro_type['symbols'][0]}"
353
+ elif avro_type_name == 'array':
354
+ item_type = self.get_test_value(avro_type['items'])
355
+ return f"std::vector<{item_type}>{{{item_type}}}"
356
+ elif avro_type_name == 'map':
357
+ value_type = self.get_test_value(avro_type['values'])
358
+ return f"std::map<std::string, {value_type}>{{{{\"key\", {value_type}}}}}"
359
+ elif avro_type_name == 'fixed' or (avro_type_name == 'bytes' and 'logicalType' in avro_type and avro_type['logicalType'] == 'decimal'):
360
+ return '"fixed_bytes"'
361
+ elif 'logicalType' in avro_type:
362
+ logical_type = avro_type['logicalType']
363
+ if logical_type == 'date':
364
+ return 'std::chrono::system_clock::now()'
365
+ elif logical_type in ['time-millis', 'time-micros']:
366
+ return 'std::chrono::milliseconds(123456)'
367
+ elif logical_type in ['timestamp-millis', 'timestamp-micros']:
368
+ return 'std::chrono::system_clock::now()'
369
+ elif logical_type == 'uuid':
370
+ return 'boost::uuids::random_generator()()'
371
+ return '/* Unknown complex type */'
372
+
373
+ return '/* Unknown type */'
374
+
375
+
376
+
377
+ def write_to_file(self, namespace: str, name: str, includes: str, definition: str):
378
+ """Writes a C++ class or enum to a file"""
379
+ directory_path = os.path.join(
380
+ self.output_dir, "include", namespace.replace('::', os.sep))
381
+ if not os.path.exists(directory_path):
382
+ os.makedirs(directory_path, exist_ok=True)
383
+ file_path = os.path.join(directory_path, f"{name}.hpp")
384
+
385
+ with open(file_path, 'w', encoding='utf-8') as file:
386
+ file.write("#pragma once\n")
387
+ if self.json_annotation:
388
+ file.write("#include <nlohmann/json.hpp>\n")
389
+ if self.avro_annotation:
390
+ file.write("#include <avro/Specific.hh>\n")
391
+ file.write("#include <avro/Encoder.hh>\n")
392
+ file.write("#include <avro/Decoder.hh>\n")
393
+ file.write("#include <avro/Compiler.hh>\n")
394
+ file.write("#include <avro/Stream.hh>\n")
395
+ if "std::vector" in definition:
396
+ file.write("#include <vector>\n")
397
+ if "std::map" in definition:
398
+ file.write("#include <map>\n")
399
+ if "std::optional" in definition:
400
+ file.write("#include <optional>\n")
401
+ file.write("#include <stdexcept>\n")
402
+ if "std::chrono" in definition:
403
+ file.write("#include <chrono>\n")
404
+ if "boost::uuid" in definition:
405
+ file.write("#include <boost/uuid/uuid.hpp>\n")
406
+ file.write("#include <boost/uuid/uuid_io.hpp>\n")
407
+ if includes:
408
+ file.write(includes + '\n')
409
+ if namespace:
410
+ file.write(f"namespace {namespace} {{\n\n")
411
+ file.write(definition)
412
+ if namespace:
413
+ file.write(f"}} // namespace {namespace}\n")
414
+
415
+ # Collect the generated file names
416
+ self.generated_files.append(file_path.replace(os.sep, '/'))
417
+
418
+ def generate_cmake_lists(self, project_name: str):
419
+ """Generates a CMakeLists.txt file"""
420
+
421
+ # get the current file dir
422
+ cmake_content = process_template("avrotocpp/CMakeLists.txt.jinja", project_name=project_name, avro_annotation=self.avro_annotation, json_annotation=self.json_annotation)
423
+ cmake_path = os.path.join(self.output_dir, 'CMakeLists.txt')
424
+ with open(cmake_path, 'w', encoding='utf-8') as file:
425
+ file.write(cmake_content)
426
+
427
+ def generate_vcpkg_json(self):
428
+ """Generates a vcpkg.json file"""
429
+ vcpkg_json = process_template("avrotocpp/vcpkg.json.jinja", project_name=self.base_namespace, avro_annotation=self.avro_annotation, json_annotation=self.json_annotation)
430
+ vcpkg_json_path = os.path.join(self.output_dir, 'vcpkg.json')
431
+ with open(vcpkg_json_path, 'w', encoding='utf-8') as file:
432
+ file.write(vcpkg_json)
433
+
434
+ def generate_build_scripts(self):
435
+ """Generates build scripts for Windows and Linux"""
436
+ build_script_linux = process_template("avrotocpp/build.sh.jinja")
437
+ build_script_windows = process_template("avrotocpp/build.bat.jinja")
438
+ script_path_linux = os.path.join(self.output_dir, 'build.sh')
439
+ script_path_windows = os.path.join(self.output_dir, 'build.bat')
440
+
441
+ with open(script_path_linux, 'w', encoding='utf-8') as file:
442
+ file.write(build_script_linux)
443
+
444
+ with open(script_path_windows, 'w', encoding='utf-8') as file:
445
+ file.write(build_script_windows)
446
+
447
+ def convert_schema(self, schema: Union[Dict, List], output_dir: str):
448
+ """Converts Avro schema to C++"""
449
+ if not isinstance(schema, list):
450
+ schema = [schema]
451
+ if not os.path.exists(output_dir):
452
+ os.makedirs(output_dir, exist_ok=True)
453
+ self.output_dir = output_dir
454
+ for avro_schema in (x for x in schema if isinstance(x, dict)):
455
+ self.generate_class_or_enum(avro_schema, '')
456
+ self.generate_cmake_lists(self.base_namespace)
457
+ self.generate_build_scripts()
458
+ self.generate_vcpkg_json()
459
+
460
+ def convert(self, avro_schema_path: str, output_dir: str):
461
+ """Converts Avro schema to C++"""
462
+ with open(avro_schema_path, 'r', encoding='utf-8') as file:
463
+ schema = json.load(file)
464
+ self.convert_schema(schema, output_dir)
465
+
466
+
467
+ def convert_avro_to_cpp(avro_schema_path, output_dir, namespace='', avro_annotation=False, json_annotation=False):
468
+ """Converts Avro schema to C++ classes"""
469
+
470
+ if not namespace:
471
+ namespace = os.path.splitext(os.path.basename(avro_schema_path))[0].replace('-', '_')
472
+
473
+ avroToCpp = AvroToCpp(namespace)
474
+ avroToCpp.avro_annotation = avro_annotation
475
+ avroToCpp.json_annotation = json_annotation
476
+ avroToCpp.convert(avro_schema_path, output_dir)
477
+
478
+ def convert_avro_schema_to_cpp(avro_schema: Dict, output_dir: str, namespace: str = '', avro_annotation: bool = False, json_annotation: bool = False):
479
+ """Converts Avro schema to C++ classes"""
480
+ avroToCpp = AvroToCpp(namespace)
481
+ avroToCpp.avro_annotation = avro_annotation
482
+ avroToCpp.json_annotation = json_annotation
483
+ avroToCpp.convert_schema(avro_schema, output_dir)