protobuf

Protocol Buffers binary serialization for Kit

Files

FileDescription
.editorconfigEditor formatting configuration
.gitignoreGit ignore rules for build artifacts and dependencies
.tool-versionsasdf tool versions (Zig, Kit)
LICENSEMIT license file
README.mdThis file
examples/basic.kitBasic usage example
examples/nested.kitNested message example
examples/person.kitComplete person/address-book style example
examples/repeated.kitRepeated and packed field example
kit.tomlPackage manifest with metadata and dependencies
src/protobuf.kitKit Protobuf - Protocol Buffers binary serialization
tests/protobuf.test.kitAPI and behavior tests
tests/types.test.kitType and constructor tests

Dependencies

No Kit package dependencies.

This is a pure Kit package. It does not require protoc, generated code, or native FFI libraries.

Installation

kit add gitlab.com/kit-lang/packages/kit-protobuf.git

Usage

import Kit.Protobuf as Protobuf

main = fn =>
  # Build a message with:
  # field 1: int32 id = 150
  # field 2: string name = "testing"
  message = [
    Protobuf.field 1 (Protobuf.int 150), 
    Protobuf.field 2 (Protobuf.string "testing")
  ]

  # Encode fields to protobuf wire-format bytes
  bytes = Protobuf.encode message
  println "Encoded: ${Protobuf.bytes-to-hex bytes}"

  # Decode bytes back into field records
  match Protobuf.decode bytes
    | Err err ->
      println "Decode error: ${err}"
    | Ok fields ->
      match Protobuf.get-field fields 1
        | Some value ->
          match Protobuf.as-int value
            | Some id -> println "id: ${Int.to-string id}"
            | None -> println "field 1 is not an int"
        | None -> println "field 1 not found"

      match Protobuf.get-field fields 2
        | Some value ->
          match Protobuf.as-string value
            | Some name -> println "name: ${name}"
            | None -> println "field 2 is not a string"
        | None -> println "field 2 not found"

main

Nested Messages

import Kit.Protobuf as Protobuf

address = [
  Protobuf.field 1 (Protobuf.string "123 Main St"), 
  Protobuf.field 2 (Protobuf.string "Springfield")
]

person = [
  Protobuf.field 1 (Protobuf.int 42), 
  Protobuf.field 2 (Protobuf.string "Alice"), 
  Protobuf.field 3 (Protobuf.message address)
]

bytes = Protobuf.encode person

Repeated Fields

import Kit.Protobuf as Protobuf

# Non-packed repeated strings are encoded as repeated fields with the same number.
tags = [
  Protobuf.field 1 (Protobuf.string "kit"), 
  Protobuf.field 1 (Protobuf.string "protobuf")
]

# Packed repeated integers are encoded as one length-delimited field.
numbers = [
  Protobuf.field 2 (Protobuf.packed-ints [1, 2, 3, 100, 300])
]

API Notes

encode accepts a List Field and returns encoded bytes as List Int.

decode, as-message, and as-packed-ints return Kit Result values using Ok and Err.

Field values are built with helpers such as int, uint, sint, bool, enum, fixed32, fixed64, string, bytes, message, repeated-values, and packed-ints.

Decoded values can be extracted with as-int, as-sint, as-bool, as-fixed32, as-fixed64, as-bytes, as-string, as-message, and as-packed-ints.

Length-delimited fields decode to BytesValue by default. Use as-string for UTF-8/ASCII string fields, or as-message for embedded messages.

The implementation supports protobuf wire types for varint, fixed32, fixed64, and length-delimited values. Deprecated group wire types are recognized but return decode errors.

Development

Running Examples

Run examples with the interpreter:

kit run examples/basic.kit
kit run examples/nested.kit
kit run examples/repeated.kit
kit run examples/person.kit

Compile an example to a native binary:

kit build examples/basic.kit -o basic && ./basic

Running Tests

Run the test suite:

kit test

Run the test suite with coverage:

kit test --coverage

Running kit dev

Run the standard development workflow (format, check, and test):

kit dev

This will:

  1. Check formatting
  2. Type check source and examples
  3. Run tests with coverage

