Current section

Files

Jump to
protox lib protox generate.ex
Raw

lib/protox/generate.ex

defmodule Protox.Generate do
@moduledoc false
defmodule FileContent do
@moduledoc false
defstruct([:name, :content])
end
def generate_module_code(files, output_path, multiple_files, include_paths, opts \\ [])
when is_list(files) and is_binary(output_path) and is_boolean(multiple_files) do
paths =
case include_paths do
[] -> nil
_ -> Enum.map(include_paths, &Path.expand/1)
end
case launch_protoc(files, paths) do
{:ok, file_descriptor_set} ->
{:ok, definition} = Protox.Parse.parse(file_descriptor_set, opts)
code = Protox.Define.define(definition, opts)
{:ok, generate_files(output_path, code, multiple_files)}
{:error, msg} ->
{:error, msg}
end
end
# -- Private
defp launch_protoc(files, paths) do
files
|> Enum.map(&Path.expand/1)
|> Protox.Protoc.run(paths)
end
defp generate_files(output_path, code, false = _multiple_files) do
[
%FileContent{
name: output_path,
content: generate_file_content(code)
}
]
end
defp generate_files(output_path, code, true = _multiple_files) do
# Protox.Define.define outputs a block, we thus have to extract the content
# on which we can iterate.
{:__block__, _, block} = code
Enum.map(block, fn {:defmodule, _, [module_name | _]} = module_code ->
snake_module_name =
module_name
|> Module.split()
|> Enum.map_join("_", &Macro.underscore/1)
%FileContent{
name: Path.join(output_path, snake_module_name <> ".ex"),
content: generate_file_content(module_code)
}
end)
end
defp generate_file_content(code) do
[
"# Code generated by protox. Don't edit.\n",
"# credo:disable-for-this-file\n",
code |> Macro.to_string() |> Code.format_string!(),
"\n"
]
end
end