structurize 3.6.1__tar.gz → 3.7.1__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.
Files changed (121) hide show
  1. {structurize-3.6.1/structurize.egg-info → structurize-3.7.1}/PKG-INFO +1 -1
  2. {structurize-3.6.1 → structurize-3.7.1}/avrotize/__init__.py +30 -0
  3. {structurize-3.6.1 → structurize-3.7.1}/avrotize/_version.py +3 -3
  4. structurize-3.7.1/avrotize/avrotocapnproto.py +207 -0
  5. {structurize-3.6.1 → structurize-3.7.1}/avrotize/avrotocsharp.py +230 -68
  6. structurize-3.7.1/avrotize/avrotocue.py +145 -0
  7. structurize-3.7.1/avrotize/avrotoflatbuffers.py +179 -0
  8. {structurize-3.6.1 → structurize-3.7.1}/avrotize/avrotojstruct.py +20 -0
  9. structurize-3.7.1/avrotize/avrotojtd.py +228 -0
  10. structurize-3.7.1/avrotize/avrotoraml.py +144 -0
  11. structurize-3.7.1/avrotize/avrotosmithy.py +311 -0
  12. structurize-3.7.1/avrotize/avrotosurreal.py +138 -0
  13. structurize-3.7.1/avrotize/avrotothrift.py +140 -0
  14. structurize-3.7.1/avrotize/capnprototoavro.py +407 -0
  15. {structurize-3.6.1 → structurize-3.7.1}/avrotize/commands.json +1288 -3
  16. {structurize-3.6.1 → structurize-3.7.1}/avrotize/common.py +18 -0
  17. structurize-3.7.1/avrotize/cuetoavro.py +450 -0
  18. structurize-3.7.1/avrotize/cuetostructure.py +67 -0
  19. {structurize-3.6.1 → structurize-3.7.1}/avrotize/dependency_resolver.py +60 -1
  20. structurize-3.7.1/avrotize/flatbufferstoavro.py +433 -0
  21. structurize-3.7.1/avrotize/flatbufferstojstruct.py +76 -0
  22. structurize-3.7.1/avrotize/jstructtoflatbuffers.py +23 -0
  23. structurize-3.7.1/avrotize/jstructtoraml.py +20 -0
  24. structurize-3.7.1/avrotize/jstructtosmithy.py +24 -0
  25. structurize-3.7.1/avrotize/jtdtoavro.py +233 -0
  26. structurize-3.7.1/avrotize/jtdtostructure.py +24 -0
  27. structurize-3.7.1/avrotize/ramltoavro.py +251 -0
  28. structurize-3.7.1/avrotize/ramltojstruct.py +20 -0
  29. structurize-3.7.1/avrotize/smithytoavro.py +542 -0
  30. structurize-3.7.1/avrotize/smithytojstruct.py +24 -0
  31. {structurize-3.6.1 → structurize-3.7.1}/avrotize/structuretocsharp.py +167 -37
  32. structurize-3.7.1/avrotize/structuretocue.py +26 -0
  33. structurize-3.7.1/avrotize/structuretojtd.py +24 -0
  34. structurize-3.7.1/avrotize/structuretoparquet.py +296 -0
  35. structurize-3.7.1/avrotize/surrealtoavro.py +318 -0
  36. structurize-3.7.1/avrotize/thriftstructure.py +48 -0
  37. structurize-3.7.1/avrotize/thrifttoavro.py +412 -0
  38. {structurize-3.6.1 → structurize-3.7.1/structurize.egg-info}/PKG-INFO +1 -1
  39. {structurize-3.6.1 → structurize-3.7.1}/structurize.egg-info/SOURCES.txt +56 -0
  40. {structurize-3.6.1 → structurize-3.7.1}/.gitignore +0 -0
  41. {structurize-3.6.1 → structurize-3.7.1}/LICENSE +0 -0
  42. {structurize-3.6.1 → structurize-3.7.1}/MANIFEST.in +0 -0
  43. {structurize-3.6.1 → structurize-3.7.1}/README.md +0 -0
  44. {structurize-3.6.1 → structurize-3.7.1}/avrotize/__main__.py +0 -0
  45. {structurize-3.6.1 → structurize-3.7.1}/avrotize/asn1toavro.py +0 -0
  46. {structurize-3.6.1 → structurize-3.7.1}/avrotize/avrotize.py +0 -0
  47. {structurize-3.6.1 → structurize-3.7.1}/avrotize/avrotocpp.py +0 -0
  48. {structurize-3.6.1 → structurize-3.7.1}/avrotize/avrotocsv.py +0 -0
  49. {structurize-3.6.1 → structurize-3.7.1}/avrotize/avrotodatapackage.py +0 -0
  50. {structurize-3.6.1 → structurize-3.7.1}/avrotize/avrotodb.py +0 -0
  51. {structurize-3.6.1 → structurize-3.7.1}/avrotize/avrotogo.py +0 -0
  52. {structurize-3.6.1 → structurize-3.7.1}/avrotize/avrotographql.py +0 -0
  53. {structurize-3.6.1 → structurize-3.7.1}/avrotize/avrotoiceberg.py +0 -0
  54. {structurize-3.6.1 → structurize-3.7.1}/avrotize/avrotojava.py +0 -0
  55. {structurize-3.6.1 → structurize-3.7.1}/avrotize/avrotojs.py +0 -0
  56. {structurize-3.6.1 → structurize-3.7.1}/avrotize/avrotojsons.py +0 -0
  57. {structurize-3.6.1 → structurize-3.7.1}/avrotize/avrotokusto.py +0 -0
  58. {structurize-3.6.1 → structurize-3.7.1}/avrotize/avrotomd.py +0 -0
  59. {structurize-3.6.1 → structurize-3.7.1}/avrotize/avrotools.py +0 -0
  60. {structurize-3.6.1 → structurize-3.7.1}/avrotize/avrotoparquet.py +0 -0
  61. {structurize-3.6.1 → structurize-3.7.1}/avrotize/avrotoproto.py +0 -0
  62. {structurize-3.6.1 → structurize-3.7.1}/avrotize/avrotopython.py +0 -0
  63. {structurize-3.6.1 → structurize-3.7.1}/avrotize/avrotorust.py +0 -0
  64. {structurize-3.6.1 → structurize-3.7.1}/avrotize/avrotots.py +0 -0
  65. {structurize-3.6.1 → structurize-3.7.1}/avrotize/avrototsml.py +0 -0
  66. {structurize-3.6.1 → structurize-3.7.1}/avrotize/avrotoxsd.py +0 -0
  67. {structurize-3.6.1 → structurize-3.7.1}/avrotize/avrovalidator.py +0 -0
  68. {structurize-3.6.1 → structurize-3.7.1}/avrotize/cddltostructure.py +0 -0
  69. {structurize-3.6.1 → structurize-3.7.1}/avrotize/choice_inference.py +0 -0
  70. {structurize-3.6.1 → structurize-3.7.1}/avrotize/constants.py +0 -0
  71. {structurize-3.6.1 → structurize-3.7.1}/avrotize/csvtoavro.py +0 -0
  72. {structurize-3.6.1 → structurize-3.7.1}/avrotize/datapackagetoavro.py +0 -0
  73. {structurize-3.6.1 → structurize-3.7.1}/avrotize/dependencies/cpp/vcpkg/vcpkg.json +0 -0
  74. {structurize-3.6.1 → structurize-3.7.1}/avrotize/dependencies/typescript/node22/package.json +0 -0
  75. {structurize-3.6.1 → structurize-3.7.1}/avrotize/dependency_version.py +0 -0
  76. {structurize-3.6.1 → structurize-3.7.1}/avrotize/jsonstoavro.py +0 -0
  77. {structurize-3.6.1 → structurize-3.7.1}/avrotize/jsonstostructure.py +0 -0
  78. {structurize-3.6.1 → structurize-3.7.1}/avrotize/jsontoschema.py +0 -0
  79. {structurize-3.6.1 → structurize-3.7.1}/avrotize/jstructtoavro.py +0 -0
  80. {structurize-3.6.1 → structurize-3.7.1}/avrotize/kstructtoavro.py +0 -0
  81. {structurize-3.6.1 → structurize-3.7.1}/avrotize/kustotoavro.py +0 -0
  82. {structurize-3.6.1 → structurize-3.7.1}/avrotize/kustotojstruct.py +0 -0
  83. {structurize-3.6.1 → structurize-3.7.1}/avrotize/mcp_server.py +0 -0
  84. {structurize-3.6.1 → structurize-3.7.1}/avrotize/openapitostructure.py +0 -0
  85. {structurize-3.6.1 → structurize-3.7.1}/avrotize/parquettoavro.py +0 -0
  86. {structurize-3.6.1 → structurize-3.7.1}/avrotize/proto2parser.py +0 -0
  87. {structurize-3.6.1 → structurize-3.7.1}/avrotize/proto3parser.py +0 -0
  88. {structurize-3.6.1 → structurize-3.7.1}/avrotize/prototoavro.py +0 -0
  89. {structurize-3.6.1 → structurize-3.7.1}/avrotize/schema_inference.py +0 -0
  90. {structurize-3.6.1 → structurize-3.7.1}/avrotize/sqltoavro.py +0 -0
  91. {structurize-3.6.1 → structurize-3.7.1}/avrotize/structuretocddl.py +0 -0
  92. {structurize-3.6.1 → structurize-3.7.1}/avrotize/structuretocpp.py +0 -0
  93. {structurize-3.6.1 → structurize-3.7.1}/avrotize/structuretocsv.py +0 -0
  94. {structurize-3.6.1 → structurize-3.7.1}/avrotize/structuretodatapackage.py +0 -0
  95. {structurize-3.6.1 → structurize-3.7.1}/avrotize/structuretodb.py +0 -0
  96. {structurize-3.6.1 → structurize-3.7.1}/avrotize/structuretogo.py +0 -0
  97. {structurize-3.6.1 → structurize-3.7.1}/avrotize/structuretographql.py +0 -0
  98. {structurize-3.6.1 → structurize-3.7.1}/avrotize/structuretoiceberg.py +0 -0
  99. {structurize-3.6.1 → structurize-3.7.1}/avrotize/structuretojava.py +0 -0
  100. {structurize-3.6.1 → structurize-3.7.1}/avrotize/structuretojs.py +0 -0
  101. {structurize-3.6.1 → structurize-3.7.1}/avrotize/structuretojsons.py +0 -0
  102. {structurize-3.6.1 → structurize-3.7.1}/avrotize/structuretokusto.py +0 -0
  103. {structurize-3.6.1 → structurize-3.7.1}/avrotize/structuretomd.py +0 -0
  104. {structurize-3.6.1 → structurize-3.7.1}/avrotize/structuretoproto.py +0 -0
  105. {structurize-3.6.1 → structurize-3.7.1}/avrotize/structuretopython.py +0 -0
  106. {structurize-3.6.1 → structurize-3.7.1}/avrotize/structuretorust.py +0 -0
  107. {structurize-3.6.1 → structurize-3.7.1}/avrotize/structuretots.py +0 -0
  108. {structurize-3.6.1 → structurize-3.7.1}/avrotize/structuretotsml.py +0 -0
  109. {structurize-3.6.1 → structurize-3.7.1}/avrotize/structuretoxsd.py +0 -0
  110. {structurize-3.6.1 → structurize-3.7.1}/avrotize/tmslvalidate.py +0 -0
  111. {structurize-3.6.1 → structurize-3.7.1}/avrotize/validate.py +0 -0
  112. {structurize-3.6.1 → structurize-3.7.1}/avrotize/xmltoschema.py +0 -0
  113. {structurize-3.6.1 → structurize-3.7.1}/avrotize/xsdtoavro.py +0 -0
  114. {structurize-3.6.1 → structurize-3.7.1}/build.ps1 +0 -0
  115. {structurize-3.6.1 → structurize-3.7.1}/build.sh +0 -0
  116. {structurize-3.6.1 → structurize-3.7.1}/pyproject.toml +0 -0
  117. {structurize-3.6.1 → structurize-3.7.1}/setup.cfg +0 -0
  118. {structurize-3.6.1 → structurize-3.7.1}/structurize.egg-info/dependency_links.txt +0 -0
  119. {structurize-3.6.1 → structurize-3.7.1}/structurize.egg-info/entry_points.txt +0 -0
  120. {structurize-3.6.1 → structurize-3.7.1}/structurize.egg-info/requires.txt +0 -0
  121. {structurize-3.6.1 → structurize-3.7.1}/structurize.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: structurize
