Packages
protox
0.7.1
2.0.9
2.0.8
2.0.7
2.0.6
2.0.5
2.0.4
2.0.3
2.0.2
2.0.1
2.0.0
2.0.0-dev
1.7.8
1.7.7
1.7.6
1.7.5
1.7.4
retired
1.7.3
1.7.2
1.7.1
1.7.0
1.6.10
1.6.9
1.6.8
1.6.7
1.6.6
1.6.5
1.6.4
1.6.3
1.6.2
1.6.1
1.6.0
1.5.1
1.5.0
1.4.0
1.3.2
1.3.1
1.3.0
1.2.4
1.2.3
1.2.2
1.2.1
1.2.0
1.1.1
1.1.0
1.0.0
0.25.0
0.24.0
0.23.1
0.23.0
0.22.0
0.21.0
0.20.0
0.19.1
0.19.0
0.18.0
0.17.0
0.16.2
0.16.1
0.16.0
0.15.2
0.15.1
0.15.0
0.14.0
0.13.0
0.12.1
0.12.0
0.11.1
0.11.0
0.10.0
0.9.1
0.9.0
0.8.0
0.7.1
0.7.0
A fast, easy to use and 100% conformant Elixir library for Google Protocol Buffers (aka protobuf)
Current section
Files
Jump to
Current section
Files
lib/protox/define.ex
defmodule Protox.Define do
@moduledoc false
# Generates structs from message and enumeration definitions.
defmacro __using__(enums: enums, messages: messages) do
define(
Code.eval_quoted(enums) |> elem(0),
Code.eval_quoted(messages) |> elem(0)
)
end
def define(enums, messages) do
define_enums(enums) ++ define_messages(messages)
end
# -- Private
defp define_enums(enums) do
for {enum_name, constants} <- enums do
default_fun = make_enum_default(constants)
encode_constants_funs = make_encode_enum_constants(constants)
decode_constants_funs = make_decode_enum_constants(constants)
quote do
defmodule unquote(enum_name) do
@moduledoc false
unquote(default_fun)
unquote(encode_constants_funs)
def encode(x), do: x
unquote(decode_constants_funs)
def decode(x), do: x
def constants(), do: unquote(constants)
end
end
end
end
defp define_messages(messages) do
for {msg_name, fields} <- messages do
struct_fields = make_struct_fields(fields)
required = get_required_fields(fields)
fields_map = make_fields_map(fields)
encoder = Protox.DefineEncoder.define(fields)
quote do
defmodule unquote(msg_name) do
@moduledoc false
import Protox.Encode
# Use @enforce_keys for protobuf 2 `required` fields.
@enforce_keys unquote(required)
defstruct unquote(struct_fields)
# The encoding function is generated for each message.
unquote(encoder)
@spec decode!(binary) :: struct | no_return
def decode!(bytes) do
Protox.Decode.decode!(bytes, unquote(msg_name))
end
@spec decode(binary) :: {:ok, struct} | {:error, any}
def decode(bytes) do
Protox.Decode.decode(bytes, unquote(msg_name))
end
def defs(), do: unquote(fields_map)
end # module
end
end # for
end
# -- Enum
defp make_enum_default(constant_values) do
[{_, default_value} | _] = constant_values
quote do
def default(), do: unquote(default_value)
end
end
defp make_encode_enum_constants(constant_values) do
for {value, constant} <- constant_values do
quote do
def encode(unquote(constant)), do: unquote(value)
end
end
end
defp make_decode_enum_constants(constant_values) do
# Map.new -> unify enum aliases
for {value, constant} <- (Map.new(constant_values)) do
quote do
def decode(unquote(value)), do: unquote(constant)
end
end
end
# Generate fields of the struct which is create for a message.
defp make_struct_fields(fields) do
for {_, _, name, kind, _} <- fields do
case kind do
:map -> {name, Macro.escape(%{})}
{:oneof, parent} -> {parent, nil}
:packed -> {name, []}
:unpacked -> {name, []}
{:default, default_value} -> {name, default_value}
end
end
end
# Get the list of fields that are marked as `required`.
defp get_required_fields(fields) do
for {_, :required, name, _, _} <- fields, do: name
end
# Generate a map used to store a message's definitions.
defp make_fields_map(fields) do
fields
|> Enum.reduce(%{},
fn ({tag, _, name, kind, type}, acc) ->
ty = case {kind, type} do
{:map, {key_type, {:message, msg}}} -> {key_type, {:message, msg}}
{:map, {key_type, {:enum, enum}}} -> {key_type, {:enum, enum}}
{_, {:enum, enum}} -> {:enum, enum}
{_, {:message, msg}} -> {:message, msg}
{_, ty} -> ty
end
Map.put(acc, tag, {name, kind, ty})
end)
|> Macro.escape()
end
end