{"id":13508964,"url":"https://github.com/bitwalker/exprotobuf","last_synced_at":"2025-05-15T05:06:54.579Z","repository":{"id":16749605,"uuid":"19507204","full_name":"bitwalker/exprotobuf","owner":"bitwalker","description":"Protocol Buffers in Elixir made easy!","archived":false,"fork":false,"pushed_at":"2024-07-04T16:43:38.000Z","size":182,"stargazers_count":484,"open_issues_count":21,"forks_count":69,"subscribers_count":14,"default_branch":"master","last_synced_at":"2025-05-13T03:07:24.007Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Elixir","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/bitwalker.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2014-05-06T19:12:42.000Z","updated_at":"2025-05-12T10:57:02.000Z","dependencies_parsed_at":"2024-11-01T08:31:46.860Z","dependency_job_id":"a831b6c8-227f-4baa-85bd-fb809c051edd","html_url":"https://github.com/bitwalker/exprotobuf","commit_stats":{"total_commits":159,"total_committers":33,"mean_commits":4.818181818181818,"dds":0.3836477987421384,"last_synced_commit":"36dc21c075140ba35586d89feca23bbb64ae8e9c"},"previous_names":[],"tags_count":40,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bitwalker%2Fexprotobuf","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bitwalker%2Fexprotobuf/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bitwalker%2Fexprotobuf/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bitwalker%2Fexprotobuf/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bitwalker","download_url":"https://codeload.github.com/bitwalker/exprotobuf/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254276447,"owners_count":22043867,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-01T02:01:01.096Z","updated_at":"2025-05-15T05:06:49.559Z","avatar_url":"https://github.com/bitwalker.png","language":"Elixir","readme":"# Protocol Buffers for Elixir\n\nexprotobuf works by building module/struct definitions from a [Google Protocol Buffer](https://code.google.com/p/protobuf)\nschema. This allows you to work with protocol buffers natively in Elixir, with easy decoding/encoding for transport across the\nwire.\n\n[![Build Status](https://travis-ci.org/bitwalker/exprotobuf.svg?branch=master)](https://travis-ci.org/bitwalker/exprotobuf)\n[![Hex.pm Version](http://img.shields.io/hexpm/v/exprotobuf.svg?style=flat)](https://hex.pm/packages/exprotobuf)\n\n## Features\n\n* Load protobuf from file or string\n* Respects the namespace of messages\n* Allows you to specify which modules should be loaded in the definition of records\n* Currently uses [gpb](https://github.com/tomas-abrahamsson/gpb) for protobuf schema parsing\n\nTODO:\n\n* Clean up code/tests\n\n## Breaking Changes\n\nThe 1.0 release removed the feature of handling `import \"...\";` statements.\nPlease see [the imports upgrade guide](imports_upgrade_guide.md) for details if you were using this feature.\n\n## Getting Started\n\nAdd exprotobuf as a dependency to your project:\n\n```elixir\ndefp deps do\n  [{:exprotobuf, \"~\u003e x.x.x\"}]\nend\n```\n\nThen run `mix deps.get` to fetch.\n\nAdd exprotobuf to applications list:\n\n```elixir\ndef application do\n  [applications: [:exprotobuf]]\nend\n```\n\n## Usage\n\nUsage of exprotobuf boils down to a single `use` statement within one or\nmore modules in your project.\n\nLet's start with the most basic of usages:\n\n### Define from a string\n\n```elixir\ndefmodule Messages do\n  use Protobuf, \"\"\"\n    message Msg {\n      message SubMsg {\n        required uint32 value = 1;\n      }\n\n      enum Version {\n        V1 = 1;\n        V2 = 2;\n      }\n\n      required Version version = 2;\n      optional SubMsg sub = 1;\n    }\n  \"\"\"\nend\n```\n\n```elixir\niex\u003e msg = Messages.Msg.new(version: :'V2')\n%Messages.Msg{version: :V2, sub: nil}\niex\u003e encoded = Messages.Msg.encode(msg)\n\u003c\u003c16, 2\u003e\u003e\niex\u003e Messages.Msg.decode(encoded)\n%Messages.Msg{version: :V2, sub: nil}\n```\n\nThe above code takes the provided protobuf schema as a string, and\ngenerates modules/structs for the types it defines. In this case, there\nwould be a Msg module, containing a SubMsg and Version module. The\nproperties defined for those values are keys in the struct belonging to\neach. Enums do not generate structs, but a specialized module with two\nfunctions: `atom(x)` and `value(x)`. These will get either the name of\nthe enum value, or it's associated value.\n\nValues defined in the schema using the `oneof` construct are represented with tuples:\n\n```elixir\ndefmodule Messages do\n  use Protobuf, \"\"\"\n    message Msg {\n      oneof choice {\n        string first = 1;\n        int32 second = 2;\n      }\n    }\n  \"\"\"\nend\n```\n\n```elixir\niex\u003e msg = Messages.Msg.new(choice: {:second, 42})\n%Messages.Msg{choice: {:second, 42}}\niex\u003e encoded = Messages.Msg.encode(msg)\n\u003c\u003c16, 42\u003e\u003e\n```\n\n### Define from a file\n\n```elixir\ndefmodule Messages do\n  use Protobuf, from: Path.expand(\"../proto/messages.proto\", __DIR__)\nend\n```\n\nThis is equivalent to the above, if you assume that `messages.proto`\ncontains the same schema as in the string of the first example.\n\n### Loading all definitions from a set of files\n\n```elixir\ndefmodule Protobufs do\n  use Protobuf, from: Path.wildcard(Path.expand(\"../definitions/**/*.proto\", __DIR__))\nend\n```\n\n```elixir\niex\u003e Protobufs.Msg.new(v: :V1)\n%Protobufs.Msg{v: :V1}\niex\u003e %Protobufs.OtherMessage{middle_name: \"Danger\"}\n%Protobufs.OtherMessage{middle_name: \"Danger\"}\n```\n\nThis will load all the various definitions in your `.proto` files and\nallow them to share definitions like enums or messages between them.\n\n### Customizing Generated Module Names\n\nIn some cases your library of protobuf definitions might already contain some\nnamespaces that you would like to keep.\nIn this case you will probably want to pass the `use_package_names: true` option.\nLet's say you had a file called `protobufs/example.proto` that contained:\n\n```protobuf\npackage world;\nmessage Example {\n  enum Continent {\n    ANTARCTICA = 0;\n    EUROPE = 1;\n  }\n\n  optional Continent continent = 1;\n  optional uint32 id = 2;\n}\n```\n\nYou could load that file (and everything else in the protobufs directory) by doing:\n\n```elixir\ndefmodule Definitions do\n  use Protobuf, from: Path.wildcard(\"protobufs/*.proto\"), use_package_names: true\nend\n```\n\n```elixir\niex\u003e Definitions.World.Example.new(continent: :EUROPE)\n%Definitions.World.Example{continent: :EUROPE}\n```\n\nYou might also want to define all of these modules in the top-level namespace. You\ncan do this by passing an explicit `namespace: :\"Elixir\"` option.\n\n```elixir\ndefmodule Definitions do\n  use Protobuf, from: Path.wildcard(\"protobufs/*.proto\"),\n                use_package_names: true,\n                namespace: :\"Elixir\"\nend\n```\n\n```elixir\niex\u003e World.Example.new(continent: :EUROPE)\n%World.Example{continent: :EUROPE}\n```\n\nNow you can use just the package names and message names that your team is already\nfamiliar with.\n\n### Inject a definition into an existing module\n\nThis is useful when you only have a single type, or if you want to pull\nthe module definition into the current module instead of generating a\nnew one.\n\n```elixir\ndefmodule Msg do\n  use Protobuf, from: Path.expand(\"../proto/messages.proto\", __DIR__), inject: true\n\n  def update(msg, key, value), do: Map.put(msg, key, value)\nend\n```\n\n```elixir\niex\u003e %Msg{}\n%Msg{v: :V1}\niex\u003e Msg.update(%Msg{}, :v, :V2)\n%Msg{v: :V2}\n```\n\nAs you can see, Msg is no longer created as a nested module, but is\ninjected right at the top level. I find this approach to be a lot\ncleaner than `use_in`, but may not work in all use cases.\n\n### Inject a specific type from a larger subset of types\n\nWhen you have a large schema, but perhaps only care about a small subset\nof those types, you can use `:only`:\n\n```elixir\ndefmodule Messages do\n  use Protobuf, from: Path.expand(\"../proto/messages.proto\", __DIR__),\nonly: [:TypeA, :TypeB]\nend\n```\n\nAssuming that the provided .proto file contains multiple type\ndefinitions, the above code would extract only TypeA and TypeB as nested\nmodules. Keep in mind your dependencies, if you select a child type\nwhich depends on a parent, or another top-level type, exprotobuf may\nfail, or your code may fail at runtime.\n\nYou may only combine `:only` with `:inject` when `:only` is a single\ntype, or a list containing a single type. This is due to the restriction\nof one struct per module. Theoretically you should be able to pass `:only`\nwith multiple types, as long all but one of the types is an enum, since\nenums are just generated as modules, this does not currently work\nthough.\n\n### Extend generated modules via `use_in`\n\nIf you need to add behavior to one of the generated modules, `use_in`\nwill help you. The tricky part is that the struct for the module you\n`use_in` will not be defined yet, so you can't rely on it in your\nfunctions. You can still work with the structs via the normal Maps API,\nbut you lose compile-time guarantees. I would recommend favoring\n`:inject` over this when possible, as it's a much cleaner solution.\n\n```elixir\ndefmodule Messages do\n  use Protobuf, \"\n    message Msg {\n      enum Version {\n        V1 = 1;\n        V2 = 1;\n      }\n      required Version v = 1;\n    }\n  \"\n\n  defmodule MsgHelpers do\n    defmacro __using__(_opts) do\n      quote do\n        def convert_to_record(msg) do\n          msg\n          |\u003e Map.to_list\n          |\u003e Enum.reduce([], fn {_key, value}, acc -\u003e [value | acc] end)\n          |\u003e Enum.reverse\n          |\u003e list_to_tuple\n        end\n      end\n    end\n  end\n\n  use_in \"Msg\", MsgHelpers\nend\n```\n\n```elixir\niex\u003e Messages.Msg.new |\u003e Messages.Msg.convert_to_record\n{Messages.Msg, :V1}\n```\n\n## Attribution/License\n\nexprotobuf is a fork of the azukiaapp/elixir-protobuf project, both of which are released under Apache 2 License.\n\nCheck LICENSE files for more information.\n","funding_links":[],"categories":["Protocols","Elixir"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbitwalker%2Fexprotobuf","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbitwalker%2Fexprotobuf","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbitwalker%2Fexprotobuf/lists"}