Skip to content

Commit 0b3ea7d

Browse files
authored
Implement Code Generation (#1)
1 parent a1d6ce0 commit 0b3ea7d

File tree

1,187 files changed

+5847
-109266
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

1,187 files changed

+5847
-109266
lines changed

.generator/schemas/v1/openapi.yaml

+33-32,118
Large diffs are not rendered by default.

.generator/schemas/v2/openapi.yaml

+472-27,480
Large diffs are not rendered by default.

.generator/src/generator/cli.py

+39-21
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from . import formatter
88
from . import utils
99

10-
MODULE = "github.com/DataDog/datadog-api-client-go/v2"
10+
MODULE = "datadog-api-client-rust"
1111
COMMON_PACKAGE_NAME = "datadog"
1212

1313

@@ -24,7 +24,7 @@
2424
)
2525
def cli(specs, output):
2626
"""
27-
Generate a Go code snippet from OpenAPI specification.
27+
Generate a Rust code snippet from OpenAPI specification.
2828
"""
2929
env = Environment(loader=FileSystemLoader(str(pathlib.Path(__file__).parent / "templates")))
3030

@@ -41,7 +41,6 @@ def cli(specs, output):
4141
env.filters["parameters"] = openapi.parameters
4242
env.filters["form_parameter"] = openapi.form_parameter
4343
env.filters["response_type"] = openapi.get_type_for_response
44-
env.filters["responses_by_types"] = openapi.responses_by_types
4544
env.filters["return_type"] = openapi.return_type
4645
env.filters["simple_type"] = formatter.simple_type
4746
env.filters["snake_case"] = formatter.snake_case
@@ -50,10 +49,12 @@ def cli(specs, output):
5049
env.filters["variable_name"] = formatter.variable_name
5150

5251
env.globals["enumerate"] = enumerate
52+
env.globals["responses_by_types"] = openapi.responses_by_types
5353
env.globals["get_name"] = openapi.get_name
5454
env.globals["get_type_for_attribute"] = openapi.get_type_for_attribute
55+
env.globals["get_type_for_response"] = openapi.get_type_for_response
5556
env.globals["get_type_for_parameter"] = openapi.get_type_for_parameter
56-
env.globals["get_type"] = openapi.type_to_go
57+
env.globals["get_type"] = openapi.type_to_rust
5758
env.globals["get_default"] = openapi.get_default
5859
env.globals["get_container"] = openapi.get_container
5960
env.globals["get_container_type"] = openapi.get_container_type
@@ -63,17 +64,18 @@ def cli(specs, output):
6364

6465
api_j2 = env.get_template("api.j2")
6566
model_j2 = env.get_template("model.j2")
66-
doc_j2 = env.get_template("doc.j2")
67+
mod_j2 = env.get_template("mod.j2")
68+
mod_api_j2 = env.get_template("mod_api.j2")
69+
mod_model_j2 = env.get_template("mod_model.j2")
70+
# doc_j2 = env.get_template("doc.j2")
6771

