Skip to content

Commit

Permalink
fix: Support repeated string enums. (#284)
Browse files Browse the repository at this point in the history
* fix: Support repeated string enums.

* Fix packed encoding.

Co-authored-by: kanziw <kanziwoong@gmail.com>
  • Loading branch information
stephenh and kanziw committed Apr 24, 2021
1 parent cf0b200 commit be9ecf7
Show file tree
Hide file tree
Showing 5 changed files with 56 additions and 2 deletions.
6 changes: 5 additions & 1 deletion integration/simple-string-enums/simple-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,17 @@ import { Simple, StateEnum } from './simple';

describe('simple-string-enums', () => {
it('encodes', () => {
const s1: Simple = { name: 'a', state: StateEnum.ON };
const s1: Simple = { name: 'a', state: StateEnum.ON, states: [StateEnum.ON, StateEnum.OFF] };
const b = Simple.encode(s1).finish();
const s2 = Simple.decode(b);
expect(s2).toMatchInlineSnapshot(`
Object {
"name": "a",
"state": "ON",
"states": Array [
"ON",
"OFF",
],
}
`);
});
Expand Down
Binary file modified integration/simple-string-enums/simple.bin
Binary file not shown.
1 change: 1 addition & 0 deletions integration/simple-string-enums/simple.proto
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ package simple;
message Simple {
string name = 1;
StateEnum state = 4;
repeated StateEnum states = 5;
}

enum StateEnum {
Expand Down
36 changes: 35 additions & 1 deletion integration/simple-string-enums/simple.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,10 @@ export function stateEnumToNumber(object: StateEnum): number {
export interface Simple {
name: string;
state: StateEnum;
states: StateEnum[];
}

const baseSimple: object = { name: '', state: StateEnum.UNKNOWN };
const baseSimple: object = { name: '', state: StateEnum.UNKNOWN, states: StateEnum.UNKNOWN };

export const Simple = {
encode(message: Simple, writer: Writer = Writer.create()): Writer {
Expand All @@ -70,13 +71,19 @@ export const Simple = {
if (message.state !== StateEnum.UNKNOWN) {
writer.uint32(32).int32(stateEnumToNumber(message.state));
}
writer.uint32(42).fork();
for (const v of message.states) {
writer.int32(stateEnumToNumber(v));
}
writer.ldelim();
return writer;
},

decode(input: Reader | Uint8Array, length?: number): Simple {
const reader = input instanceof Reader ? input : new Reader(input);
let end = length === undefined ? reader.len : reader.pos + length;
const message = { ...baseSimple } as Simple;
message.states = [];
while (reader.pos < end) {
const tag = reader.uint32();
switch (tag >>> 3) {
Expand All @@ -86,6 +93,16 @@ export const Simple = {
case 4:
message.state = stateEnumFromJSON(reader.int32());
break;
case 5:
if ((tag & 7) === 2) {
const end2 = reader.uint32() + reader.pos;
while (reader.pos < end2) {
message.states.push(stateEnumFromJSON(reader.int32()));
}
} else {
message.states.push(stateEnumFromJSON(reader.int32()));
}
break;
default:
reader.skipType(tag & 7);
break;
Expand All @@ -96,6 +113,7 @@ export const Simple = {

fromJSON(object: any): Simple {
const message = { ...baseSimple } as Simple;
message.states = [];
if (object.name !== undefined && object.name !== null) {
message.name = String(object.name);
} else {
Expand All @@ -106,18 +124,29 @@ export const Simple = {
} else {
message.state = StateEnum.UNKNOWN;
}
if (object.states !== undefined && object.states !== null) {
for (const e of object.states) {
message.states.push(stateEnumFromJSON(e));
}
}
return message;
},

toJSON(message: Simple): unknown {
const obj: any = {};
message.name !== undefined && (obj.name = message.name);
message.state !== undefined && (obj.state = stateEnumToJSON(message.state));
if (message.states) {
obj.states = message.states.map((e) => stateEnumToJSON(e));
} else {
obj.states = [];
}
return obj;
},

fromPartial(object: DeepPartial<Simple>): Simple {
const message = { ...baseSimple } as Simple;
message.states = [];
if (object.name !== undefined && object.name !== null) {
message.name = object.name;
} else {
Expand All @@ -128,6 +157,11 @@ export const Simple = {
} else {
message.state = StateEnum.UNKNOWN;
}
if (object.states !== undefined && object.states !== null) {
for (const e of object.states) {
message.states.push(e);
}
}
return message;
},
};
Expand Down
15 changes: 15 additions & 0 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -793,7 +793,22 @@ function generateEncode(ctx: Context, fullName: string, messageDesc: DescriptorP
${writeSnippet('v!')};
}
`);
} else if (isEnum(field) && options.stringEnums) {
// This is a lot like the `else` clause, but we wrap `fooToNumber` around it.
// Ideally we'd reuse `writeSnippet` here, but `writeSnippet` has the `writer.uint32(tag)`
// embedded inside of it, and we want to drop that so that we can encode it packed
// (i.e. just one tag and multiple values).
const tag = ((field.number << 3) | 2) >>> 0;
const toNumber = getEnumMethod(typeMap, field.typeName, 'ToNumber');
chunks.push(code`
writer.uint32(${tag}).fork();
for (const v of message.${fieldName}) {
writer.${toReaderCall(field)}(${toNumber}(v));
}
writer.ldelim();
`);
} else {
// Ideally we'd reuse `writeSnippet` but it has tagging embedded inside of it.
const tag = ((field.number << 3) | 2) >>> 0;
chunks.push(code`
writer.uint32(${tag}).fork();
Expand Down

0 comments on commit be9ecf7

Please sign in to comment.