Generating sources from Protocol Buffers

Getting started

First add the sbt plugin in project/plugins.sbt:

addSbtPlugin("io.higherkindness" % "sbt-mu-srcgen" % "0.31.1")

And enable the plugin on the appropriate project(s):

enablePlugins(SrcGenPlugin)

Once the plugin is enabled, you can configure it by adding a few lines to build.sbt:

import higherkindness.mu.rpc.srcgen.Model._

// Look for .proto files
muSrcGenIdlType := IdlType.Proto

Suppose you want to generate Scala code for a gRPC service based on the following Protobuf IDL file, src/main/resources/hello.proto:

syntax = "proto3";

package foo;

message HelloRequest {
  string arg1 = 1;
  string arg2 = 2;
  repeated string arg3 = 3;
}

message HelloResponse {
  string arg1 = 1;
  string arg2 = 2;
  repeated string arg3 = 3;
}

service ProtoGreeter {
  rpc SayHelloProto (HelloRequest) returns (HelloResponse);
}

You can run the source generator directly:

sbt protocGenerate

or as part of compilation:

sbt compile

Once the source generator has run, there should be some generated Scala file under target/scala-2.13/src_managed/main/foo/hello/.

There will be a separate file for each message class, plus a file for the service definition.

HelloRequest.scala will look roughly like this (tidied up and simplified for readability):

package foo.hello

final case class HelloRequest(
  arg1: String = "",
  arg2: String = "",
  arg3: Seq[String] = Seq.empty
) extends scalapb.GeneratedMessage with scalapb.lenses.Updatable[HelloRequest] {

  // ... lots of generated code

}

Note how each field has a default value, in line with the Protobuf spec.

The service definition in ProtoGreeter.scala will look something like this:

trait ProtoGreeter[F[_]] {
  def SayHelloProto(req: HelloRequest): F[HelloResponse]
}

object ProtoGreeter {

  // ... lots of generated code

}

Custom types

ScalaPB allows you to customise what types are used in the generated code. For example, if you have a string field in your Protobuf message that represents a date, you might want to model it in Scala using java.time.LocalDate.

Take a look at the ScalaPB docs for details on how to achieve this using ScalaPBā€™s TypeMapper mechanism.