Skip to content

Commit

Permalink
Add mix task to generate files
Browse files Browse the repository at this point in the history
  • Loading branch information
ahamez committed Jul 4, 2020
1 parent 002f3f1 commit 59a3275
Show file tree
Hide file tree
Showing 6 changed files with 74 additions and 4 deletions.
2 changes: 0 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
language: elixir
matrix:
include:
- elixir: 1.6
otp_release: 21.0
- elixir: 1.7
otp_release: 22.0
- elixir: 1.8
Expand Down
16 changes: 15 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ Protox is an Elixir library to work with [Google's Protocol Buffers](https://dev

Generally speaking, a lot of efforts have been put into making sure that the library is reliable (for instance using [property based testing](https://github.com/alfert/propcheck) and by having a [100% code coverage](https://coveralls.io/github/ahamez/protox?branch=master)). Therefore, this library passes all the tests of the conformance checker provided by Google. See [Conformance](#conformance) section for more information.

This library is easy to use: you just point to the `*.proto` files or give the schema to the `Protox` macro, no need to generate any file! Furthermore, it provides a full-blown Elixir experience with protobuf messages. For instance, given the following protobuf `msg.proto` file:
This library is easy to use: you just point to the `*.proto` files or give the schema to the `Protox` macro, no need to generate any file! However, should you need to generate files, a mix task is available (see [Files generation](#files-generation)).

This library also provides a full-blown Elixir experience with protobuf messages. For instance, given the following protobuf `msg.proto` file:
```proto
syntax = "proto3";
Expand Down Expand Up @@ -292,6 +294,18 @@ When you encode a message that contains unknown fields, they will be reencoded i

The detailed reference of the generated code is available [here](documentation/reference.md).

## Files generation

It's also possible to generate a file that will contain all code corresponding to the protobuf messages:
```shell
MIX_ENV=prod mix protox.generate --output-path=/path/to/message.ex --include-path=. test/messages.proto test/samples/proto2.proto
```
The `--include-path` option is the same as the option described in section [Specify import path](#specify-import-path).
The generated file will be usable in any project as long as protox is declared in the dependancies (the generated file is not a standalone, it still needs function from the protox runtime).
## Types mapping
The following table shows how Protobuf types are mapped to Elixir's ones.
Expand Down
3 changes: 2 additions & 1 deletion coveralls.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"skip_files": [
"conformance",
"benchmarks"
"benchmarks",
"generate.ex"
]
}
25 changes: 25 additions & 0 deletions lib/mix/tasks/protox/generate.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
defmodule Mix.Tasks.Protox.Generate do
@moduledoc false

use Mix.Task

@impl Mix.Task
@spec run(any) :: any
def run(args) do
with {options, files, []} <-
OptionParser.parse(args, strict: [output_path: :string, include_path: :string]),
{:ok, output_path} <- Keyword.fetch(options, :output_path),
include_path <- Keyword.get(options, :include_path) do
generate_code(files, output_path, include_path)
else
err ->
IO.puts("Failed to generate code: #{inspect(err)}")
:error
end
end

defp generate_code(files, output_path, include_path) do
code = Protox.generate_code(files, include_path)
File.write!(output_path, code)
end
end
21 changes: 21 additions & 0 deletions lib/protox.ex
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,27 @@ defmodule Protox do
end
end

def generate_code(files, include_path \\ nil) do
path =
case include_path do
nil -> nil
_ -> Path.expand(include_path)
end

{:ok, file_descriptor_set} =
files
|> Enum.map(&Path.expand/1)
|> Protox.Protoc.run(path)

{enums, messages} = Protox.Parse.parse(file_descriptor_set, nil)

code = quote do: unquote(Protox.Define.define(enums, messages))

code_str = Macro.to_string(code)

["#", " credo:disable-for-this-file\n", code_str]
end

defp make_external_resources(files) do
Enum.map(files, fn file -> quote(do: @external_resource(unquote(file))) end)
end
Expand Down
11 changes: 11 additions & 0 deletions test/protox_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,17 @@ defmodule ProtoxTest do
assert msg == msg |> Camel.encode!() |> :binary.list_to_bin() |> Camel.decode!()
end

test "Generate code" do
file = Path.join(__DIR__, "./samples/prefix/bar/bar.proto")
str = Protox.generate_code([file], "./test/samples")

tmp_dir = System.tmp_dir!()
tmp_file = Path.join(tmp_dir, "generated_code.ex")
File.write!(tmp_file, str)

assert Code.compile_file(tmp_file) != []
end

@tag conformance: true
test "Launch conformance" do
{:ok, _} = File.rm_rf("./failing_tests.txt")
Expand Down

0 comments on commit 59a3275

Please sign in to comment.