avrotize 2.21.0__tar.gz → 2.22.0__tar.gz
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.
- {avrotize-2.21.0 → avrotize-2.22.0}/PKG-INFO +1 -1
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/_version.py +3 -3
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/avrotogo.py +21 -8
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/avrotojava/class_test.java.jinja +2 -4
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/avrotojava.py +67 -5
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/avrotopython/dataclass_core.jinja +12 -2
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/avrotopython/test_class.jinja +2 -1
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/avrotopython.py +48 -2
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/avrotorust/dataclass_enum.rs.jinja +9 -7
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/avrotorust/dataclass_struct.rs.jinja +19 -11
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/avrotorust/dataclass_union.rs.jinja +25 -7
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/avrotorust.py +64 -26
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/structuretocsharp.py +42 -11
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/structuretodb.py +21 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/structuretogo.py +38 -10
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/structuretojava/pom.xml.jinja +5 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/structuretojava.py +114 -8
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/structuretopython/dataclass_core.jinja +2 -1
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/structuretopython.py +100 -19
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/structuretots.py +8 -5
- {avrotize-2.21.0 → avrotize-2.22.0}/LICENSE +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/README.md +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/__init__.py +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/__main__.py +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/asn1toavro.py +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/avrotize.py +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/avrotocpp/CMakeLists.txt.jinja +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/avrotocpp/build.bat.jinja +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/avrotocpp/build.sh.jinja +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/avrotocpp/dataclass_body.jinja +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/avrotocpp/vcpkg.json.jinja +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/avrotocpp.py +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/avrotocsharp/README.md.jinja +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/avrotocsharp/class_test.cs.jinja +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/avrotocsharp/dataclass_core.jinja +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/avrotocsharp/enum_test.cs.jinja +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/avrotocsharp/project.csproj.jinja +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/avrotocsharp/project.sln.jinja +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/avrotocsharp/run_coverage.ps1.jinja +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/avrotocsharp/run_coverage.sh.jinja +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/avrotocsharp/testproject.csproj.jinja +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/avrotocsharp.py +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/avrotocsv.py +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/avrotodatapackage.py +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/avrotodb.py +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/avrotogo/go_enum.jinja +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/avrotogo/go_helpers.jinja +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/avrotogo/go_struct.jinja +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/avrotogo/go_test.jinja +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/avrotogo/go_union.jinja +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/avrotographql.py +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/avrotoiceberg.py +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/avrotojava/enum_test.java.jinja +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/avrotojava/testproject.pom.jinja +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/avrotojs.py +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/avrotojsons.py +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/avrotojstruct.py +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/avrotokusto.py +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/avrotomd/README.md.jinja +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/avrotomd.py +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/avrotools.py +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/avrotoparquet.py +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/avrotoproto.py +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/avrotopython/enum_core.jinja +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/avrotopython/pyproject_toml.jinja +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/avrotopython/test_enum.jinja +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/avrotots/class_core.ts.jinja +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/avrotots/class_test.ts.jinja +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/avrotots/enum_core.ts.jinja +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/avrotots/gitignore.jinja +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/avrotots/index.ts.jinja +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/avrotots/package.json.jinja +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/avrotots/tsconfig.json.jinja +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/avrotots.py +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/avrotoxsd.py +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/cddltostructure.py +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/commands.json +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/common.py +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/constants.py +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/csvtoavro.py +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/datapackagetoavro.py +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/dependencies/cpp/vcpkg/vcpkg.json +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/dependencies/cs/net90/dependencies.csproj +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/dependencies/go/go121/go.mod +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/dependencies/java/jdk21/pom.xml +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/dependencies/python/py312/requirements.txt +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/dependencies/rust/stable/Cargo.toml +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/dependencies/typescript/node22/package.json +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/dependency_resolver.py +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/dependency_version.py +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/generic/generic.avsc +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/jsonstoavro.py +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/jsonstostructure.py +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/jstructtoavro.py +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/kstructtoavro.py +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/kustotoavro.py +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/openapitostructure.py +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/parquettoavro.py +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/proto2parser.py +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/proto3parser.py +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/prototoavro.py +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/prototypes/any.avsc +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/prototypes/api.avsc +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/prototypes/duration.avsc +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/prototypes/field_mask.avsc +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/prototypes/struct.avsc +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/prototypes/timestamp.avsc +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/prototypes/type.avsc +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/prototypes/wrappers.avsc +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/structuretocddl.py +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/structuretocpp/CMakeLists.txt.jinja +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/structuretocpp/build.bat.jinja +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/structuretocpp/build.sh.jinja +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/structuretocpp/dataclass_body.jinja +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/structuretocpp/vcpkg.json.jinja +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/structuretocpp.py +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/structuretocsharp/class_test.cs.jinja +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/structuretocsharp/dataclass_core.jinja +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/structuretocsharp/enum_test.cs.jinja +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/structuretocsharp/json_structure_converters.cs.jinja +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/structuretocsharp/program.cs.jinja +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/structuretocsharp/project.csproj.jinja +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/structuretocsharp/project.sln.jinja +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/structuretocsharp/testproject.csproj.jinja +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/structuretocsharp/tuple_converter.cs.jinja +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/structuretocsv.py +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/structuretodatapackage.py +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/structuretogo/go_enum.jinja +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/structuretogo/go_helpers.jinja +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/structuretogo/go_interface.jinja +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/structuretogo/go_struct.jinja +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/structuretogo/go_test.jinja +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/structuretographql.py +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/structuretoiceberg.py +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/structuretojava/choice_core.jinja +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/structuretojava/class_core.jinja +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/structuretojava/enum_core.jinja +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/structuretojava/equals_hashcode.jinja +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/structuretojava/tuple_core.jinja +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/structuretojs/class_core.js.jinja +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/structuretojs/enum_core.js.jinja +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/structuretojs/package.json.jinja +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/structuretojs/test_class.js.jinja +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/structuretojs/test_enum.js.jinja +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/structuretojs/test_runner.js.jinja +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/structuretojs.py +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/structuretojsons.py +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/structuretokusto.py +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/structuretomd/README.md.jinja +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/structuretomd.py +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/structuretoproto.py +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/structuretopython/enum_core.jinja +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/structuretopython/map_alias.jinja +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/structuretopython/pyproject_toml.jinja +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/structuretopython/test_class.jinja +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/structuretopython/test_enum.jinja +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/structuretorust/dataclass_enum.rs.jinja +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/structuretorust/dataclass_struct.rs.jinja +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/structuretorust/dataclass_union.rs.jinja +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/structuretorust.py +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/structuretots/class_core.ts.jinja +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/structuretots/enum_core.ts.jinja +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/structuretots/gitignore.jinja +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/structuretots/index.ts.jinja +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/structuretots/package.json.jinja +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/structuretots/test_class.ts.jinja +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/structuretots/tsconfig.json.jinja +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/structuretoxsd.py +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/avrotize/xsdtoavro.py +0 -0
- {avrotize-2.21.0 → avrotize-2.22.0}/pyproject.toml +0 -0
|
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
|
|
|
28
28
|
commit_id: COMMIT_ID
|
|
29
29
|
__commit_id__: COMMIT_ID
|
|
30
30
|
|
|
31
|
-
__version__ = version = '2.
|
|
32
|
-
__version_tuple__ = version_tuple = (2,
|
|
31
|
+
__version__ = version = '2.22.0'
|
|
32
|
+
__version_tuple__ = version_tuple = (2, 22, 0)
|
|
33
33
|
|
|
34
|
-
__commit_id__ = commit_id = '
|
|
34
|
+
__commit_id__ = commit_id = 'g5d3e04df0'
|
|
@@ -10,8 +10,15 @@ JsonNode = Dict[str, 'JsonNode'] | List['JsonNode'] | str | None
|
|
|
10
10
|
class AvroToGo:
|
|
11
11
|
"""Converts Avro schema to Go structs, including JSON and Avro marshalling methods"""
|
|
12
12
|
|
|
13
|
+
# Go reserved keywords that cannot be used as package names
|
|
14
|
+
GO_RESERVED_WORDS = [
|
|
15
|
+
'break', 'default', 'func', 'interface', 'select', 'case', 'defer', 'go', 'map', 'struct', 'chan',
|
|
16
|
+
'else', 'goto', 'package', 'switch', 'const', 'fallthrough', 'if', 'range', 'type', 'continue', 'for',
|
|
17
|
+
'import', 'return', 'var',
|
|
18
|
+
]
|
|
19
|
+
|
|
13
20
|
def __init__(self, base_package: str = '') -> None:
|
|
14
|
-
self.base_package = base_package
|
|
21
|
+
self.base_package = self._safe_package_name(base_package) if base_package else base_package
|
|
15
22
|
self.output_dir = os.getcwd()
|
|
16
23
|
self.generated_types_avro_namespace: Dict[str, str] = {}
|
|
17
24
|
self.generated_types_go_package: Dict[str, str] = {}
|
|
@@ -25,14 +32,15 @@ class AvroToGo:
|
|
|
25
32
|
self.structs = []
|
|
26
33
|
self.enums = []
|
|
27
34
|
|
|
35
|
+
def _safe_package_name(self, name: str) -> str:
|
|
36
|
+
"""Converts a name to a safe Go package name"""
|
|
37
|
+
if name in self.GO_RESERVED_WORDS:
|
|
38
|
+
return f"{name}_"
|
|
39
|
+
return name
|
|
40
|
+
|
|
28
41
|
def safe_identifier(self, name: str) -> str:
|
|
29
42
|
"""Converts a name to a safe Go identifier"""
|
|
30
|
-
|
|
31
|
-
'break', 'default', 'func', 'interface', 'select', 'case', 'defer', 'go', 'map', 'struct', 'chan',
|
|
32
|
-
'else', 'goto', 'package', 'switch', 'const', 'fallthrough', 'if', 'range', 'type', 'continue', 'for',
|
|
33
|
-
'import', 'return', 'var',
|
|
34
|
-
]
|
|
35
|
-
if name in reserved_words:
|
|
43
|
+
if name in self.GO_RESERVED_WORDS:
|
|
36
44
|
return f"{name}_"
|
|
37
45
|
return name
|
|
38
46
|
|
|
@@ -157,6 +165,10 @@ class AvroToGo:
|
|
|
157
165
|
'original_name': field['name']
|
|
158
166
|
} for field in avro_schema.get('fields', [])]
|
|
159
167
|
|
|
168
|
+
# Collect imports from field types
|
|
169
|
+
go_types = [f['type'] for f in fields]
|
|
170
|
+
imports = self.get_imports_for_definition(go_types)
|
|
171
|
+
|
|
160
172
|
context = {
|
|
161
173
|
'doc': avro_schema.get('doc', ''),
|
|
162
174
|
'struct_name': go_struct_name,
|
|
@@ -166,6 +178,7 @@ class AvroToGo:
|
|
|
166
178
|
'avro_annotation': self.avro_annotation,
|
|
167
179
|
'json_match_predicates': [self.get_is_json_match_clause(f['name'], f['type']) for f in fields],
|
|
168
180
|
'base_package': self.base_package,
|
|
181
|
+
'imports': imports,
|
|
169
182
|
}
|
|
170
183
|
|
|
171
184
|
pkg_dir = os.path.join(self.output_dir, 'pkg', self.base_package)
|
|
@@ -430,7 +443,7 @@ class AvroToGo:
|
|
|
430
443
|
def convert(self, avro_schema_path: str, output_dir: str):
|
|
431
444
|
"""Converts Avro schema to Go"""
|
|
432
445
|
if not self.base_package:
|
|
433
|
-
self.base_package = os.path.splitext(os.path.basename(avro_schema_path))[0]
|
|
446
|
+
self.base_package = self._safe_package_name(os.path.splitext(os.path.basename(avro_schema_path))[0])
|
|
434
447
|
|
|
435
448
|
with open(avro_schema_path, 'r', encoding='utf-8') as file:
|
|
436
449
|
schema = json.load(file)
|
|
@@ -113,8 +113,7 @@ public class {{ test_class_name }} {
|
|
|
113
113
|
"Instances with different {{ field.field_name }} should not be equal");
|
|
114
114
|
{%- elif field.is_enum %}
|
|
115
115
|
// Enum - try to get a different enum value if more than one value exists
|
|
116
|
-
{
|
|
117
|
-
{{ simple_type }}[] enumValues = {{ simple_type }}.values();
|
|
116
|
+
{{ base_type }}[] enumValues = {{ base_type }}.values();
|
|
118
117
|
if (enumValues.length > 1) {
|
|
119
118
|
instance2.set{{ field.field_name | pascal }}(enumValues[(instance2.get{{ field.field_name | pascal }}().ordinal() + 1) % enumValues.length]);
|
|
120
119
|
assertNotEquals(instance1, instance2,
|
|
@@ -150,8 +149,7 @@ public class {{ test_class_name }} {
|
|
|
150
149
|
{%- if '<' in field.field_type %}
|
|
151
150
|
{{ field.field_type }} testValue = {{ field.test_value }};
|
|
152
151
|
{%- else %}
|
|
153
|
-
{
|
|
154
|
-
{{ simple_field_type }} testValue = {{ field.test_value }};
|
|
152
|
+
{{ field.field_type }} testValue = {{ field.test_value }};
|
|
155
153
|
{%- endif %}
|
|
156
154
|
{%- if not field.is_const %}
|
|
157
155
|
instance.set{{ field.field_name | pascal }}(testValue);
|
|
@@ -1721,6 +1721,51 @@ class AvroToJava:
|
|
|
1721
1721
|
def get_test_imports(self, fields: List) -> List[str]:
|
|
1722
1722
|
""" Gets the necessary imports for the test class """
|
|
1723
1723
|
imports = []
|
|
1724
|
+
|
|
1725
|
+
# Track simple names to detect conflicts
|
|
1726
|
+
# Map: simple_name -> list of FQNs that have that simple name
|
|
1727
|
+
simple_name_to_fqns: Dict[str, List[str]] = {}
|
|
1728
|
+
|
|
1729
|
+
# First pass: collect all custom type FQNs and their simple names
|
|
1730
|
+
for field in fields:
|
|
1731
|
+
inner_types = []
|
|
1732
|
+
if field.field_type.startswith("List<"):
|
|
1733
|
+
inner_type = field.field_type[5:-1]
|
|
1734
|
+
if inner_type.startswith("Map<"):
|
|
1735
|
+
start = inner_type.index('<') + 1
|
|
1736
|
+
end = inner_type.rindex('>')
|
|
1737
|
+
map_types = inner_type[start:end].split(',')
|
|
1738
|
+
if len(map_types) > 1:
|
|
1739
|
+
inner_types.append(map_types[1].strip())
|
|
1740
|
+
else:
|
|
1741
|
+
inner_types.append(inner_type)
|
|
1742
|
+
elif field.field_type.startswith("Map<"):
|
|
1743
|
+
start = field.field_type.index('<') + 1
|
|
1744
|
+
end = field.field_type.rindex('>')
|
|
1745
|
+
map_types = field.field_type[start:end].split(',')
|
|
1746
|
+
if len(map_types) > 1:
|
|
1747
|
+
inner_types.append(map_types[1].strip())
|
|
1748
|
+
if not field.field_type.startswith(("List<", "Map<")):
|
|
1749
|
+
inner_types.append(field.field_type)
|
|
1750
|
+
if hasattr(field, 'java_type_obj') and field.java_type_obj and field.java_type_obj.union_types:
|
|
1751
|
+
for union_member_type in field.java_type_obj.union_types:
|
|
1752
|
+
inner_types.append(union_member_type.type_name)
|
|
1753
|
+
|
|
1754
|
+
for type_to_check in inner_types:
|
|
1755
|
+
if type_to_check in self.generated_types_java_package and '.' in type_to_check:
|
|
1756
|
+
simple_name = type_to_check.split('.')[-1]
|
|
1757
|
+
if simple_name not in simple_name_to_fqns:
|
|
1758
|
+
simple_name_to_fqns[simple_name] = []
|
|
1759
|
+
if type_to_check not in simple_name_to_fqns[simple_name]:
|
|
1760
|
+
simple_name_to_fqns[simple_name].append(type_to_check)
|
|
1761
|
+
|
|
1762
|
+
# Find conflicting simple names (same simple name, different FQNs)
|
|
1763
|
+
conflicting_fqns: set = set()
|
|
1764
|
+
for simple_name, fqns in simple_name_to_fqns.items():
|
|
1765
|
+
if len(fqns) > 1:
|
|
1766
|
+
# This simple name has conflicts - mark all FQNs as conflicting
|
|
1767
|
+
conflicting_fqns.update(fqns)
|
|
1768
|
+
|
|
1724
1769
|
for field in fields:
|
|
1725
1770
|
# Extract inner types from generic collections
|
|
1726
1771
|
inner_types = []
|
|
@@ -1772,7 +1817,8 @@ class AvroToJava:
|
|
|
1772
1817
|
if type_to_check in self.generated_types_java_package:
|
|
1773
1818
|
type_kind = self.generated_types_java_package[type_to_check]
|
|
1774
1819
|
# Only import if it's a fully qualified name with a package
|
|
1775
|
-
|
|
1820
|
+
# Skip imports for types with conflicting simple names - they'll use FQN
|
|
1821
|
+
if '.' in type_to_check and type_to_check not in conflicting_fqns:
|
|
1776
1822
|
import_stmt = f"import {type_to_check};"
|
|
1777
1823
|
if import_stmt not in imports:
|
|
1778
1824
|
imports.append(import_stmt)
|
|
@@ -1809,10 +1855,11 @@ class AvroToJava:
|
|
|
1809
1855
|
if java_qualified_name:
|
|
1810
1856
|
if java_qualified_name in self.generated_types_java_package or java_qualified_name.split('.')[-1] in self.generated_types_java_package:
|
|
1811
1857
|
member_type_kind = self.generated_types_java_package.get(java_qualified_name, self.generated_types_java_package.get(java_qualified_name.split('.')[-1], None))
|
|
1812
|
-
# Import the class/enum
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
imports
|
|
1858
|
+
# Import the class/enum only if not conflicting
|
|
1859
|
+
if java_qualified_name not in conflicting_fqns:
|
|
1860
|
+
class_import = f"import {java_qualified_name};"
|
|
1861
|
+
if class_import not in imports:
|
|
1862
|
+
imports.append(class_import)
|
|
1816
1863
|
# No longer import test classes - we instantiate classes directly
|
|
1817
1864
|
return imports
|
|
1818
1865
|
|
|
@@ -1920,6 +1967,21 @@ class AvroToJava:
|
|
|
1920
1967
|
'Double': 'Double.valueOf(3.14)',
|
|
1921
1968
|
'byte[]': 'new byte[] { 0x01, 0x02, 0x03 }',
|
|
1922
1969
|
'Object': 'null', # Use null for Object types (Avro unions) to avoid reference equality issues
|
|
1970
|
+
# Java time types - use factory methods, not constructors
|
|
1971
|
+
'Instant': 'java.time.Instant.now()',
|
|
1972
|
+
'java.time.Instant': 'java.time.Instant.now()',
|
|
1973
|
+
'LocalDate': 'java.time.LocalDate.now()',
|
|
1974
|
+
'java.time.LocalDate': 'java.time.LocalDate.now()',
|
|
1975
|
+
'LocalTime': 'java.time.LocalTime.now()',
|
|
1976
|
+
'java.time.LocalTime': 'java.time.LocalTime.now()',
|
|
1977
|
+
'LocalDateTime': 'java.time.LocalDateTime.now()',
|
|
1978
|
+
'java.time.LocalDateTime': 'java.time.LocalDateTime.now()',
|
|
1979
|
+
'Duration': 'java.time.Duration.ofSeconds(42)',
|
|
1980
|
+
'java.time.Duration': 'java.time.Duration.ofSeconds(42)',
|
|
1981
|
+
'UUID': 'java.util.UUID.randomUUID()',
|
|
1982
|
+
'java.util.UUID': 'java.util.UUID.randomUUID()',
|
|
1983
|
+
'BigDecimal': 'new java.math.BigDecimal("42.00")',
|
|
1984
|
+
'java.math.BigDecimal': 'new java.math.BigDecimal("42.00")',
|
|
1923
1985
|
}
|
|
1924
1986
|
|
|
1925
1987
|
# Handle generic types
|
|
@@ -25,7 +25,7 @@ import avro.schema
|
|
|
25
25
|
import avro.name
|
|
26
26
|
import avro.io
|
|
27
27
|
{%- endif %}
|
|
28
|
-
{%- for import_type in import_types if import_type not in ['datetime.datetime', 'datetime.date', 'datetime.time', 'datetime.timedelta', 'decimal.Decimal'] %}
|
|
28
|
+
{%- for import_type in import_types if import_type not in ['datetime.datetime', 'datetime.date', 'datetime.time', 'datetime.timedelta', 'decimal.Decimal', 'uuid.UUID'] %}
|
|
29
29
|
from {{ '.'.join(import_type.split('.')[:-1]) | lower }} import {{ import_type.split('.')[-1] }}
|
|
30
30
|
{%- endfor %}
|
|
31
31
|
{%- for import_type in import_types if import_type in ['datetime.datetime', 'datetime.date', 'datetime.time', 'datetime.timedelta'] %}
|
|
@@ -33,6 +33,16 @@ from {{ '.'.join(import_type.split('.')[:-1]) | lower }} import {{ import_type.s
|
|
|
33
33
|
import datetime
|
|
34
34
|
{%- endif %}
|
|
35
35
|
{%- endfor %}
|
|
36
|
+
{%- for import_type in import_types if import_type == 'decimal.Decimal' %}
|
|
37
|
+
{%- if loop.first %}
|
|
38
|
+
import decimal
|
|
39
|
+
{%- endif %}
|
|
40
|
+
{%- endfor %}
|
|
41
|
+
{%- for import_type in import_types if import_type == 'uuid.UUID' %}
|
|
42
|
+
{%- if loop.first %}
|
|
43
|
+
import uuid
|
|
44
|
+
{%- endif %}
|
|
45
|
+
{%- endfor %}
|
|
36
46
|
|
|
37
47
|
{% if dataclasses_json_annotation %}
|
|
38
48
|
@dataclass_json(undefined=Undefined.EXCLUDE)
|
|
@@ -48,7 +58,7 @@ class {{ class_name }}:
|
|
|
48
58
|
"""
|
|
49
59
|
{% for field in fields %}
|
|
50
60
|
{%- set isdate = field.type == "datetime" or field.type == "typing.Optional[datetime.datetime]" %}
|
|
51
|
-
{{ field.name }}: {{ field.type }}=dataclasses.field(kw_only=True{% if dataclasses_json_annotation %}, metadata=dataclasses_json.config(field_name="{{ field.original_name }}"{%- if isdate -%}, encoder=lambda d:
|
|
61
|
+
{{ field.name }}: {{ field.type }}=dataclasses.field(kw_only=True{% if dataclasses_json_annotation %}, metadata=dataclasses_json.config(field_name="{{ field.original_name }}"{%- if isdate -%}, encoder=lambda d: d.isoformat() if isinstance(d, datetime.datetime) else d if d else None, decoder=lambda d: datetime.datetime.fromisoformat(d) if isinstance(d, str) else d if d else None, mm_field=fields.DateTime(format='iso'){%- endif -%}){%- endif %})
|
|
52
62
|
{%- endfor %}
|
|
53
63
|
{% if avro_annotation %}
|
|
54
64
|
AvroType: typing.ClassVar[avro.schema.Schema] = avro.schema.make_avsc_object(
|
|
@@ -12,7 +12,8 @@ from {{ package_name | lower }} import {{ class_name }}
|
|
|
12
12
|
|
|
13
13
|
{%- for import_type in import_types if import_type not in ['decimal.Decimal', 'datetime.datetime', 'datetime.date', 'datetime.time', 'datetime.timedelta'] %}
|
|
14
14
|
{%- set import_type_name = 'Test_'+import_type.split('.')[-1] %}
|
|
15
|
-
{%- set
|
|
15
|
+
{%- set flat_package = '_'.join(import_type.split('.')[:-1]) | lower %}
|
|
16
|
+
{%- set import_package_name = flat_package if flat_package.startswith('test_') else 'test_' + flat_package %}
|
|
16
17
|
|
|
17
18
|
{%- if import_type.startswith('.') %}
|
|
18
19
|
from .{{ import_package_name }} import {{ import_type_name }}
|
|
@@ -12,6 +12,38 @@ from avrotize.common import fullname, get_typing_args_from_string, is_generic_av
|
|
|
12
12
|
|
|
13
13
|
INDENT = ' '
|
|
14
14
|
|
|
15
|
+
# Python standard library modules that should not be shadowed by package names
|
|
16
|
+
PYTHON_STDLIB_MODULES = {
|
|
17
|
+
'abc', 'aifc', 'argparse', 'array', 'ast', 'asynchat', 'asyncio', 'asyncore',
|
|
18
|
+
'atexit', 'audioop', 'base64', 'bdb', 'binascii', 'binhex', 'bisect', 'builtins',
|
|
19
|
+
'bz2', 'calendar', 'cgi', 'cgitb', 'chunk', 'cmath', 'cmd', 'code', 'codecs',
|
|
20
|
+
'codeop', 'collections', 'colorsys', 'compileall', 'concurrent', 'configparser',
|
|
21
|
+
'contextlib', 'contextvars', 'copy', 'copyreg', 'cProfile', 'crypt', 'csv',
|
|
22
|
+
'ctypes', 'curses', 'dataclasses', 'datetime', 'dbm', 'decimal', 'difflib',
|
|
23
|
+
'dis', 'distutils', 'doctest', 'email', 'encodings', 'enum', 'errno', 'faulthandler',
|
|
24
|
+
'fcntl', 'filecmp', 'fileinput', 'fnmatch', 'fractions', 'ftplib', 'functools',
|
|
25
|
+
'gc', 'getopt', 'getpass', 'gettext', 'glob', 'graphlib', 'grp', 'gzip',
|
|
26
|
+
'hashlib', 'heapq', 'hmac', 'html', 'http', 'imaplib', 'imghdr', 'imp',
|
|
27
|
+
'importlib', 'inspect', 'io', 'ipaddress', 'itertools', 'json', 'keyword',
|
|
28
|
+
'lib2to3', 'linecache', 'locale', 'logging', 'lzma', 'mailbox', 'mailcap',
|
|
29
|
+
'marshal', 'math', 'mimetypes', 'mmap', 'modulefinder', 'multiprocessing',
|
|
30
|
+
'netrc', 'nis', 'nntplib', 'numbers', 'operator', 'optparse', 'os', 'ossaudiodev',
|
|
31
|
+
'pathlib', 'pdb', 'pickle', 'pickletools', 'pipes', 'pkgutil', 'platform',
|
|
32
|
+
'plistlib', 'poplib', 'posix', 'posixpath', 'pprint', 'profile', 'pstats',
|
|
33
|
+
'pty', 'pwd', 'py_compile', 'pyclbr', 'pydoc', 'queue', 'quopri', 'random',
|
|
34
|
+
're', 'readline', 'reprlib', 'resource', 'rlcompleter', 'runpy', 'sched',
|
|
35
|
+
'secrets', 'select', 'selectors', 'shelve', 'shlex', 'shutil', 'signal',
|
|
36
|
+
'site', 'smtpd', 'smtplib', 'sndhdr', 'socket', 'socketserver', 'spwd',
|
|
37
|
+
'sqlite3', 'ssl', 'stat', 'statistics', 'string', 'stringprep', 'struct',
|
|
38
|
+
'subprocess', 'sunau', 'symtable', 'sys', 'sysconfig', 'syslog', 'tabnanny',
|
|
39
|
+
'tarfile', 'telnetlib', 'tempfile', 'termios', 'test', 'textwrap', 'threading',
|
|
40
|
+
'time', 'timeit', 'tkinter', 'token', 'tokenize', 'trace', 'traceback',
|
|
41
|
+
'tracemalloc', 'tty', 'turtle', 'turtledemo', 'types', 'typing', 'unicodedata',
|
|
42
|
+
'unittest', 'urllib', 'uu', 'uuid', 'venv', 'warnings', 'wave', 'weakref',
|
|
43
|
+
'webbrowser', 'winreg', 'winsound', 'wsgiref', 'xdrlib', 'xml', 'xmlrpc',
|
|
44
|
+
'zipapp', 'zipfile', 'zipimport', 'zlib', 'zoneinfo',
|
|
45
|
+
}
|
|
46
|
+
|
|
15
47
|
|
|
16
48
|
def is_python_reserved_word(word: str) -> bool:
|
|
17
49
|
"""Checks if a word is a Python reserved word"""
|
|
@@ -25,6 +57,13 @@ def is_python_reserved_word(word: str) -> bool:
|
|
|
25
57
|
return word in reserved_words
|
|
26
58
|
|
|
27
59
|
|
|
60
|
+
def safe_package_name(name: str) -> str:
|
|
61
|
+
"""Converts a name to a safe Python package name that won't shadow stdlib"""
|
|
62
|
+
if name.lower() in PYTHON_STDLIB_MODULES:
|
|
63
|
+
return f"{name}_types"
|
|
64
|
+
return name
|
|
65
|
+
|
|
66
|
+
|
|
28
67
|
class AvroToPython:
|
|
29
68
|
"""Converts Avro schema to Python data classes"""
|
|
30
69
|
|
|
@@ -167,6 +206,9 @@ class AvroToPython:
|
|
|
167
206
|
enum_ref = self.generate_enum(avro_type, parent_package, write_file=True)
|
|
168
207
|
import_types.add(enum_ref)
|
|
169
208
|
return self.strip_package_from_fully_qualified_name(enum_ref)
|
|
209
|
+
elif avro_type['type'] == 'fixed':
|
|
210
|
+
# Fixed types are represented as bytes in Python
|
|
211
|
+
return 'bytes'
|
|
170
212
|
elif avro_type['type'] == 'array':
|
|
171
213
|
return f"typing.List[{self.convert_avro_type_to_python(avro_type['items'], parent_package, import_types)}]"
|
|
172
214
|
elif avro_type['type'] == 'map':
|
|
@@ -327,7 +369,8 @@ class AvroToPython:
|
|
|
327
369
|
def generate_test_class(self, package_name: str, class_name: str, fields: List[Dict[str, str]], import_types: Set[str]) -> None:
|
|
328
370
|
"""Generates a unit test class for a Python data class"""
|
|
329
371
|
test_class_name = f"Test_{class_name}"
|
|
330
|
-
|
|
372
|
+
flat_package = package_name.replace('.', '_').lower()
|
|
373
|
+
tests_package_name = flat_package if flat_package.startswith('test_') else f"test_{flat_package}"
|
|
331
374
|
test_class_definition = process_template(
|
|
332
375
|
"avrotopython/test_class.jinja",
|
|
333
376
|
package_name=package_name,
|
|
@@ -348,7 +391,8 @@ class AvroToPython:
|
|
|
348
391
|
def generate_test_enum(self, package_name: str, class_name: str, symbols: List[str]) -> None:
|
|
349
392
|
"""Generates a unit test class for a Python enum"""
|
|
350
393
|
test_class_name = f"Test_{class_name}"
|
|
351
|
-
|
|
394
|
+
flat_package = package_name.replace('.', '_').lower()
|
|
395
|
+
tests_package_name = flat_package if flat_package.startswith('test_') else f"test_{flat_package}"
|
|
352
396
|
test_class_definition = process_template(
|
|
353
397
|
"avrotopython/test_enum.jinja",
|
|
354
398
|
package_name=package_name,
|
|
@@ -609,6 +653,7 @@ def convert_avro_to_python(avro_schema_path, py_file_path, package_name='', data
|
|
|
609
653
|
if not package_name:
|
|
610
654
|
package_name = os.path.splitext(os.path.basename(avro_schema_path))[
|
|
611
655
|
0].lower().replace('-', '_')
|
|
656
|
+
package_name = safe_package_name(package_name)
|
|
612
657
|
|
|
613
658
|
avro_to_python = AvroToPython(
|
|
614
659
|
package_name, dataclasses_json_annotation=dataclasses_json_annotation, avro_annotation=avro_annotation)
|
|
@@ -617,6 +662,7 @@ def convert_avro_to_python(avro_schema_path, py_file_path, package_name='', data
|
|
|
617
662
|
|
|
618
663
|
def convert_avro_schema_to_python(avro_schema, py_file_path, package_name='', dataclasses_json_annotation=False, avro_annotation=False):
|
|
619
664
|
"""Converts Avro schema to Python data classes"""
|
|
665
|
+
package_name = safe_package_name(package_name) if package_name else package_name
|
|
620
666
|
avro_to_python = AvroToPython(
|
|
621
667
|
package_name, dataclasses_json_annotation=dataclasses_json_annotation, avro_annotation=avro_annotation)
|
|
622
668
|
if isinstance(avro_schema, dict):
|
|
@@ -55,20 +55,22 @@ mod tests {
|
|
|
55
55
|
use super::*;
|
|
56
56
|
use rand::Rng;
|
|
57
57
|
|
|
58
|
+
{%- if serde_annotation %}
|
|
58
59
|
#[test]
|
|
59
60
|
fn test_serialize_deserialize_{{ enum_name.lower() }}() {
|
|
60
61
|
{%- for symbol in symbols %}
|
|
61
62
|
let instance = {{ enum_name }}::{{ symbol }};
|
|
62
|
-
{%- if serde_annotation %}
|
|
63
63
|
let json_bytes = serde_json::to_vec(&instance).unwrap();
|
|
64
64
|
let deserialized_instance: {{ enum_name }} = serde_json::from_slice(&json_bytes).unwrap();
|
|
65
65
|
assert_eq!(instance, deserialized_instance);
|
|
66
|
-
{%-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
66
|
+
{%- endfor %}
|
|
67
|
+
}
|
|
68
|
+
{%- endif %}
|
|
69
|
+
|
|
70
|
+
#[test]
|
|
71
|
+
fn test_enum_variants_{{ enum_name.lower() }}() {
|
|
72
|
+
{%- for symbol in symbols %}
|
|
73
|
+
let _instance = {{ enum_name }}::{{ symbol }};
|
|
72
74
|
{%- endfor %}
|
|
73
75
|
}
|
|
74
76
|
}
|
|
@@ -4,28 +4,28 @@ use apache_avro::Schema;
|
|
|
4
4
|
{%- endif %}
|
|
5
5
|
{%- if serde_annotation or avro_annotation %}
|
|
6
6
|
use serde::{Serialize, Deserialize};
|
|
7
|
-
{%- endif %}
|
|
8
7
|
use std::io::Write;
|
|
9
8
|
use flate2::write::GzEncoder;
|
|
10
9
|
use flate2::read::GzDecoder;
|
|
10
|
+
{%- endif %}
|
|
11
11
|
{%- set uses_chrono = false %}
|
|
12
12
|
{%- set uses_uuid = false %}
|
|
13
13
|
{%- set uses_hashmap = false%}
|
|
14
14
|
{%- for field in fields %}
|
|
15
|
-
{%- if field.type == "NaiveDate" or field.type == "NaiveTime" or field.type == "NaiveDateTime" %}
|
|
15
|
+
{%- if field.type == "NaiveDate" or field.type == "NaiveTime" or field.type == "NaiveDateTime" or "chrono::" in field.type %}
|
|
16
16
|
{%- set uses_chrono = true %}
|
|
17
17
|
{%- endif %}
|
|
18
|
-
{%- if field.type == "Uuid" %}
|
|
18
|
+
{%- if field.type == "Uuid" or "uuid::" in field.type %}
|
|
19
19
|
{%- set uses_uuid = true %}
|
|
20
20
|
{%- endif %}
|
|
21
|
-
{%- if field.type.startswith("HashMap<") %}
|
|
21
|
+
{%- if field.type.startswith("HashMap<") or "HashMap<" in field.type %}
|
|
22
22
|
{%- set uses_hashmap = true %}
|
|
23
23
|
{%- endif %}
|
|
24
24
|
{%- endfor %}
|
|
25
25
|
{%- if uses_chrono %}
|
|
26
26
|
use chrono::{NaiveDate, NaiveTime, NaiveDateTime};
|
|
27
27
|
{%- endif %}
|
|
28
|
-
{%- if
|
|
28
|
+
{%- if uses_uuid %}
|
|
29
29
|
use uuid::Uuid;
|
|
30
30
|
{%- endif %}
|
|
31
31
|
{%- if uses_hashmap %}
|
|
@@ -39,7 +39,7 @@ use std::collections::HashMap;
|
|
|
39
39
|
#[derive(Debug{%- if serde_annotation or avro_annotation %}, Serialize, Deserialize{%- endif %}, PartialEq, Clone, Default)]
|
|
40
40
|
pub struct {{ struct_name }} {
|
|
41
41
|
{%- for field in fields %}
|
|
42
|
-
{%- if field.serde_rename %}
|
|
42
|
+
{%- if field.serde_rename and (serde_annotation or avro_annotation) %}
|
|
43
43
|
#[serde(rename = "{{ field.original_name }}")]
|
|
44
44
|
{%- endif %}
|
|
45
45
|
pub {{ field.name }}: {{ field.type }},
|
|
@@ -55,6 +55,7 @@ lazy_static! {
|
|
|
55
55
|
{%- endif %}
|
|
56
56
|
|
|
57
57
|
impl {{ struct_name }} {
|
|
58
|
+
{%- if serde_annotation or avro_annotation %}
|
|
58
59
|
/// Serializes the struct to a byte array based on the provided content type
|
|
59
60
|
pub fn to_byte_array(&self, content_type: &str) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
|
|
60
61
|
let result: Vec<u8>;
|
|
@@ -70,14 +71,10 @@ impl {{ struct_name }} {
|
|
|
70
71
|
let value = apache_avro::to_value(self).unwrap();
|
|
71
72
|
result = apache_avro::to_avro_datum(&SCHEMA, value).unwrap();
|
|
72
73
|
}
|
|
73
|
-
{
|
|
74
|
-
{%- if avro_annotation or serde_annotation %}
|
|
74
|
+
else {% endif -%}
|
|
75
75
|
{
|
|
76
|
-
{%- endif %}
|
|
77
76
|
return Err(format!("unsupported media type: {}", media_type).into())
|
|
78
|
-
{%- if avro_annotation or serde_annotation %}
|
|
79
77
|
}
|
|
80
|
-
{%- endif %}
|
|
81
78
|
if media_type.ends_with("+gzip") {
|
|
82
79
|
let mut encoder = GzEncoder::new(Vec::new(), flate2::Compression::default());
|
|
83
80
|
encoder.write_all(&result)?;
|
|
@@ -87,7 +84,9 @@ impl {{ struct_name }} {
|
|
|
87
84
|
return Ok(result)
|
|
88
85
|
}
|
|
89
86
|
}
|
|
87
|
+
{%- endif %}
|
|
90
88
|
|
|
89
|
+
{%- if serde_annotation or avro_annotation %}
|
|
91
90
|
/// Deserializes the struct from a byte array based on the provided content type
|
|
92
91
|
pub fn from_data(data: impl AsRef<[u8]>, content_type: &str) -> Result<Self, Box<dyn std::error::Error>> {
|
|
93
92
|
let media_type = content_type.split(';').next().unwrap_or("");
|
|
@@ -114,7 +113,9 @@ impl {{ struct_name }} {
|
|
|
114
113
|
{%- endif %}
|
|
115
114
|
Err(format!("unsupported media type: {}", media_type).into())
|
|
116
115
|
}
|
|
116
|
+
{%- endif %}
|
|
117
117
|
|
|
118
|
+
{%- if serde_annotation or avro_annotation %}
|
|
118
119
|
/// Checks if the given JSON value matches the schema of the struct
|
|
119
120
|
pub fn is_json_match(node: &serde_json::Value) -> bool {
|
|
120
121
|
{%- for predicate in json_match_predicates %}
|
|
@@ -122,6 +123,7 @@ impl {{ struct_name }} {
|
|
|
122
123
|
{%- endfor %}
|
|
123
124
|
true
|
|
124
125
|
}
|
|
126
|
+
{%- endif %}
|
|
125
127
|
|
|
126
128
|
/// Returns the struct instance itself
|
|
127
129
|
pub fn to_object(&self) -> &Self {
|
|
@@ -160,6 +162,12 @@ mod tests {
|
|
|
160
162
|
assert!(instance.{{ field.name }} != {{ field_type }}::default()); // Check that {{ field.name }} is not default
|
|
161
163
|
{%- elif field.type.startswith("std::collections::HashMap<")%}
|
|
162
164
|
assert!(instance.{{ field.name }}.len() >= 0); // Check that {{ field.name }} is not empty
|
|
165
|
+
{%- elif field.is_generated_type %}
|
|
166
|
+
// Skip default comparison for generated types (enums/unions) as random values may match default
|
|
167
|
+
let _ = &instance.{{ field.name }};
|
|
168
|
+
{%- elif field.type in ["i8", "i16", "i32", "i64", "u8", "u16", "u32", "u64", "f32", "f64", "isize", "usize"] %}
|
|
169
|
+
// Skip default comparison for numeric types as random values can legitimately be 0
|
|
170
|
+
let _ = instance.{{ field.name }};
|
|
163
171
|
{%- elif field.type != "bool" %}
|
|
164
172
|
assert!(instance.{{ field.name }} != {{ field.type }}::default()); // Check that {{ field.name }} is not default
|
|
165
173
|
{%- endif %}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
{%- if serde_annotation %}
|
|
1
|
+
{%- if serde_annotation or avro_annotation %}
|
|
2
2
|
use serde::{self, Serialize, Deserialize};
|
|
3
3
|
{%- endif %}
|
|
4
4
|
|
|
@@ -70,14 +70,25 @@ impl {{ union_enum_name }} {
|
|
|
70
70
|
|
|
71
71
|
#[cfg(test)]
|
|
72
72
|
impl {{ union_enum_name }} {
|
|
73
|
+
{%- set unique_fields = union_fields | selectattr('is_first_with_predicate') | list %}
|
|
73
74
|
pub fn generate_random_instance() -> {{ union_enum_name }} {
|
|
74
75
|
let mut rng = rand::thread_rng();
|
|
76
|
+
{%- if serde_annotation and unique_fields | length < union_fields | length %}
|
|
77
|
+
// Only pick from variants with unique JSON structures to ensure round-trip works
|
|
78
|
+
match rand::Rng::gen_range(&mut rng, 0..{{ unique_fields | length }}) {
|
|
79
|
+
{%- for union_field in unique_fields %}
|
|
80
|
+
{{ loop.index0 }} => {{ union_enum_name }}::{{ union_field.name }}({{ union_field.random_value }}),
|
|
81
|
+
{%- endfor %}
|
|
82
|
+
_ => panic!("Invalid random index generated"),
|
|
83
|
+
}
|
|
84
|
+
{%- else %}
|
|
75
85
|
match rand::Rng::gen_range(&mut rng, 0..{{ union_fields | length }}) {
|
|
76
86
|
{%- for union_field in union_fields %}
|
|
77
87
|
{{ loop.index0 }} => {{ union_enum_name }}::{{ union_field.name }}({{ union_field.random_value }}),
|
|
78
88
|
{%- endfor %}
|
|
79
89
|
_ => panic!("Invalid random index generated"),
|
|
80
90
|
}
|
|
91
|
+
{%- endif %}
|
|
81
92
|
}
|
|
82
93
|
}
|
|
83
94
|
|
|
@@ -85,21 +96,28 @@ impl {{ union_enum_name }} {
|
|
|
85
96
|
mod tests {
|
|
86
97
|
use super::*;
|
|
87
98
|
|
|
99
|
+
{%- if serde_annotation %}
|
|
88
100
|
#[test]
|
|
89
101
|
fn test_serialize_deserialize_{{ union_enum_name.lower() }}() {
|
|
90
102
|
let mut rng = rand::thread_rng();
|
|
91
103
|
{%- for union_field in union_fields %}
|
|
104
|
+
{%- if union_field.is_first_with_predicate %}
|
|
92
105
|
let instance = {{ union_enum_name }}::{{ union_field.name }}({{ union_field.random_value }});
|
|
93
|
-
{%- if serde_annotation %}
|
|
94
106
|
let json_bytes = serde_json::to_vec(&instance).unwrap();
|
|
95
107
|
let deserialized_instance: {{ union_enum_name }} = serde_json::from_slice(&json_bytes).unwrap();
|
|
96
108
|
assert_eq!(instance, deserialized_instance);
|
|
109
|
+
{%- else %}
|
|
110
|
+
// Skip {{ union_field.name }} - structurally identical to earlier variant, would deserialize as first match
|
|
97
111
|
{%- endif %}
|
|
98
|
-
{%-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
112
|
+
{%- endfor %}
|
|
113
|
+
}
|
|
114
|
+
{%- endif %}
|
|
115
|
+
|
|
116
|
+
#[test]
|
|
117
|
+
fn test_union_variants_{{ union_enum_name.lower() }}() {
|
|
118
|
+
let mut rng = rand::thread_rng();
|
|
119
|
+
{%- for union_field in union_fields %}
|
|
120
|
+
let _instance = {{ union_enum_name }}::{{ union_field.name }}({{ union_field.random_value }});
|
|
103
121
|
{%- endfor %}
|
|
104
122
|
}
|
|
105
123
|
}
|