README.md

# Protocol Buffers for Elixir

exprotobuf works by building module/struct definitions from a [Google Protocol Buffer](https://code.google.com/p/protobuf)
schema. This allows you to work with protocol buffers natively in Elixir, with easy decoding/encoding for transport across the
wire.

[![Build Status](https://travis-ci.org/bitwalker/exprotobuf.svg?branch=master)](https://travis-
ci.org/bitwalker/exprotobuf)

## Features

* Load protobuf from file or string
* Respects the namespace of messages
* Allows you to specify which modules should be loaded in the definition of records
* Currently uses [gpb](https://github.com/tomas-abrahamsson/gpb) for protobuf schema parsing

TODO:

* Support importing definitions
* Clean up code/tests

## Getting Started

Add exprotobuf as a dependency to your project:

```elixir
defp deps do
  [{:exprotobuf, "~> 0.6.1"},
   {:gpb, github: "tomas-abrahamsson/gpb"}]
end
```

Then run `mix deps.get` to fetch.

## Usage

Usage of exprotobuf boils down to a single `use` statement within one or
more modules in your project.

Let's start with the most basic of usages:

### Define from a string

```elixir
defmodule Messages do
  use Protobuf, """
    message Msg {
      message SubMsg {
        required uint32 value = 1;
      }

      enum Version {
        V1 = 1;
        V2 = 2;
      }

      required Version version = 2;
      optional SubMsg sub = 1;
    }
  """
end
```

```elixir
iex> msg = Messages.Msg.new(version: :'V2')
%Messages.Msg{version: :V2, sub: nil}
iex> encoded = Messages.Msg.encode(msg)
<<16, 2>>
iex> Messages.Msg.decode(encoded)
%Messages.Msg{version: :V2, sub: nil}
```

The above code takes the provided protobuf schema as a string, and
generates modules/structs for the types it defines. In this case, there
would be a Msg module, containing a SubMsg and Version module. The
properties defined for those values are keys in the struct belonging to
each. Enums do not generate structs, but a specialized module with two
functions: `atom(x)` and `value(x)`. These will get either the name of
the enum value, or it's associated value.

### Define from a file

```elixir
defmodule Messages do
  use Protobuf, from: Path.expand("../proto/messages.proto", __DIR__)
end
```

This is equivalent to the above, if you assume that `messages.proto`
contains the same schema as in the string of the first example.

### Inject a definition into an existing module

This is useful when you only have a single type, or if you want to pull
the module definition into the current module instead of generating a
new one.

```elixir
defmodule Msg do
  use Protobuf, from: Path.expand("../proto/messages.proto", __DIR__), inject: true

  def update(msg, key, value), do: Map.put(msg, key, value)
end
```

```elixir
iex> %Msg{}
%Msg{v: :V1}
iex> Msg.update(%Msg{}, :v, :V2)
%Msg{v: :V2}
```

As you can see, Msg is no longer created as a nested module, but is
injected right at the top level. I find this approach to be a lot
cleaner than `use_in`, but may not work in all use cases.

### Inject a specific type from a larger subset of types

When you have a large schema, but perhaps only care about a small subset
of those types, you can use `:only`:

```elixir
defmodule Messages do
  use Protobuf, from: Path.expand("../proto/messages.proto", __DIR__),
only: [:TypeA, :TypeB]
end
```

Assuming that the provided .proto file contains multiple type
definitions, the above code would extract only TypeA and TypeB as nested
modules. Keep in mind your dependencies, if you select a child type
which depends on a parent, or another top-level type, exprotobuf may
fail, or your code may fail at runtime.

You may only combine `:only` with `:inject` when `:only` is a single
type, or a list containing a single type. This is due to the restriction
of one struct per module. Theoretically you should be able to pass `:only`
with multiple types, as long all but one of the types is an enum, since
enums are just generated as modules, this does not currently work
though.

### Extend generated modules via `use_in`

If you need to add behavior to one of the generated modules, `use_in`
will help you. The tricky part is that the struct for the module you
`use_in` will not be defined yet, so you can't rely on it in your
functions. You can still work with the structs via the normal Maps API,
but you lose compile-time guarantees. I would recommend favoring
`:inject` over this when possible, as it's a much cleaner solution.

```elixir
defmodule Messages do
  use Protobuf, "
    message Msg {
      enum Version {
        V1 = 1;
        V2 = 1;
      }
      required Version v = 1;
    }
  "

  defmodule MsgHelpers do
    defmacro __using__(_opts) do
      quote do
        def convert_to_record(msg) do
          msg
          |> Map.to_list
          |> Enum.reduce([], fn {_key, value}, acc -> [value | acc] end)
          |> Enum.reverse
          |> list_to_tuple
        end
      end
    end
  end

  use_in "Msg", MsgHelpers
end
```

```elixir
iex> Messages.Msg.new |> Messages.Msg.convert_to_record
{Messages.Msg, :V1}
```

## Attribution/License

exprotobuf is a fork of the azukiaapp/elixir-protobuf project, both of which are released under Apache 2 License.

Check LICENSE files for more information.