forked from datacontract/datacontract-cli
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add ability to export to go types (datacontract#195)
* Add ability to export to go types * add test * rename to types * updated naming * update docs
- Loading branch information
Mark Olliver
authored
May 15, 2024
1 parent
15e3e40
commit 2e87d90
Showing
6 changed files
with
147 additions
and
18 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
import datacontract.model.data_contract_specification as spec | ||
from typing import List | ||
import re | ||
|
||
|
||
def to_go_types(contract: spec.DataContractSpecification) -> str: | ||
result = "package main\n\n" | ||
|
||
for key in contract.models.keys(): | ||
go_types = generate_go_type(contract.models[key], key) | ||
for go_type in go_types: | ||
# print(go_type + "\n\n") | ||
result += f"\n{go_type}\n" | ||
|
||
return result | ||
|
||
|
||
def python_type_to_go_type(py_type) -> str: | ||
match py_type: | ||
case "text": | ||
return "string" | ||
case "timestamp": | ||
return "time.Time" | ||
case "long": | ||
return "int64" | ||
case "int": | ||
return "int" | ||
case "float": | ||
return "float64" | ||
case "boolean": | ||
return "bool" | ||
case _: | ||
return "interface{}" | ||
|
||
|
||
def to_camel_case(snake_str) -> str: | ||
return "".join(word.capitalize() for word in re.split(r"_|(?<!^)(?=[A-Z])", snake_str)) | ||
|
||
|
||
def get_subtype(field_info, nested_types, type_name, camel_case_name) -> str: | ||
go_type = "interface{}" | ||
if field_info.fields: | ||
nested_type_name = to_camel_case(f"{type_name}_{camel_case_name}") | ||
nested_types[nested_type_name] = field_info.fields | ||
go_type = nested_type_name | ||
|
||
match field_info.type: | ||
case "array": | ||
if field_info.items: | ||
item_type = get_subtype(field_info.items, nested_types, type_name, camel_case_name + "Item") | ||
go_type = f"[]{item_type}" | ||
else: | ||
go_type = "[]interface{}" | ||
case "record": | ||
if field_info.fields: | ||
nested_type_name = to_camel_case(f"{type_name}_{camel_case_name}") | ||
nested_types[nested_type_name] = field_info.fields | ||
go_type = nested_type_name | ||
else: | ||
go_type = "interface{}" | ||
case "object": | ||
pass | ||
case _: | ||
go_type = field_info.type | ||
|
||
return go_type | ||
|
||
|
||
def generate_go_type(model, model_name) -> List[str]: | ||
go_types = [] | ||
type_name = to_camel_case(model_name) | ||
lines = [f"type {type_name} struct {{"] | ||
|
||
nested_types = {} | ||
|
||
for field_name, field_info in model.fields.items(): | ||
go_type = python_type_to_go_type(field_info.type) | ||
camel_case_name = to_camel_case(field_name) | ||
json_tag = field_name if field_info.required else f"{field_name},omitempty" | ||
avro_tag = field_name | ||
|
||
if go_type == "interface{}": | ||
go_type = get_subtype(field_info, nested_types, type_name, camel_case_name) | ||
|
||
go_type = go_type if field_info.required else f"*{go_type}" | ||
|
||
lines.append( | ||
f' {camel_case_name} {go_type} `json:"{json_tag}" avro:"{avro_tag}"` // {field_info.description}' | ||
) | ||
lines.append("}") | ||
go_types.append("\n".join(lines)) | ||
|
||
for nested_type_name, nested_fields in nested_types.items(): | ||
nested_model = spec.Model(fields=nested_fields) | ||
nested_go_types = generate_go_type(nested_model, nested_type_name) | ||
go_types.extend(nested_go_types) | ||
|
||
return go_types |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import logging | ||
|
||
from typer.testing import CliRunner | ||
|
||
from datacontract.cli import app | ||
from datacontract.data_contract import DataContract | ||
|
||
logging.basicConfig(level=logging.DEBUG, force=True) | ||
|
||
|
||
def test_cli(): | ||
runner = CliRunner() | ||
result = runner.invoke(app, ["export", "./fixtures/export/datacontract.yaml", "--format", "go"]) | ||
assert result.exit_code == 0 | ||
|
||
|
||
def test_to_go_types(): | ||
actual = DataContract(data_contract_file="fixtures/export/datacontract.yaml").export("go") | ||
expected = """ | ||
package main | ||
type Orders struct { | ||
OrderId varchar `json:"order_id" avro:"order_id"` // None | ||
OrderTotal bigint `json:"order_total" avro:"order_total"` // The order_total field | ||
OrderStatus string `json:"order_status" avro:"order_status"` // None | ||
} | ||
""" | ||
assert actual.strip() == expected.strip() |