Protobuf support for Gleam ✨
gleam_pb wraps the excellent gpb erlang library and generates idiomatic Gleam types 🤘
- Gleam Type generation
- custom functions that better handle default values
- gleam format generated files
- stop including unnecessary imports
- message encoding
- message decoding
- improve UX
- call protoc-erl internally
- flag to better handle gpb header includes
- helper functions
- grpc
gleam_pb generally follows gpb's type generation, but makes it easier to use from Gleam.
protobuf | gleam_pb | gpb |
---|---|---|
double,float | Float | float() |
int32, int64, uint32, uint64, sint32, sint64, fixed32, fixed64, sfixed32, sfixed64 | Int | integer() |
bool | Bool | true | false |
enum | Zero Paramater Multi Constructor Type | atom() |
message | Option(<CustomType>) | record | undefined |
string | String | unicode string |
bytes | BitString | binary() |
oneof | Option(<CustomType>) with multiple constructors | {chosen_field, value} |
map | unordered list of tuples List(#(Key, Value)) | [{key, value}] |
gleam_pb generates functions to make using the types easier
-
function to generate the message with protobuf's default values named new_<custom_type>() -> <CustomType>
-
functions to encode and decode the messages
- encode_<custom_type>(m: <CustomType>) -> BitString
- encode_<custom_type>(b: BitString) -> <CustomType>
There are also several other functions intended for usage by gleam_pb
This message
syntax = "proto3";
package protos;
message Example {
message Response {
int32 val = 1;
string user = 2;
}
enum OptionType {
foo = 0;
bar = 1;
baz = 2;
}
repeated OptionType options = 1;
oneof ResponseOrError {
Response response = 2;
string error = 3;
}
}
Becomes
import gleam/option
import gleam/list
import gleam/pair
import gleam/dynamic
import gleam/erlang/atom
import gleam_pb
/// protos package types generated by gleam_pb
/// DO NOT EDIT
pub type OptionType {
OptionTypefoo
OptionTypebar
OptionTypebaz
}
pub type ExampleResponseOrError {
ResponseOrErrorresponse(response: option.Option(Response))
ResponseOrErrorerror(error: String)
}
pub type Example {
Example(
options: List(OptionType),
response_or_error: option.Option(ExampleResponseOrError),
)
}
pub type Response {
Response(val: Int, user: String)
}
pub fn new_example() {
Example(list.new(), option.None)
}
pub fn new_response() {
Response(0, "")
}
pub fn encode_example(m: Example) -> BitString {
let name = atom.create_from_string("protos.Example")
extract_example(name, m)
|> gleam_pb.encode(name)
}
pub fn decode_example(b: BitString) -> Example {
let name = atom.create_from_string("protos.Example")
decode_msg_example(b, name)
|> reconstruct_example
}
pub fn encode_response(m: Response) -> BitString {
let name = atom.create_from_string("protos.Example.Response")
extract_response(name, m)
|> gleam_pb.encode(name)
}
pub fn decode_response(b: BitString) -> Response {
let name = atom.create_from_string("protos.Example.Response")
decode_msg_response(b, name)
|> reconstruct_response
}
//internal functions continue ...
gleam_pb and gpb must be used together to generate working Gleam code.
Example Script
# make sure protoc-gen-gleam is in you're path or add it manually using --plugin
protoc --plugin=protoc-gen-gleam -I . --gleam_out="output_path=./src:./src" protos/*.proto
- 'output_path': (Required) specifies the desired output path
- 'protoc_erl_path': path to gpb's protoc-erl
- 'gpb_header_include': path to prepend to the header include for gpb. See Knwon Issues for more info
- if you need a variable include here, remember that erlang header resolution is quite clever and can use environment variables
protoc -I . \
--gleam_out="gpb_header_include=$ENV/include/,output_path=./src,protoc_erl_path=bin/protoc-erl:./src" \
protos/*.proto
% generated in `gleam_gpb.erl`
-include("gpb.hrl"). % -> update to point to the correct header post Gleam compilation