Running Parity Checks

Compare interpreter output against compiled binary output for examples:

kit parity --failures-only

Use --no-spinner in CI or scripted runs:

kit parity --no-spinner --failures-only

Generating Documentation

Generate API documentation from doc comments:

kit doc

Note: Kit sources with doc comments (##) will generate HTML documents in docs/*.html.

Cleaning Build Artifacts

Remove generated files, caches, and build artifacts:

kit task clean

Note: Defined in kit.toml.

Local Installation

To install this package locally for development:

kit install

This installs the package to ~/.kit/packages/@kit/protobuf/, making it available for import as Kit.Protobuf in other projects.

When changing src/protobuf.kit, reinstall or otherwise refresh the local package copy before testing examples that import Kit.Protobuf.

License

This package is released under the MIT License - see LICENSE for details.

Exported Functions & Types

ProtobufError

Protobuf error type with specific variants for different failure modes.

Variants

ProtobufEncodeError {message}
ProtobufDecodeError {message}

WireType

Protocol buffer wire types (encoding format identifiers).

Variants

Varint
Variable-length integer encoding (int32, int64, uint32, uint64, sint32, sint64, bool, enum)
Fixed64
64-bit little-endian encoding (fixed64, sfixed64, double)
LengthDelim
Length-prefixed data (string, bytes, embedded messages, packed repeated)
StartGroup
Deprecated group start marker (not supported)
EndGroup
Deprecated group end marker (not supported)
Fixed32
32-bit little-endian encoding (fixed32, sfixed32, float)

FieldValue

Field value variants for different protobuf types.

Variants

VarintValue {Int}
Variable-length integer value
Fixed32Value {Int}
32-bit fixed-width value
Fixed64Value {Int}
64-bit fixed-width value
BytesValue {List, Int}
Raw byte sequence
StringValue {String}
UTF-8 string value
MessageValue {List, Field}
Nested message (list of fields)
RepeatedValue {List, FieldValue}
Non-packed repeated field
PackedValue {List, Int}
Packed repeated integers

IntDecodeResult

Internal result types for decoding with remaining bytes.

Variants

IntDecodeOk {IntWithRest}
IntDecodeErr {String}

FieldType

Field type descriptors for schema definitions.

Variants

TInt32
Signed 32-bit integer
TInt64
Signed 64-bit integer
TUint32
Unsigned 32-bit integer
TUint64
Unsigned 64-bit integer
TSint32
ZigZag-encoded signed 32-bit integer
TSint64
ZigZag-encoded signed 64-bit integer
TBool
Boolean value (encoded as varint 0/1)
TEnum
Enumeration value (encoded as varint)
TFixed32
32-bit fixed-width integer
TSfixed32
Signed 32-bit fixed-width integer
TFloat
32-bit floating point (encoded as fixed32)
TFixed64
64-bit fixed-width integer
TSfixed64
Signed 64-bit fixed-width integer
TDouble
64-bit floating point (encoded as fixed64)
TString
UTF-8 string
TBytes
Arbitrary byte sequence
TMessage
Nested message
TRepeated {FieldType}
Non-packed repeated field
TPacked {FieldType}
Packed repeated field (varint encoding)

varint

WireType

fixed64

WireType

length-delim

WireType

fixed32

WireType

int32

FieldType

int64

FieldType

uint32

FieldType

uint64

FieldType

sint32

FieldType

sint64

FieldType

bool-type

FieldType

enum-type

FieldType

fixed32-type

FieldType

sfixed32

FieldType

float-type

FieldType

fixed64-type

FieldType

sfixed64

FieldType

double-type

FieldType

string-type

FieldType

bytes-type

FieldType

message-type

FieldType

repeated

FieldType -> FieldType

packed

FieldType -> FieldType

field-def

PositiveInt -> NonEmptyString -> FieldType -> FieldDef

message-def

NonEmptyString -> List FieldDef -> MessageDef

encode-field

Encode a single field to bytes.

Parameters:

Returns:

PositiveInt -> FieldValue -> List Int

bytes = encode-field 1 (int 42)
# Returns: [8, 42] (tag for field 1 varint, value 42)

encode

Encode a complete protobuf message to bytes.

Parameters:

Returns:

List Field -> List Int

fields = [field 1 (int 42), field 2 (string "hello")]
bytes = encode fields

decode

Decode a complete protobuf message from bytes.

Parameters:

Returns:

List Int -> Result (List Field) ProtobufError

match decode bytes
  | Ok fields -> print "Decoded ${Int.to-string (List.length fields)} fields"
  | Err err -> print "Error: ${err}"

int

Create a varint integer field value (int32/int64/uint32/uint64).

Parameters:

Returns:

Int -> FieldValue

uint

Create an unsigned varint integer field value (alias for int).

Parameters:

Returns:

Int -> FieldValue

sint

Create a signed varint integer field value with zigzag encoding (sint32/sint64).

Parameters:

Returns:

Int -> FieldValue

bool

Create a boolean field value (encoded as varint 0 or 1).

Parameters:

Returns:

Bool -> FieldValue

enum

Create an enum field value (encoded as varint).

Parameters:

Returns:

Int -> FieldValue

fixed32

Create a fixed32 field value (fixed32/sfixed32/float).

Parameters:

Returns:

Int -> FieldValue

sfixed32

Create a signed fixed32 field value (alias for fixed32).

Parameters:

Returns:

Int -> FieldValue

fixed64

Create a fixed64 field value (fixed64/sfixed64/double).

Parameters:

Returns:

Int -> FieldValue

sfixed64

Create a signed fixed64 field value (alias for fixed64).

Parameters:

Returns:

Int -> FieldValue

string

Create a string field value.

Parameters:

Returns:

String -> FieldValue

bytes

Create a bytes field value.

Parameters:

Returns:

List Int -> FieldValue

message

Create a nested message field value.

Parameters:

Returns:

List Field -> FieldValue

repeated-values

Create a repeated field value (non-packed).

Parameters:

Returns:

List FieldValue -> FieldValue

packed-ints

Create a packed repeated integer field value.

Parameters:

Returns:

List Int -> FieldValue

get-field

Get a field by number from decoded message fields.

Parameters:

Returns:

List Field -> PositiveInt -> Option FieldValue

match get-field decoded-fields 1
  | Some value -> print "Found field 1"
  | None -> print "Field 1 not found"

get-repeated

Get all fields with a given number (for repeated fields).

Parameters:

Returns:

List Field -> PositiveInt -> List FieldValue

repeated-values = get-repeated decoded-fields 3
print "Found ${Int.to-string (List.length repeated-values)} values"

as-int

Extract varint as integer.

Parameters:

Returns:

FieldValue -> Option Int

as-sint

Extract varint as signed integer (with zigzag decoding).

Parameters:

Returns:

FieldValue -> Option Int

as-bool

Extract varint as boolean.

Parameters:

Returns:

FieldValue -> Option Bool

as-fixed32

Extract fixed32 value.

Parameters:

Returns:

FieldValue -> Option Int

as-fixed64

Extract fixed64 value.

Parameters:

Returns:

FieldValue -> Option Int

as-bytes

Extract bytes value.

Parameters:

Returns:

FieldValue -> Option (List Int)

as-string

Extract string value.

Parameters:

Returns:

FieldValue -> Option String

as-message

Extract embedded message as fields.

Parameters:

Returns:

FieldValue -> Result (List Field) ProtobufError

as-packed-ints

Extract packed repeated integers.

Parameters:

Returns:

FieldValue -> Result (List Int) ProtobufError

field

Create a Field record from field number and value.

Parameters:

Returns:

PositiveInt -> FieldValue -> Field

f = field 1 (int 42)
# Creates: { number: 1, wire-type: Varint, value: VarintValue 42 }

bytes-to-hex

Format bytes as hex string for debugging.

Parameters:

Returns:

List Int -> String

hex = bytes-to-hex [8, 42, 18, 5]
print "Encoded: ${hex}"

inspect

Print decoded message structure for debugging.

Parameters:

Returns:

List Field -> Unit

inspect decoded-fields
# Prints:
# Field 1:
#   varint: 42
# Field 2:
#   string: "hello"