如何仅使用一个 TypeMapper 将任何 protobuf Enum 生成为 String?

How to generate any protobuf Enum as String with only one TypeMapper?

例如,我的原型模式中有一些枚举:

enum E1 {
     UNKNOWN = 0;
     OPTION_1 = 1;
     OPTION_2 = 2;
     OPTION_3 = 3;
}

enum E2 {
     UNKNOWN = 0;
     ANOTHER_OPTION_1 = 1;
     ANOTHER_OPTION_2 = 2;
     ANOTHER_OPTION_3 = 3;
}

message M {
    E1 my_enum_1 = 1;
    E2 my_enum_2 = 2;
}

我可以通过提供 scalaPB TypeMappers:

使用字符串而不是枚举生成 scala 类
TypeMapper(_.name)(E1.fromName(_).get)
TypeMapper(_.name)(E2.fromName(_).get)

但我不想为任何单个枚举复制粘贴相同的 TypeMappers

有什么方法可以为所有 scalaPB 的枚举只创建一个 TypeMapper?

您无法完全避免为每个 enum 类型定义 TypeMapper。但是您可以创建一个辅助方法来创建 TypeMapper 个实例。

例如

import scalapb.GeneratedEnum
import scalapb.GeneratedEnumCompanion
import scalapb.TypeMapper

def enumMapper[E <: GeneratedEnum: GeneratedEnumCompanion] = {
  TypeMapper[E, String](_.name)(implicitly[GeneratedEnumCompanion[E]].fromName(_).get)
}

---更新---

根据@thesamet 的评论,如果您将 implicit 添加到上述方法并在您的隐式上下文中使用它,它将起作用。

这可以用下面编译的代码进行测试

implicit def enumMapper...

val Enum1Mapper = implicitly[TypeMapper[E1]]

是的,这是可以做到的。

如果你想让 ScalaPB 对一个包中的所有枚举使用字符串,你可以添加 package-scoped options file to tell ScalaPB to set the scala-type for each enum to a String using field transformations:

// options.proto:
syntax = "proto3";

import "scalapb/scalapb.proto";

option (scalapb.options) = {
  scope: PACKAGE,
  field_transformations : [
    {
      when : {
        type: TYPE_ENUM
      }
      set : {[scalapb.field] {type : 'String'}}
    }
}

下一步是为所有枚举定义类型映射器。借用 Ivan 的回答,这可以定义如下:

implicit def enumMapper[E <: GeneratedEnum](implicit ec: GeneratedEnumCompanion[E]) =
  TypeMapper[E, String](_.name)(ec.fromName(_).get)

如果将上述代码添加到与生成代码相同的包中的包对象,使用ScalaPB中的import选项导入,类型映射器将在隐式搜索中找到。