Skip to content

Commit 621931c

Browse files
committed
First
0 parents  commit 621931c

34 files changed

Lines changed: 5276 additions & 0 deletions

.claude/settings.local.json

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
{
2+
"permissions": {
3+
"allow": [
4+
"Bash(gleam test:*)",
5+
"Bash(gleam run:*)",
6+
"Bash(gleam check:*)",
7+
"Bash(grep:*)",
8+
"Bash(sed:*)",
9+
"Bash(gleam build:*)",
10+
"Bash(cat:*)",
11+
"Read(//usr/**)",
12+
"Bash(find:*)",
13+
"Bash(chmod:*)",
14+
"Bash(./test/cleanup.sh:*)",
15+
"Bash(./scripts/setup_tests.sh:*)",
16+
"Bash(./gloto:*)",
17+
"Bash(./scripts/clean_test_files.sh:*)",
18+
"Bash(./scripts/setup_test_files.sh:*)",
19+
"Bash(mkdir:*)",
20+
"Bash(bash:*)",
21+
"WebFetch(domain:hexdocs.pm)",
22+
"Bash(rm:*)",
23+
"Bash(rg:*)"
24+
],
25+
"deny": [],
26+
"ask": []
27+
}
28+
}

.githooks/pre-commit

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#!/bin/bash
2+
# Pre-commit hook to prevent committing generated test files
3+
4+
# Check if any files in build/test/ are staged
5+
if git diff --cached --name-only | grep -q "^build/test/"; then
6+
echo "Error: Attempting to commit generated test files from build/test/"
7+
echo "These files should not be committed to the repository."
8+
echo "Run 'git reset HEAD build/test/' to unstage them."
9+
exit 1
10+
fi
11+
12+
# Check if any _pb.gleam files in src/ are staged (except example_pb.gleam and test_optional_pb.gleam)
13+
if git diff --cached --name-only | grep -E "^src/.*_pb\.gleam$" | grep -v "example_pb.gleam" | grep -v "test_optional_pb.gleam"; then
14+
echo "Warning: You are committing generated _pb.gleam files in src/"
15+
echo "Consider moving test-generated files to build/test/ instead."
16+
read -p "Continue anyway? (y/n) " -n 1 -r
17+
echo
18+
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
19+
exit 1
20+
fi
21+
fi
22+
23+
exit 0

.github/workflows/test.yml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
name: test
2+
3+
on:
4+
push:
5+
branches:
6+
- master
7+
- main
8+
pull_request:
9+
10+
jobs:
11+
test:
12+
runs-on: ubuntu-latest
13+
steps:
14+
- uses: actions/checkout@v4
15+
- uses: erlef/setup-beam@v1
16+
with:
17+
otp-version: "28"
18+
gleam-version: "1.12.0-nightly-20250818"
19+
rebar3-version: "3"
20+
# elixir-version: "1"
21+
- run: gleam deps download
22+
- run: gleam test
23+
- run: gleam format --check src test

