How to get a Protobuf enum value from its string representation?
Asked Answered
B

1

10

I can get the string value of a Protobuf enum with this instruction:

str := testPB.Status_ENABLED.String()

How can I perform the inverse operation? (from a string, get the enum element).

Bonin answered 3/8, 2021 at 20:27 Comment(1)
U
11

The generated code has a map called <EnumName>_value of type map[string]int32. Then you can convert the numerical value to the actual defined type:

num := testPB.Status_value[str]
v := testPB.Status(num)

Consider that if the str value doesn't exist in the map (note that it's case sensitive), the map look-up will return 0. Depending on how you defined your protobuffer, the 0 value might be mapped to a an enum instance that does not have "zero" semantics. This is why it is recommended to map 0 to an "unknown" instance:

enum Status {
  UNKNOWN = 0;
  ENABLED = 1;
  // and so on
}

Which in Go correctly yields a makeshift zero-value if the string representation is effectively unknown:

v := testPB.Status(testPB.Status_value["does_not_exist"]) 
fmt.Println(v == testPB.Status_UNKNOWN) // true

Go 1.18

With generics, it is possible to write reusable code to construct protobuffer enums from string values:

func Enum[T ~string, PB ~int32](val T, pbmap map[string]int32, dft PB) PB {
    v, ok := pbmap[string(val)]
    if !ok {
        return dft
    }
    return PB(v)
}

where:

  • the type parameter T is the string representation, which could also be a type with underlying type string, e.g. type MyEnum string
  • the argument pbmap is the <EnumName>_value from the generated protobuffer code
  • the type parameter PB is the protobuffer enum type.

The function above takes a default value of type PB to (obviously) have a fallback in case the string representation is invalid, and also to allow type inference on PB, which otherwise would be used only as a return type.

Usage:

type SomeEnum string

const (
    SomeEnumFoo SomeEnum = "FOO"
    SomeEnumBar SomeEnum = "BAR"
) 

func main() {
    foo := SomeEnumFoo
    v := Enum(foo, pb.SomeEnum_value, pb.SomeEnum_Foo)
    //        ^ T  ^ map[string]int32 ^ default PB value
    // v is type pb.SomeEnum
}
Upbraid answered 3/8, 2021 at 20:42 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.