Decode the protobuf wire format in Gleam!
3

Configure Feed

Select the types of activity you want to include in your feed.

Support i32s

Gavin Morrow (Sep 18, 2025, 3:28 PM EDT) 3cb9247f ba88a40a

+22 -15
+12 -10
src/protobuf_decode_gleam.gleam
··· 34 34 pub type DecodeError { 35 35 UnknownWireType(Int) 36 36 InvalidVarInt(leftover_bits: BitArray, acc: BitArray) 37 - InvalidI64(bits: BitArray) 37 + InvalidFixed(size: Int, bits: BitArray) 38 38 UnableToDecode(List(decode.DecodeError)) 39 39 InvalidLen(len: Int, value_bits: BitArray) 40 40 } ··· 68 68 69 69 let read_fn: fn(BitArray) -> ValueResult = case wire_type { 70 70 VarInt -> read_varint 71 - I64 -> read_i64 71 + I64 -> read_fixed(64) 72 72 Len -> read_len 73 - I32 -> todo 73 + I32 -> read_fixed(32) 74 74 } 75 75 use #(value, rest): #(Dynamic, BitArray) <- result.try({ 76 76 use value <- result.map(read_fn(bits)) ··· 100 100 } 101 101 } 102 102 103 - fn read_i64(bits: BitArray) -> ValueResult { 104 - case bits { 105 - <<i64:bits-size(64), rest:bytes>> -> Ok(#(i64, rest)) 106 - bits -> Error(InvalidI64(bits:)) 103 + fn read_fixed(size: Int) -> fn(BitArray) -> ValueResult { 104 + fn(bits: BitArray) -> ValueResult { 105 + case bits { 106 + <<num:bits-size(size), rest:bytes>> -> Ok(#(num, rest)) 107 + bits -> Error(InvalidFixed(size:, bits:)) 108 + } 107 109 } 108 110 } 109 111 ··· 132 134 bit_array_to_uint(bits) |> decode.success 133 135 } 134 136 135 - pub fn decode_u64() -> Decoder(Int) { 137 + pub fn decode_fixed(size: Int) -> Decoder(Int) { 136 138 use bits <- decode.then(decode.bit_array) 137 - let assert <<u64:unsigned-little-size(64)>> = bits 138 - u64 |> decode.success 139 + let assert <<num:unsigned-little-size(size)>> = bits 140 + num |> decode.success 139 141 } 140 142 141 143 fn bit_array_to_uint(bits: BitArray) -> Int {
test/person.pb

This is a binary file and will not be displayed.

+2
test/person.protosope.txt
··· 5 5 1: 150 6 6 2: 81050 7 7 3: 42i64 8 + 5: 22i32 8 9 } 10 + 5: 22i32
+8 -5
test/protobuf_decode_gleam_test.gleam
··· 1 1 import gleam/dynamic/decode.{type Decoder} 2 2 import gleam/option 3 3 import gleeunit 4 - import protobuf_decode_gleam.{decode, decode_u64, decode_uint} 4 + import protobuf_decode_gleam.{decode, decode_fixed, decode_uint} 5 5 import simplifile as file 6 6 7 7 pub fn main() -> Nil { ··· 9 9 } 10 10 11 11 type Person { 12 - Person(id: Int, age: Int, score: Int, self: option.Option(Person)) 12 + Person(id: Int, age: Int, score: Int, self: option.Option(Person), day: Int) 13 13 } 14 14 15 15 fn person_decoder() -> Decoder(Person) { ··· 21 21 Ok(person) -> decode.success(person) 22 22 Error(_) -> 23 23 decode.failure( 24 - Person(id: 0, age: 0, score: 0, self: option.None), 24 + Person(id: 0, age: 0, score: 0, self: option.None, day: 0), 25 25 "Person", 26 26 ) 27 27 } 28 28 } 29 29 30 - use id <- decode.field(3, decode_u64()) 30 + use id <- decode.field(3, decode_fixed(64)) 31 31 use age <- decode.field(1, decode_uint()) 32 32 use score <- decode.field(2, decode_uint()) 33 33 use person <- decode.optional_field( ··· 35 35 option.None, 36 36 decode.optional(person_inner_decoder), 37 37 ) 38 + use day <- decode.field(5, decode_fixed(32)) 38 39 39 - decode.success(Person(id:, age:, score:, self: person)) 40 + decode.success(Person(id:, age:, score:, self: person, day:)) 40 41 } 41 42 42 43 pub fn person_pb_test() { ··· 53 54 age: 150, 54 55 score: 81_050, 55 56 self: option.None, 57 + day: 22, 56 58 )), 59 + day: 22, 57 60 ) 58 61 } 59 62