Current section

Files

Jump to
pbuf lib protoc fields field.ex
Raw

lib/protoc/fields/field.ex

defmodule Pbuf.Protoc.Field do
alias Pbuf.Protoc
alias Pbuf.Protoc.Fields
@enforce_keys [:tag, :name, :prefix, :typespec, :encode_fun, :decode_fun, :default]
defstruct @enforce_keys ++ [
hidden: false,
oneof_index: nil,
post_decode: :none,
]
@type t :: %__MODULE__{
tag: pos_integer,
name: String.t,
prefix: binary,
typespec: String.t,
encode_fun: String.t,
decode_fun: String.t,
default: String.t,
hidden: boolean,
oneof_index: nil | non_neg_integer,
post_decode: :none | :map | :repack | :oneof1 | :oneof2
}
defmacro __using__(_opts) do
quote do
alias Pbuf.Protoc.Field
import Pbuf.Protoc.Field
end
end
# returns a type that implements the Field protocol
@spec build(Protoc.proto_field, Context.t) :: any
def build(desc, context) do
case get_type(desc, context) do
:map -> Fields.Map.new(desc, context)
:oneof -> Fields.OneOf.new(desc, context)
:simple -> Fields.Simple.new(desc, context)
:enum -> Fields.Enumeration.new(desc, context)
end
end
@spec get_type(Protoc.proto_field, Context.t) :: :map | :oneof | :enum | :simple
defp get_type(desc, context) do
with {false, _} <- {is_map?(desc.type_name, context.maps), :map},
{false, _} <- {is_oneof?(desc.oneof_index, context), :oneof},
{false, _} <- {is_enum?(desc.type), :enum}
do
:simple
else
{true, type} -> type
end
end
defp is_enum?(type) do
internal_type(type) == :enum
end
defp is_oneof?(nil, _ctx) do
false
end
defp is_oneof?(0, %{oneofs: m}) when map_size(m) == 0 do
false
end
defp is_oneof?(_index, _ctx) do
true
end
defp is_map?(nil, _maps) do
false
end
defp is_map?(type_name, maps) do
Enum.any?(maps, fn {name, _value} ->
String.ends_with?(type_name, name)
end)
end
# helpers for actual field builders
def module_name(%{type_name: <<".", type::binary>>}) do
type
|> String.split(".")
|> Enum.map(&Protoc.capitalize_first/1)
|> Enum.join(".")
end
@spec is_repeated?(Protoc.proto_field) :: boolean
def is_repeated?(desc) do
desc.label == :LABEL_REPEATED
end
@spec extract_core(Protoc.proto_field) :: {non_neg_integer, String.t, atom, binary}
def extract_core(desc, packed? \\ true) do
tag = desc.number
name = desc.name
type = internal_type(desc.type)
prefix = case is_repeated?(desc) && packed? do
true -> Pbuf.Encoder.prefix(tag, :bytes) # bytes share the same encoding type as any type of array
false -> Pbuf.Encoder.prefix(tag, type)
end
{tag, name, type, stringify_binary(prefix)}
end
@spec internal_type(byte | atom) :: atom
def internal_type(:TYPE_BOOL), do: :bool
def internal_type(:TYPE_BYTES), do: :bytes
def internal_type(:TYPE_STRING), do: :string
def internal_type(:TYPE_DOUBLE), do: :double
def internal_type(:TYPE_FLOAT), do: :float
def internal_type(:TYPE_INT32), do: :int32
def internal_type(:TYPE_INT64), do: :int64
def internal_type(:TYPE_SINT32), do: :sint32
def internal_type(:TYPE_SINT64), do: :sint64
def internal_type(:TYPE_FIXED32), do: :fixed32
def internal_type(:TYPE_FIXED64), do: :fixed64
def internal_type(:TYPE_SFIXED32), do: :sfixed32
def internal_type(:TYPE_SFIXED64), do: :sfixed64
def internal_type(:TYPE_UINT32), do: :uint32
def internal_type(:TYPE_UINT64), do: :uint64
def internal_type(:TYPE_MESSAGE), do: :struct
def internal_type(:TYPE_ENUM), do: :enum
def stringify_binary(bin) do
s = bin
|> :erlang.binary_to_list()
|> Enum.map(&Integer.to_string/1)
|> Enum.join(", ")
"<<#{s}>>"
end
end