Decode the protobuf wire format in Gleam!
3

Configure Feed

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

Decode len field

Gavin Morrow (Sep 18, 2025, 3:21 PM EDT) ba88a40a fdac67e8

+38 -2
+22 -1
src/protobuf_decode_gleam.gleam
··· 36 36 InvalidVarInt(leftover_bits: BitArray, acc: BitArray) 37 37 InvalidI64(bits: BitArray) 38 38 UnableToDecode(List(decode.DecodeError)) 39 + InvalidLen(len: Int, value_bits: BitArray) 39 40 } 40 41 41 42 fn read_fields( ··· 68 69 let read_fn: fn(BitArray) -> ValueResult = case wire_type { 69 70 VarInt -> read_varint 70 71 I64 -> read_i64 71 - Len -> todo 72 + Len -> read_len 72 73 I32 -> todo 73 74 } 74 75 use #(value, rest): #(Dynamic, BitArray) <- result.try({ ··· 104 105 <<i64:bits-size(64), rest:bytes>> -> Ok(#(i64, rest)) 105 106 bits -> Error(InvalidI64(bits:)) 106 107 } 108 + } 109 + 110 + fn read_len(bits: BitArray) -> ValueResult { 111 + // First, read the length of the value 112 + // It is encoded as a varint immediately after the tag 113 + use #(len, bits) <- result.try(read_varint(bits)) 114 + let len = bit_array_to_uint(len) 115 + 116 + // Just decoded a uint, so should be safe 117 + assert len > 0 118 + 119 + use value <- result.try( 120 + bit_array.slice(from: bits, at: 0, take: len) 121 + |> result.map_error(fn(_) { InvalidLen(len:, value_bits: bits) }), 122 + ) 123 + // Assert b/c if the len was too long, it would've errored in the prev slice 124 + let assert Ok(rest) = 125 + bit_array.slice(from: bits, at: len, take: bit_array.byte_size(bits) - len) 126 + 127 + Ok(#(value, rest)) 107 128 } 108 129 109 130 pub fn decode_uint() -> Decoder(Int) {
+16 -1
test/protobuf_decode_gleam_test.gleam
··· 13 13 } 14 14 15 15 fn person_decoder() -> Decoder(Person) { 16 + let person_inner_decoder = { 17 + use bits <- decode.then(decode.bit_array) 18 + 19 + let person = decode(from: bits, using: person_decoder()) 20 + case person { 21 + Ok(person) -> decode.success(person) 22 + Error(_) -> 23 + decode.failure( 24 + Person(id: 0, age: 0, score: 0, self: option.None), 25 + "Person", 26 + ) 27 + } 28 + } 29 + 16 30 use id <- decode.field(3, decode_u64()) 17 31 use age <- decode.field(1, decode_uint()) 18 32 use score <- decode.field(2, decode_uint()) 19 33 use person <- decode.optional_field( 20 34 4, 21 35 option.None, 22 - person_decoder() |> decode.map(option.Some), 36 + decode.optional(person_inner_decoder), 23 37 ) 38 + 24 39 decode.success(Person(id:, age:, score:, self: person)) 25 40 } 26 41