bufbuild / protobuf-es

Protocol Buffers for ECMAScript. The only JavaScript Protobuf library that is fully-compliant with Protobuf conformance tests.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

How to use enums ?

Sceat opened this issue · comments

I'm confused on how we can use enum string values instead of integer ?

syntax = "proto3";
package test

message Packet {
	oneof type {
		HelloPacket hello = 1;
	}
}

enum SomeEnum {	A = 0; }
message HelloPacket { SomeEnum letter = 1; }
import * as Packets from 'test_pb.js'

const buffer = Packets.Packet.fromJson({ helloPacket: { letter: 'A' } }).toBinary()
const { type: { value, case } } = Packets.Packet.fromBinary(buffer)

Here value.letter will be 0 and I'd like to find A instead, how can I do ?

const valueLetterEnumLabel = Packets.SomeEnum[value.letter];

The enum value is always going to be the thing on the right hand side. The enum value label is the thing on the left hand side. This is just how TypeScript enums work:

enum SomeEnum {	A = 0; }

Will generate:

var SomeEnum;
(function (SomeEnum) {
    SomeEnum[SomeEnum["A"] = 0] = "A";
})(SomeEnum || (SomeEnum = {}));

See this playground link.

Edit: Ignore what I said about TS enums. I got my protobuf es/ts libraries confused 😅

Thanks @jcready for sure but as per my example I use a generic approach with oneof where I don't have knowledge of what I receive, I don't know if I was supposed to do it that way but I'm sending packets through websocket and when I receive the buffer, I want to convert it back to JSON including enums so I can't do Packets.SomeEnum because I won't know the content of the packet unless my proto file only contains a single big Enum for all values.

websocket.on('message', event => {
	try {
    	const { type: { value, case: name } } = Packets.Packet.fromBinary(new Uint8Array(event.data))

     	if (!type) throw new Error('Invalid packet')

		const received_packet = { name, value }
      	// handle packet elsewhere { name: 'helloPacket', value: { letter: 0 } }
})

To answer your original question, there are two ways to find A based on what you have:

  1. You can use the value to look up the name (docs)

  2. Convert the message to a JSON object and read it that way. By default, the name of the enum value is used when serializing to JSON. Note that you have to use the emitDefaultValues: true option in your example. This is because 'A' is the default value of your enum. Without that option, you won't see the value in your serialized JSON output.

const buffer = Packets.Packet.fromJson({ hello: { letter: "A" }}).toBinary();
const packet = Packets.Packet.fromBinary(buffer);

// 1st approach using enum
const { type: { value }} = packet;
console.log(Packets.SomeEnum[value.letter]); // A

// 2nd approach using JSON output
const packetJson = packet.toJson({ emitDefaultValues: true });
console.log(packetJson.hello.letter);  // A

I wasn't even using the .toJson() that's great, and it works indeed with the emitDefaultValues option thanks