68-
extra_files = {
72+
common_files = {
6973
"configuration.rs": env.get_template("configuration.j2"),
70-
"mod.rs": env.get_template("mod.j2"),
71-
"lib.rs": env.get_template("lib.j2"),
74+
"mod.rs": env.get_template("common_mod.j2"),
7275
}
76+
librs = env.get_template("lib.j2")
7377

74-
# test_scenarios_files = {
75-
# "api_mappings.rs": env.get_template("scenarios_api_mappings.j2"),
76-
# }
78+
test_fixtures = env.get_template("function_mappings.j2")
7779

7880
output.mkdir(parents=True, exist_ok=True)
7981

@@ -97,34 +99,50 @@ def cli(specs, output):
9799

98100
for name, model in models.items():
99101
filename = "model_" + formatter.model_filename(name) + ".rs"
100-
model_path = resources_dir / filename
102+
model_path = resources_dir / "model" / filename
101103
model_path.parent.mkdir(parents=True, exist_ok=True)
102104
with model_path.open("w") as fp:
103105
fp.write(model_j2.render(name=name, model=model, models=models))
104106

107+
mod_path = resources_dir / "model" / "mod.rs"
108+
with mod_path.open("w") as fp:
109+
fp.write(mod_model_j2.render(apis=apis, models=models))
110+
105111
all_operations = []
106112

107113
for name, operations in apis.items():
108114
filename = "api_" + formatter.snake_case(name) + ".rs"
109-
api_path = resources_dir / filename
115+
api_path = resources_dir / "api" / filename
110116
api_path.parent.mkdir(parents=True, exist_ok=True)
111117
with api_path.open("w") as fp:
112118
fp.write(api_j2.render(name=name, operations=operations))
113119
all_operations.append((name, operations))
114120

121+
mod_path = resources_dir / "api" / "mod.rs"
122+
with mod_path.open("w") as fp:
123+
fp.write(mod_api_j2.render(apis=apis, models=models))
124+
125+
mod_path = resources_dir / "mod.rs"
126+
with mod_path.open("w") as fp:
127+
fp.write(mod_j2.render(apis=apis, models=models))
128+
129+
115130
# doc_path = resources_dir / "doc.rs"
116131
# with doc_path.open("w") as fp:
117132
# fp.write(doc_j2.render(all_operations=all_operations))
118133

119-
common_package_output = pathlib.Path(f"../api/{COMMON_PACKAGE_NAME}")
134+
common_package_output = pathlib.Path(f"../src/{COMMON_PACKAGE_NAME}")
120135
common_package_output.mkdir(parents=True, exist_ok=True)
121-
for name, template in extra_files.items():
136+
for name, template in common_files.items():
122137
filename = common_package_output / name
123138
with filename.open("w") as fp:
124139
fp.write(template.render(apis=all_apis, all_specs=all_specs))
125-
126-
# scenarios_test_output = pathlib.Path("../tests/scenarios/")
127-
# for name, template in test_scenarios_files.items():
128-
# filename = scenarios_test_output / name
129-
# with filename.open("w") as fp:
130-
# fp.write(template.render(apis=all_apis, all_specs=all_specs))
140+
141+
lib_name = output / "lib.rs"
142+
with lib_name.open("w") as fp:
143+
fp.write(librs.render())
144+
145+
test_fixture_output = pathlib.Path("../tests/scenarios/")
146+
filename = test_fixture_output / "function_mappings.rs"
147+
with filename.open("w") as fp:
148+
fp.write(test_fixtures.render(all_apis=all_apis, all_specs=all_specs))

.generator/src/generator/formatter.py

+77-80
Original file line numberDiff line numberDiff line change
@@ -9,63 +9,57 @@
99
PRIMITIVE_TYPES = ["string", "number", "boolean", "integer"]
1010

1111
KEYWORDS = {
12+
"as",
1213
"break",
13-
"case",
14-
"chan",
1514
"const",
1615
"continue",
17-
"default",
18-
"defer",
16+
"crate",
1917
"else",
20-
"fallthrough",
18+
"enum",
19+
"extern",
20+
"false",
21+
"fn",
2122
"for",
22-
"func",
23-
"go",
24-
"goto",
2523
"if",
26-
"import",
27-
"interface",
28-
"map",
29-
"package",
30-
"range",
24+
"impl",
25+
"in",
26+
"let",
27+
"loop",
28+
"match",
29+
"mod",
30+
"move",
31+
"mut",
32+
"pub",
33+
"ref",
3134
"return",
32-
"select",
35+
"self",
36+
"Self",
37+
"static",
3338
"struct",
34-
"switch",
39+
"super",
40+
"trait",
41+
"true",
3542
"type",
36-
"var",
37-
}
38-
39-
SUFFIXES = {
40-
# Test
41-
"test",
42-
# $GOOS
43-
"aix",
44-
"android",
45-
"darwin",
46-
"dragonfly",
47-
"freebsd",
48-
"illumos",
49-
"js",
50-
"linux",
51-
"netbsd",
52-
"openbsd",
53-
"plan9",
54-
"solaris",
55-
"windows",
56-
# $GOARCH
57-
"386",
58-
"amd64",
59-
"arm",
60-
"arm64",
61-
"mips",
62-
"mips64",
63-
"mips64le",
64-
"mipsle",
65-
"ppc64",
66-
"ppc64le",
67-
"s390x",
68-
"wasm",
43+
"unsafe",
44+
"use",
45+
"where",
46+
"while",
47+
"async",
48+
"await",
49+
"dyn",
50+
"abstract",
51+
"become",
52+
"box",
53+
"do",
54+
"final",
55+
"macro",
56+
"override",
57+
"priv",
58+
"typeof",
59+
"unsized",
60+
"virtual",
61+
"yield",
62+
"try",
6963
}
7064

7165

@@ -74,18 +68,14 @@ def is_primitive(schema):
7468
return _type in PRIMITIVE_TYPES and "enum" not in schema
7569

7670

77-
def block_comment(comment, prefix="#", first_line=True):
71+
def block_comment(comment, prefix="///", first_line=True):
7872
lines = comment.split("\n")
7973
start = "" if first_line else lines[0] + "\n"
8074
return (start + "\n".join(f"{prefix} {line}".rstrip() for line in lines[(0 if first_line else 1) :])).rstrip()
8175

8276

8377
def model_filename(name):
84-
filename = snake_case(name)
85-
last = filename.split("_")[-1]
86-
if last in SUFFIXES:
87-
filename += "_"
88-
return filename
78+
return snake_case(name)
8979

9080

9181
def escape_reserved_keyword(word):
@@ -95,7 +85,7 @@ def escape_reserved_keyword(word):
9585
:return: The escaped word if it was a reserved keyword, the word unchanged otherwise
9686
"""
9787
if word in KEYWORDS:
98-
return f"{word}Var"
88+
return f"{word}_"
9989
return word
10090

10191

@@ -104,15 +94,15 @@ def attribute_name(attribute):
10494

10595

10696
def variable_name(attribute):
107-
return escape_reserved_keyword(untitle_case(camel_case(attribute)))
97+
return escape_reserved_keyword(snake_case(attribute))
10898

10999

110-
def format_value(value, quotes='"', schema=None):
100+
def format_value(value, quotes='"', schema=None, version=None):
111101
if schema and "enum" in schema:
112102
index = schema["enum"].index(value)
113103
enum_varnames = schema["x-enum-varnames"][index]
114104
name = schema_name(schema)
115-
return f"{name.upper()}_{enum_varnames}"
105+
return f"crate::datadog{version.upper()}::model::{name}::{enum_varnames}"
116106

117107
if isinstance(value, str):
118108
return f"{quotes}{value}{quotes}"
@@ -123,7 +113,7 @@ def format_value(value, quotes='"', schema=None):
123113
return value
124114

125115

126-
def simple_type(schema, render_nullable=False, render_new=False):
116+
def simple_type(schema, render_nullable=False, render_option=True, render_new=False):
127117
"""Return the simple type of a schema.
128118
129119
:param schema: The schema to extract the type from
@@ -132,34 +122,41 @@ def simple_type(schema, render_nullable=False, render_new=False):
132122
type_name = schema.get("type")
133123
type_format = schema.get("format")
134124
nullable = render_nullable and schema.get("nullable", False)
135-
136-
nullable_prefix = "datadog.NewNullable" if render_new else "datadog.Nullable"
137-
125+
126+
inner_type = None
138127
if type_name == "integer":
139-
return {
140-
"int32": "int32" if not nullable else f"{nullable_prefix}Int32",
141-
"int64": "int64" if not nullable else f"{nullable_prefix}Int64",
142-
None: "int32" if not nullable else f"{nullable_prefix}Int32",
128+
inner_type = {
129+
"int32": "i32",
130+
"int64": "i64",
131+
None: "i32",
143132
}[type_format]
144133

145134
if type_name == "number":
146-
return {
147-
"double": "float64" if not nullable else f"{nullable_prefix}Float64",
148-
None: "float" if not nullable else f"{nullable_prefix}Float",
135+
inner_type = {
136+
"double": "f64",
137+
None: "f64",
149138
}[type_format]
150139

151140
if type_name == "string":
152-
return {
153-
"date": "time.Time" if not nullable else f"{nullable_prefix}Time",
154-
"date-time": "time.Time" if not nullable else f"{nullable_prefix}Time",
155-
"email": "string" if not nullable else f"{nullable_prefix}String",
156-
"binary": "*os.File",
157-
None: "string" if not nullable else f"{nullable_prefix}String",
141+
inner_type = {
142+
"date": "String",
143+
"date-time": "String",
144+
"email": "String",
145+
"binary": "Vec<u8>",
146+
None: "String",
158147
}[type_format]
159148
if type_name == "boolean":
160-
return "bool" if not nullable else f"{nullable_prefix}Bool"
149+
inner_type = "bool"
150+
151+
if inner_type == None:
152+
return None
161153

162-
return None
154+
simple_type = inner_type
155+
if nullable:
156+
simple_type = f"Option<{simple_type}>"
157+
if render_option:
158+
simple_type = f"Option<{simple_type}>"
159+
return simple_type
163160

164161

165162
def is_reference(schema, attribute):
@@ -195,7 +192,7 @@ def attribute_path(attribute):
195192

196193

197194
def go_name(name):
198-
"""Convert key to Go name.
195+
"""Convert key to Rust name.
199196
200197
Example:
201198
@@ -398,7 +395,7 @@ def format_data_with_schema(
398395
if replace_values and data in replace_values:
399396
parameters = replace_values[data]
400397

401-
# Make sure that variables used in given statements are camelCase for Go linter
398+
# Make sure that variables used in given statements are camelCase for Rust linter
402399
if parameters in variables:
403400
parameters = go_name(parameters)
404401

@@ -651,7 +648,7 @@ def format_data_with_schema_dict(
651648
else:
652649
parameters = saved_parameters
653650
else:
654-
return f"map[string]{nested_schema_name}{{\n{parameters}}}"
651+
return f"None"#f"map[string]{nested_schema_name}{{\n{parameters}}}"
655652

656653
if "oneOf" in schema:
657654
return _format_oneof(schema, data, name, name_prefix, replace_values, required, nullable, **kwargs)

0 commit comments

Comments
 (0)