3
- Version: 3.6.1
3
+ Version: 3.7.1
4
4
  Summary: Tools to convert from and to JSON Structure from various other schema languages.
5
5
  Author-email: Clemens Vasters <clemensv@microsoft.com>
6
6
  Classifier: Programming Language :: Python :: 3
@@ -25,6 +25,28 @@ class LazyLoader:
25
25
  # Define the functions and their corresponding module paths
26
26
  _mappings = {
27
27
  "convert_proto_to_avro": (f"{mod}.prototoavro", "convert_proto_to_avro"),
28
+ "convert_jtd_to_avro": (f"{mod}.jtdtoavro", "convert_jtd_to_avro"),
29
+ "convert_avro_to_jtd": (f"{mod}.avrotojtd", "convert_avro_to_jtd"),
30
+ "convert_jtd_to_structure": (f"{mod}.jtdtostructure", "convert_jtd_to_structure"),
31
+ "convert_structure_to_jtd": (f"{mod}.structuretojtd", "convert_structure_to_jtd"),
32
+ "convert_surrealql_to_avro": (f"{mod}.surrealtoavro", "convert_surrealql_to_avro"),
33
+ "convert_avro_to_surrealql": (f"{mod}.avrotosurreal", "convert_avro_to_surrealql"),
34
+ "convert_flatbuffers_to_avro": (f"{mod}.flatbufferstoavro", "convert_flatbuffers_to_avro"),
35
+ "convert_avro_to_flatbuffers": (f"{mod}.avrotoflatbuffers", "convert_avro_to_flatbuffers"),
36
+ "convert_flatbuffers_to_json_structure": (f"{mod}.flatbufferstojstruct", "convert_flatbuffers_to_json_structure"),
37
+ "convert_json_structure_to_flatbuffers": (f"{mod}.jstructtoflatbuffers", "convert_json_structure_to_flatbuffers"),
38
+ "convert_thrift_to_avro": (f"{mod}.thrifttoavro", "convert_thrift_to_avro"),
39
+ "convert_avro_to_thrift": (f"{mod}.avrotothrift", "convert_avro_to_thrift"),
40
+ "convert_thrift_to_structure": (f"{mod}.thriftstructure", "convert_thrift_to_structure"),
41
+ "convert_structure_to_thrift": (f"{mod}.thriftstructure", "convert_structure_to_thrift"),
42
+ "convert_smithy_to_avro": (f"{mod}.smithytoavro", "convert_smithy_to_avro"),
43
+ "convert_avro_to_smithy": (f"{mod}.avrotosmithy", "convert_avro_to_smithy"),
44
+ "convert_smithy_to_json_structure": (f"{mod}.smithytojstruct", "convert_smithy_to_json_structure"),
45
+ "convert_json_structure_to_smithy": (f"{mod}.jstructtosmithy", "convert_json_structure_to_smithy"),
46
+ "convert_raml_to_avro": (f"{mod}.ramltoavro", "convert_raml_to_avro"),
47
+ "convert_avro_to_raml": (f"{mod}.avrotoraml", "convert_avro_to_raml"),
48
+ "convert_raml_to_json_structure": (f"{mod}.ramltojstruct", "convert_raml_to_json_structure"),
49
+ "convert_json_structure_to_raml": (f"{mod}.jstructtoraml", "convert_json_structure_to_raml"),
28
50
  "convert_jsons_to_avro": (f"{mod}.jsonstoavro", "convert_jsons_to_avro"),
29
51
  "convert_kafka_struct_to_avro_schema": (f"{mod}.kstructtoavro", "convert_kafka_struct_to_avro_schema"),
30
52
  "convert_kusto_to_avro": (f"{mod}.kustotoavro", "convert_kusto_to_avro"),
@@ -35,6 +57,14 @@ _mappings = {
35
57
  "convert_avro_to_kusto_db": (f"{mod}.avrotokusto", "convert_avro_to_kusto_db"),
36
58
  "convert_avro_to_parquet": (f"{mod}.avrotoparquet", "convert_avro_to_parquet"),
37
59
  "convert_avro_to_proto": (f"{mod}.avrotoproto", "convert_avro_to_proto"),
60
+ "convert_cue_to_avro": (f"{mod}.cuetoavro", "convert_cue_to_avro"),
61
+ "convert_avro_to_cue": (f"{mod}.avrotocue", "convert_avro_to_cue"),
62
+ "convert_cue_to_json_structure": (f"{mod}.cuetostructure", "convert_cue_to_json_structure"),
63
+ "convert_json_structure_to_cue": (f"{mod}.structuretocue", "convert_json_structure_to_cue"),
64
+ "convert_capnproto_to_avro": (f"{mod}.capnprototoavro", "convert_capnproto_to_avro"),
65
+ "convert_avro_to_capnproto": (f"{mod}.avrotocapnproto", "convert_avro_to_capnproto"),
66
+ "convert_capnproto_to_json_structure": (f"{mod}.capnprototoavro", "convert_capnproto_to_json_structure"),
67
+ "convert_json_structure_to_capnproto": (f"{mod}.avrotocapnproto", "convert_json_structure_to_capnproto"),
38
68
  "convert_avro_to_sql": (f"{mod}.avrotodb", "convert_avro_to_sql"),
39
69
  "convert_avro_to_xsd": (f"{mod}.avrotoxsd", "convert_avro_to_xsd"),
40
70
  "convert_avro_to_java": (f"{mod}.avrotojava", "convert_avro_to_java"),
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '3.6.1'
22
- __version_tuple__ = version_tuple = (3, 6, 1)
21
+ __version__ = version = '3.7.1'
22
+ __version_tuple__ = version_tuple = (3, 7, 1)
23
23
 
24
- __commit_id__ = commit_id = 'ga0404973f'
24
+ __commit_id__ = commit_id = 'g2f9460efb'
@@ -0,0 +1,207 @@
1
+ """Convert Avrotize/Avro schemas to Cap'n Proto .capnp schemas."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import os
7
+ import re
8
+ import tempfile
9
+ from typing import Any
10
+
11
+ from avrotize.common import unique_name
12
+
13
+ INDENT = " "
14
+ FILE_ID = "0xd12f8a7e9b0c4a6f"
15
+
16
+
17
+ def _safe_ident(name: str) -> str:
18
+ safe = re.sub(r"[^A-Za-z0-9_]", "_", name)
19
+ if not safe or safe[0].isdigit():
20
+ safe = f"_{safe}"
21
+ return safe
22
+
23
+
24
+ def _type_name(schema: dict[str, Any]) -> str:
25
+ namespace = schema.get("namespace")
26
+ name = schema["name"]
27
+ return f"{namespace}.{name}" if namespace else name
28
+
29
+
30
+ class AvroToCapnpConverter:
31
+ primitive_map = {
32
+ "null": "Void",
33
+ "boolean": "Bool",
34
+ "int": "Int32",
35
+ "long": "Int64",
36
+ "float": "Float32",
37
+ "double": "Float64",
38
+ "bytes": "Data",
39
+ "string": "Text",
40
+ }
41
+
42
+ def __init__(self, namespace: str | None = None) -> None:
43
+ self.namespace = namespace
44
+ self.named: dict[str, dict[str, Any]] = {}
45
+ self.emitted: set[str] = set()
46
+ self.type_names: dict[int, str] = {}
47
+ self.top_level_type_names: set[str] = set()
48
+
49
+ def convert(self, avro_schema: Any) -> str:
50
+ schemas = avro_schema if isinstance(avro_schema, list) else [avro_schema]
51
+ for schema in schemas:
52
+ if isinstance(schema, dict) and schema.get("type") in {"record", "enum"}:
53
+ self.named[schema["name"]] = schema
54
+ self.named[_type_name(schema)] = schema
55
+ self._assign_type_name(schema, self.top_level_type_names)
56
+ lines = [f"@{FILE_ID};", ""]
57
+ if self.namespace:
58
+ lines.append(f"# namespace: {self.namespace}")
59
+ lines.append("# Generated by avrotize. File id is stable for deterministic output; replace if needed.")
60
+ lines.append("")
61
+ for schema in schemas:
62
+ if isinstance(schema, dict) and schema.get("type") in {"enum", "record"}:
63
+ name = _type_name(schema)
64
+ if name not in self.emitted:
65
+ lines.extend(self._render_named(schema, 0, self.top_level_type_names))
66
+ lines.append("")
67
+ return "\n".join(lines).rstrip() + "\n"
68
+
69
+ def _assign_type_name(self, schema: dict[str, Any], used: set[str] | None = None) -> str:
70
+ key = id(schema)
71
+ if key not in self.type_names:
72
+ self.type_names[key] = unique_name(_safe_ident(schema["name"]), used if used is not None else self.top_level_type_names)
73
+ return self.type_names[key]
74
+
75
+ def _render_named(self, schema: dict[str, Any], level: int, used_type_names: set[str] | None = None) -> list[str]:
76
+ if schema.get("type") == "enum":
77
+ return self._render_enum(schema, level, used_type_names)
78
+ if schema.get("type") == "record":
79
+ return self._render_struct(schema, level, used_type_names)
80
+ return []
81
+
82
+ def _render_enum(self, schema: dict[str, Any], level: int, used_type_names: set[str] | None = None) -> list[str]:
83
+ self.emitted.add(_type_name(schema))
84
+ lines = [f"{INDENT * level}enum {self._assign_type_name(schema, used_type_names)} {{"]
85
+ used_symbols: set[str] = set()
86
+ for index, symbol in enumerate(schema.get("symbols", [])):
87
+ lines.append(f"{INDENT * (level + 1)}{unique_name(_safe_ident(symbol), used_symbols)} @{index};")
88
+ lines.append(f"{INDENT * level}}}")
89
+ return lines
90
+
91
+ def _render_struct(self, schema: dict[str, Any], level: int, used_type_names: set[str] | None = None) -> list[str]:
92
+ self.emitted.add(_type_name(schema))
93
+ lines = [f"{INDENT * level}struct {self._assign_type_name(schema, used_type_names)} {{"]
94
+ fields = schema.get("fields", [])
95
+ grouped: dict[str, list[tuple[int, dict[str, Any], Any, str]]] = {}
96
+ normal: list[tuple[int, dict[str, Any], Any, str]] = []
97
+ used_fields: set[str] = set()
98
+ for index, field in enumerate(fields):
99
+ avro_type = field.get("type")
100
+ union_name = field.get("capnpUnion")
101
+ safe_field_name = unique_name(_safe_ident(field.get("name", f"field{index}")), used_fields)
102
+ item = (index, field, avro_type, safe_field_name)
103
+ if union_name:
104
+ grouped.setdefault(str(union_name), []).append(item)
105
+ else:
106
+ normal.append(item)
107
+ nested_type_names: set[str] = set()
108
+ for index, field, avro_type, safe_field_name in normal:
109
+ lines.extend(self._render_field(field, avro_type, index, level + 1, safe_field_name, nested_type_names))
110
+ for _, items in grouped.items():
111
+ lines.append(f"{INDENT * (level + 1)}union {{")
112
+ for index, field, avro_type, safe_field_name in items:
113
+ lines.extend(self._render_field(field, avro_type, index, level + 2, safe_field_name, nested_type_names))
114
+ lines.append(f"{INDENT * (level + 1)}}}")
115
+ lines.append(f"{INDENT * level}}}")
116
+ return lines
117
+
118
+ def _render_field(
119
+ self,
120
+ field: dict[str, Any],
121
+ avro_type: Any,
122
+ index: int,
123
+ level: int,
124
+ safe_field_name: str,
125
+ used_type_names: set[str],
126
+ ) -> list[str]:
127
+ capnp_type, nested = self._capnp_type(avro_type, safe_field_name, used_type_names)
128
+ ordinal = index
129
+ lines: list[str] = []
130
+ lines.extend(self._render_inline_nested(nested, level, used_type_names))
131
+ lines.append(f"{INDENT * level}{safe_field_name} @{ordinal} :{capnp_type};")
132
+ return lines
133
+
134
+ def _render_inline_nested(self, nested: list[dict[str, Any]], level: int, used_type_names: set[str]) -> list[str]:
135
+ lines: list[str] = []
136
+ for schema in nested:
137
+ lines.extend(self._render_named(schema, level, used_type_names))
138
+ return lines
139
+
140
+ def _non_null_union(self, avro_type: list[Any]) -> list[Any]:
141
+ return [item for item in avro_type if item != "null" and not (isinstance(item, dict) and item.get("type") == "null")]
142
+
143
+ def _capnp_type(self, avro_type: Any, field_name: str, used_type_names: set[str] | None = None) -> tuple[str, list[dict[str, Any]]]:
144
+ used_type_names = used_type_names if used_type_names is not None else self.top_level_type_names
145
+ if isinstance(avro_type, list):
146
+ non_null = self._non_null_union(avro_type)
147
+ if len(non_null) == 1:
148
+ return self._capnp_type(non_null[0], field_name, used_type_names)
149
+ union_schema = {
150
+ "type": "record",
151
+ "name": f"{field_name[:1].upper()}{field_name[1:]}Choice",
152
+ "fields": [{"name": f"choice{i}", "type": t, "capnpUnion": "union"} for i, t in enumerate(non_null)],
153
+ }
154
+ return self._assign_type_name(union_schema, used_type_names), [union_schema]
155
+ if isinstance(avro_type, dict):
156
+ avro_kind = avro_type.get("type")
157
+ if isinstance(avro_kind, list):
158
+ return self._capnp_type(avro_kind, field_name, used_type_names)
159
+ if avro_kind == "array":
160
+ item_type, nested = self._capnp_type(avro_type.get("items", "string"), f"{field_name}Item", used_type_names)
161
+ return f"List({item_type})", nested
162
+ if avro_kind == "map":
163
+ entry = {
164
+ "type": "record",
165
+ "name": f"{field_name[:1].upper()}{field_name[1:]}Entry",
166
+ "fields": [
167
+ {"name": "key", "type": "string"},
168
+ {"name": "value", "type": avro_type.get("values", "string")},
169
+ ],
170
+ }
171
+ return f"List({self._assign_type_name(entry, used_type_names)})", [entry]
172
+ if avro_kind == "record":
173
+ return self._assign_type_name(avro_type, used_type_names), [avro_type]
174
+ if avro_kind == "enum":
175
+ return self._assign_type_name(avro_type, used_type_names), [avro_type]
176
+ if avro_kind == "fixed":
177
+ return "Data", []
178
+ return self._capnp_type(avro_kind, field_name, used_type_names)
179
+ if isinstance(avro_type, str):
180
+ named = self.named.get(avro_type)
181
+ if named:
182
+ return self._assign_type_name(named), []
183
+ return self.primitive_map.get(avro_type, _safe_ident(avro_type.split(".")[-1])), []
184
+ return "Text", []
185
+
186
+
187
+ def convert_avro_to_capnproto(avro_schema_path: str, capnp_file_path: str, namespace: str | None = None) -> None:
188
+ """Convert an Avrotize/Avro schema file to a Cap'n Proto .capnp schema file."""
189
+ with open(avro_schema_path, "r", encoding="utf-8") as avro_file:
190
+ avro_schema = json.load(avro_file)
191
+ converter = AvroToCapnpConverter(namespace=namespace)
192
+ capnp_schema = converter.convert(avro_schema)
193
+ os.makedirs(os.path.dirname(os.path.abspath(capnp_file_path)) or ".", exist_ok=True)
194
+ with open(capnp_file_path, "w", encoding="utf-8") as capnp_file:
195
+ capnp_file.write(capnp_schema)
196
+
197
+
198
+ def convert_json_structure_to_capnproto(structure_file: str, capnp_file_path: str, namespace: str | None = None) -> None:
199
+ """Convert JSON Structure to Cap'n Proto by bridging through Avrotize/Avro."""
200
+ from avrotize.jstructtoavro import convert_json_structure_to_avro
201
+
202
+ temp_parent = os.path.dirname(os.path.abspath(capnp_file_path)) or os.getcwd()
203
+ os.makedirs(temp_parent, exist_ok=True)
204
+ with tempfile.TemporaryDirectory(prefix="avrotize-capnp-", dir=temp_parent) as temp_dir:
205
+ avro_file = os.path.join(temp_dir, "schema.avsc")
206
+ convert_json_structure_to_avro(structure_file, avro_file)
207
+ convert_avro_to_capnproto(avro_file, capnp_file_path, namespace=namespace)