.gitignore

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
*.beam
2+
*.ez
3+
/build
4+
erl_crash.dump
5+
6+
# Test-generated files - kept in test/ dir to avoid cluttering src/
7+
test/*_pb.gleam
8+
test/.generated/
9+
build/test_gen/
10+
dev/

Makefile

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
.PHONY: test clean build all setup-test
2+
3+
# Default target
4+
all: build
5+
6+
# Build the project
7+
build:
8+
gleam build
9+
10+
# Setup test environment by generating test proto files
11+
setup-test:
12+
@echo "Setting up test environment..."
13+
@./scripts/setup_tests.sh
14+
15+
# Run tests with proper setup and cleanup
16+
test: setup-test
17+
@echo "Running tests..."
18+
gleam test
19+
@echo "Cleaning up test files..."
20+
@rm -rf build/test_gen
21+
22+
# Clean all build artifacts and generated test files
23+
clean:
24+
@echo "Cleaning build artifacts..."
25+
@rm -rf build/
26+
@rm -rf _build/
27+
@echo "Clean complete"
28+
29+
# Generate a proto file (usage: make generate INPUT=file.proto OUTPUT=file_pb.gleam)
30+
generate:
31+
@if [ -z "$(INPUT)" ] || [ -z "$(OUTPUT)" ]; then \
32+
echo "Usage: make generate INPUT=file.proto OUTPUT=file_pb.gleam"; \
33+
exit 1; \
34+
fi
35+
gleam run -m gloto -- generate $(INPUT) $(OUTPUT)
36+
37+
# Check code formatting and linting
38+
check:
39+
gleam check
40+
gleam format --check src test
41+
42+
# Format code
43+
format:
44+
gleam format src test
45+
46+
# Show help
47+
help:
48+
@echo "Available targets:"
49+
@echo " all - Build the project (default)"
50+
@echo " build - Build the project"
51+
@echo " test - Run tests with automatic setup/cleanup"
52+
@echo " clean - Remove all build artifacts"
53+
@echo " generate - Generate Gleam code from proto file"
54+
@echo " check - Run code checks and formatting verification"
55+
@echo " format - Format the code"
56+
@echo " help - Show this help message"

README.md

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
# gloto
2+
3+
A Protocol Buffers library for Gleam, providing encoding and decoding of protobuf messages.
4+
5+
[![Package Version](https://img.shields.io/hexpm/v/gloto)](https://hex.pm/packages/gloto)
6+
[![Hex Docs](https://img.shields.io/badge/hex-docs-ffaff3)](https://hexdocs.pm/gloto/)
7+
8+
## Installation
9+
10+
```sh
11+
gleam add gloto@1
12+
```
13+
14+
## Usage
15+
16+
### Encoding Messages
17+
18+
```gleam
19+
import gloto
20+
import gleam/int
21+
import gleam/io
22+
23+
pub fn encode_example() {
24+
// Create a protobuf message with multiple fields
25+
let message = gloto.build_message([
26+
gloto.encode_int32(1, 42), // Field 1: int32
27+
gloto.encode_string(2, "Hello"), // Field 2: string
28+
gloto.encode_bool(3, True), // Field 3: bool
29+
])
30+
31+
// message is now a BitArray containing the encoded protobuf
32+
message
33+
}
34+
```
35+
36+
### Decoding Messages
37+
38+
```gleam
39+
import gloto
40+
import gleam/int
41+
import gleam/io
42+
43+
pub fn decode_example(data: BitArray) {
44+
case gloto.decode(data) {
45+
Ok(fields) -> {
46+
// Find specific fields by number
47+
case gloto.find_field(fields, 1) {
48+
Ok(field) -> {
49+
case gloto.decode_varint_value(field) {
50+
Ok(value) -> io.println("Field 1: " <> int.to_string(value))
51+
Error(_) -> io.println("Failed to decode field 1")
52+
}
53+
}
54+
Error(_) -> io.println("Field 1 not found")
55+
}
56+
}
57+
Error(_) -> io.println("Failed to decode message")
58+
}
59+
}
60+
```
61+
62+
### Nested Messages
63+
64+
```gleam
65+
import gloto
66+
67+
pub fn nested_message_example() {
68+
// Create an inner message
69+
let inner = gloto.build_message([
70+
gloto.encode_string(1, "inner value"),
71+
gloto.encode_int32(2, 100),
72+
])
73+
74+
// Embed it in an outer message
75+
let outer = gloto.build_message([
76+
gloto.encode_int32(1, 999),
77+
gloto.encode_message_field(2, inner), // Nested message in field 2
78+
])
79+
80+
outer
81+
}
82+
```
83+
84+
### Repeated Fields
85+
86+
Protocol Buffers supports repeated fields. Simply encode multiple values with the same field number:
87+
88+
```gleam
89+
import gloto
90+
91+
pub fn repeated_fields_example() {
92+
let message = gloto.build_message([
93+
gloto.encode_int32(1, 10),
94+
gloto.encode_int32(1, 20), // Same field number
95+
gloto.encode_int32(1, 30), // Creates a repeated field
96+
gloto.encode_string(2, "other field"),
97+
])
98+
99+
// When decoding, use find_all_fields to get all values
100+
case gloto.decode(message) {
101+
Ok(fields) -> {
102+
let repeated = gloto.find_all_fields(fields, 1)
103+
// repeated will contain all three int32 values
104+
repeated
105+
}
106+
Error(_) -> panic
107+
}
108+
}
109+
```
110+
111+
## Supported Types
112+
113+
- **Varint**: int32, int64, bool
114+
- **Fixed32**: 32-bit values with fixed size
115+
- **Fixed64**: 64-bit values with fixed size
116+
- **Length-delimited**: strings, bytes, nested messages
117+
118+
## Architecture
119+
120+
The library is structured into three main modules:
121+
122+
- `gloto/wire` - Wire format types and tag encoding/decoding
123+
- `gloto/encoder` - Functions for encoding various protobuf types
124+
- `gloto/decoder` - Functions for decoding protobuf messages
125+
- `gloto` - High-level API for working with protobuf messages
126+
127+
## Future Plans
128+
129+
- Code generation from .proto files
130+
- gRPC support
131+
- Schema validation
132+
- More comprehensive type support
133+
134+
## Development
135+
136+
```sh
137+
gleam run # Run the project
138+
gleam test # Run the tests
139+
```
140+
141+
Further documentation can be found at <https://hexdocs.pm/gloto>.
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
---
2+
version: 1.4.0
3+
title: Generate all field types
4+
file: ./test/codegen_test.gleam
5+
test_name: field_types_test
6+
---
7+
import gloto/wire
8+
import gloto/decode
9+
import gloto/encode
10+
11+
12+
13+
pub type AllTypes {
14+
AllTypes(
15+
d: Float,
16+
f: Float,
17+
i32: Int,
18+
i64: Int,
19+
b: Bool,
20+
s: String,
21+
by: BitArray
22+
)
23+
}
24+
25+
pub fn encode_alltypes(alltypes: AllTypes) -> BitArray {
26+
encode.message([
27+
encode.double_field(1, alltypes.d),
28+
encode.float_field(2, alltypes.f),
29+
encode.int32_field(3, alltypes.i32),
30+
encode.int64_field(4, alltypes.i64),
31+
encode.bool_field(5, alltypes.b),
32+
encode.string_field(6, alltypes.s),
33+
encode.field(7, wire.LengthDelimited, encode.length_delimited(alltypes.by))
34+
])
35+
}
36+
37+
pub fn alltypes_decoder() -> decode.Decoder(AllTypes) {
38+
use d <- decode.subrecord(decode.double(1))
39+
use f <- decode.subrecord(decode.float(2))
40+
use i32 <- decode.subrecord(decode.int32_with_default(3, 0))
41+
use i64 <- decode.subrecord(decode.int64_with_default(4, 0))
42+
use b <- decode.subrecord(decode.bool_with_default(5, False))
43+
use s <- decode.subrecord(decode.string_with_default(6, ""))
44+
use by <- decode.subrecord(decode.bytes(7))
45+
decode.success(AllTypes(d: d, f: f, i32: i32, i64: i64, b: b, s: s, by: by))
46+
}
47+
48+
pub fn decode_alltypes(data: BitArray) -> Result(AllTypes, decode.DecodeError) {
49+
decode.decode(data, alltypes_decoder())
50+
}
51+
52+

0 commit comments

Comments
 (0)