-
Notifications
You must be signed in to change notification settings - Fork 46
/
generate.py
134 lines (111 loc) · 4.5 KB
/
generate.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
import logging
import shutil
from pathlib import Path
import click
from algokit.core.generate import load_generators, run_generator
from algokit.core.typed_client_generation import ClientGenerator
logger = logging.getLogger(__name__)
def _load_custom_generate_commands(project_dir: Path) -> dict[str, click.Command]:
"""
Load custom generate commands from .algokit.toml file.
:param project_dir: Project directory path.
:return: Custom generate commands.
"""
generators = load_generators(project_dir)
commands_table: dict[str, click.Command] = {}
for generator in generators:
@click.command(
name=generator.name, help=generator.description or "Generator command description is not supplied."
)
@click.option(
"answers",
"--answer",
"-a",
multiple=True,
help="Answers key/value pairs to pass to the template.",
nargs=2,
default=[],
metavar="<key> <value>",
)
@click.option(
"path",
"--path",
"-p",
help=f"Path to {generator.name} generator. (Default: {generator.path})",
type=click.Path(exists=True),
default=generator.path,
)
def command(answers: dict, path: Path) -> None:
if not shutil.which("git"):
raise click.ClickException(
"Git not found; please install git and add to path.\n"
"See https://github.com/git-guides/install-git for more information."
)
return run_generator(answers, path)
commands_table[generator.name] = command
return commands_table
class GeneratorGroup(click.Group):
def get_command(self, ctx: click.Context, cmd_name: str) -> click.Command | None:
return_value = super().get_command(ctx, cmd_name)
if return_value is not None:
return return_value
commands = _load_custom_generate_commands(Path.cwd())
return commands.get(cmd_name)
def list_commands(self, ctx: click.Context) -> list[str]:
predefined_command_names = super().list_commands(ctx)
dynamic_commands = _load_custom_generate_commands(Path.cwd())
dynamic_command_names = list(dynamic_commands)
return sorted(predefined_command_names + dynamic_command_names)
@click.group("generate", cls=GeneratorGroup)
def generate_group() -> None:
"""Generate code for an Algorand project."""
@generate_group.command("client")
@click.argument(
"app_spec_path_or_dir",
type=click.Path(exists=True, dir_okay=True, resolve_path=True, path_type=Path),
)
@click.option(
"output_path_pattern",
"--output",
"-o",
type=click.Path(exists=False),
default=None,
help="Path to the output file. The following tokens can be used to substitute into the output path:"
" {contract_name}, {app_spec_dir}",
)
@click.option(
"--language",
"-l",
default=None,
type=click.Choice(ClientGenerator.languages()),
help="Programming language of the generated client code",
)
def generate_client(output_path_pattern: str | None, app_spec_path_or_dir: Path, language: str | None) -> None:
"""Create a typed ApplicationClient from an ARC-32 application.json
Supply the path to an application specification file or a directory to recursively search
for "application.json" files"""
if language is not None:
generator = ClientGenerator.create_for_language(language)
elif output_path_pattern is not None:
extension = Path(output_path_pattern).suffix
try:
generator = ClientGenerator.create_for_extension(extension)
except KeyError as ex:
raise click.ClickException(
"Could not determine language from file extension, Please use the --language option to specify a "
"target language"
) from ex
else:
raise click.ClickException(
"One of --language or --output is required to determine the client langauge to generate"
)
if not app_spec_path_or_dir.is_dir():
app_specs = [app_spec_path_or_dir]
else:
app_specs = sorted(app_spec_path_or_dir.rglob("application.json"))
if not app_specs:
raise click.ClickException("No app specs found")
for app_spec in app_specs:
output_path = generator.resolve_output_path(app_spec, output_path_pattern)
if output_path is not None:
generator.generate(app_spec, output_path)