Protozoa 🦠

A Protocol Buffers library for Gleam, providing encoding and decoding of protobuf messages.

Package Version Hex Docs

Features

βœ… Complete proto3 support - Messages, enums, nested types, oneofs, maps, and repeated fields
βœ… Type-safe encoding/decoding - Compile-time guarantees for message structure
βœ… All field types - Full support for all scalar types, including fixed32/fixed64
βœ… Wire format compliance - Correct Protocol Buffers binary format
βœ… Performance optimized - Efficient encoding and decoding operations

Installation

Add to your gleam.toml:

[dependencies]
protozoa = ">= 2.0.3 and < 3.0.0"

Usage

Basic Encoding and Decoding

import protozoa/encode
import protozoa/decode

// Define your message type
pub type User {
  User(name: String, age: Int, active: Bool)
}

// Encode a message
pub fn encode_user(user: User) -> BitArray {
  encode.message([
    encode.string_field(1, user.name),
    encode.int32_field(2, user.age),
    encode.bool_field(3, user.active),
  ])
}

// Create a decoder
fn user_decoder() -> decode.Decoder(User) {
  use name <- decode.then(decode.string_with_default(1, ""))
  use age <- decode.then(decode.int32_with_default(2, 0))
  use active <- decode.then(decode.bool_with_default(3, False))
  decode.success(User(name: name, age: age, active: active))
}

// Decode a message
pub fn decode_user(data: BitArray) -> Result(User, List(decode.DecodeError)) {
  decode.run(data, user_decoder())
}

Code Generation

For automatic code generation from .proto files, use the separate protozoa_dev package:

# Add as dev dependency
gleam add protozoa_dev --dev

# Generate code from proto files
gleam run -m protozoa/dev -- input.proto output_dir
  1. Use the generated code in your Gleam modules:
import myapp/proto/user
import myapp/proto/message
import myapp/proto/user_service

pub fn example() {
  // Create and encode messages
  let user = user.User(name: "Alice", age: 30, active: True)
  let encoded = user.encode_user(user)
  
  // Decode messages
  let decoded = user.decode_user(encoded)
  
  // Use service stubs for gRPC clients
  let client = user_service.UserServiceClient(endpoint: "http://api.example.com")
}

Manual Usage

For custom workflows, you can also use protozoa directly:

# Compile a specific proto file
gleam run -m protozoa -- message.proto ./output

# Use custom import paths
gleam run -m protozoa -- -I./common -I./vendor message.proto ./src

# Check if files need regeneration (full CLI mode)
gleam run -m protozoa -- --check

CLI Reference

Automatic Project Integration

The recommended approach is to use gleam run -m protozoa, which automatically:

# Generate all proto files (recommended)
gleam run -m protozoa

# Check if proto files have changed
gleam run -m protozoa check

Manual Commands

For advanced usage or custom project structures:

# Single file compilation
gleam run -m protozoa -- input.proto output.gleam

# Multiple import paths
gleam run -m protozoa -- -I./proto -I./vendor input.proto output.gleam

# Directory processing
gleam run -m protozoa -- ./proto/ ./src/generated/

# Status checking
gleam run -m protozoa -- --check ./proto/

Supported Features

Protocol Buffer Features

FeatureSupportDescription
Messagesβœ… CompleteMessage definitions with all field types
Enumsβœ… CompleteEnum definitions with proper value handling
Servicesβœ… CompletegRPC service definitions with streaming support
Nested Typesβœ… CompleteMessages and enums nested within messages
Oneofsβœ… CompleteUnion types with proper variant handling
Mapsβœ… CompleteMap fields with Dict support
Repeated Fieldsβœ… CompleteList/array fields with proper encoding
Import Systemβœ… CompleteCross-file dependencies and path resolution
Well-Known Typesβœ… CompleteGoogle’s standard types auto-imported
Field Optionsβœ… Completedeprecated, json_name, packed options

RPC/Service Features

Streaming TypeSyntaxSupportGenerated Code
Unaryrpc Method(Request) returns (Response)βœ…Client/server stubs
Server Streamingrpc Method(Request) returns (stream Response)βœ…Streaming interface
Client Streamingrpc Method(stream Request) returns (Response)βœ…Streaming interface
Bidirectionalrpc Method(stream Request) returns (stream Response)βœ…Full duplex interface

Field Types

Proto TypeGleam TypeWire TypeSupport
boolBoolVarintβœ…
int32, sint32IntVarintβœ…
int64, sint64IntVarintβœ…
uint32, uint64IntVarintβœ…
fixed32, sfixed32IntFixed32βœ…
fixed64, sfixed64IntFixed64βœ…
floatFloatFixed32βœ…
doubleFloatFixed64βœ…
stringStringLength-delimitedβœ…
bytesBitArrayLength-delimitedβœ…
Message typesCustom typesLength-delimitedβœ…
Enum typesCustom typesVarintβœ…

Generated Code Examples

Messages with Field Options

syntax = "proto3";
package example;

message User {
  string name = 1;
  int32 age = 2 [deprecated = true];
  string email = 3 [json_name = "email_address"];
  repeated int32 scores = 4 [packed = true];
}

Generates:

pub type User {
  User(
    name: String,
    age: Int, // @deprecated: This field is deprecated
    email: String,
    scores: List(Int),
  )
}

pub fn encode_user(user: User) -> BitArray {
  // ... encoding implementation
}

pub fn user_decoder() -> decode.Decoder(User) {
  // ... decoding implementation
}

Services with Streaming

syntax = "proto3";
package example;

service UserService {
  rpc GetUser(GetUserRequest) returns (GetUserResponse);
  rpc StreamUsers(StreamRequest) returns (stream GetUserResponse);
  rpc UploadData(stream UploadRequest) returns (UploadResponse);
  rpc Chat(stream ChatMessage) returns (stream ChatMessage);
}

Generates:

/// Client interface for UserService service
pub type UserServiceClient {
  UserServiceClient(
    endpoint: String,
  )
}

/// Server interface for UserService service
pub type UserServiceServer {
  UserServiceServer(
    // Server implementation fields
  )
}

// Method signatures for client:
  // GetUser(GetUserRequest) -> GetUserResponse // Unary call
  // StreamUsers(StreamRequest) -> GetUserResponse // Server streaming
  // UploadData(UploadRequest) -> UploadResponse // Client streaming
  // Chat(ChatMessage) -> ChatMessage // Bidirectional streaming

Well-Known Types

syntax = "proto3";
package example;

import "google/protobuf/timestamp.proto";
import "google/protobuf/duration.proto";

message Event {
  string name = 1;
  google.protobuf.Timestamp created_at = 2;
  google.protobuf.Duration duration = 3;
}

Generates with automatic imports:

pub type Event {
  Event(
    name: String,
    created_at: Timestamp,
    duration: Duration,
  )
}

Development Status

Recently Completed βœ…

High Priority Roadmap

Medium Priority

Low Priority

Technical Details

Contributing

Contributions are welcome! Please feel free to submit issues, feature requests, or pull requests.

License

This project is licensed under the MIT License.

✨ Search Document