#protobuf #grpc #tonic #deserialize #convert

gin-tonic

main gin-tonic crate - rust protobuf with gin and tonic

15 releases (4 breaking)

0.5.3 Aug 3, 2024
0.5.2 Aug 2, 2024
0.5.1 Jul 31, 2024
0.4.8 Jul 10, 2024
0.1.0 Jun 11, 2024

#402 in Encoding

39 downloads per month

MIT license

155KB
4K SLoC

crates.io

gin-tonic

gin-tonic offers:

  • a protobuf de-/serialization (like prost)
  • a replacement for prost-build)
  • a tonic codec implementation
  • a wrapper for tonic-build adding some extra extra features

While all this can be achieved using the mentioned crates; gin-tonic also offers traits for converting any Rust type into a protobuf wire type. You are asking why?

If you want to pass a UUID via protobuf you likely end up doing:

message Foo {
  string my_uuid = 1;
}

Using prost-build and tonic-build this will generate the following Rust struct:

struct Foo {
    my_uuid: String,
}

As you notice the Rust type here is String, but in your actual code you want to use an actual uuid::Uuid. Now you have to do a fallible conversion into your code.

gin-tonic solves this by adding options to the protobuf file:

import "gin/proto/gin.proto";

message Foo {
  string my_uuid = 1 [(gin_tonic.v1.rust_type) = "uuid::Uuid"];
}

Using the gin-tonic code generator this generates the following Rust code:

struct Foo {
    my_uuid: uuid::Uuid,
}

For the UUID case gin-tonic offers two features:

  • uuid_string => proto transport is string, parsing error is handled within wire type conversion
  • uuid_bytes => proto transport is bytes, this does not require additional error handling

You can add you own types by implementing the PbType trait for your type.

Benchmarks

gin tonic:

decode                  time:   [699.72 ns 700.71 ns 701.81 ns]
encode                  time:   [451.35 ns 453.22 ns 455.56 ns]

prost:

decode                  time:   [778.30 ns 782.24 ns 788.19 ns]
encode                  time:   [622.77 ns 623.87 ns 625.02 ns]

Dependencies

~9–16MB
~172K SLoC