diff --git a/integration/remove-enum-prefix-string-enums/parameters.txt b/integration/remove-enum-prefix-string-enums/parameters.txt new file mode 100644 index 000000000..dddba4f35 --- /dev/null +++ b/integration/remove-enum-prefix-string-enums/parameters.txt @@ -0,0 +1 @@ +removeEnumPrefix=true,stringEnums=true,unrecognizedEnum=false diff --git a/integration/remove-enum-prefix-string-enums/remove-enum-prefix-string-enums-test.ts b/integration/remove-enum-prefix-string-enums/remove-enum-prefix-string-enums-test.ts new file mode 100644 index 000000000..8c33aa917 --- /dev/null +++ b/integration/remove-enum-prefix-string-enums/remove-enum-prefix-string-enums-test.ts @@ -0,0 +1,12 @@ +import { EnumFields, Foo, Bar } from "./remove-enum-prefix-string-enums"; + +describe("nestjs-metadata-test", () => { + it("compiles", () => { + const msg: EnumFields = { + foo: Foo.BAR, + bar: Bar.BAZ, + }; + const out = EnumFields.toJSON(msg); + expect(out).not.toBeUndefined(); + }); +}); diff --git a/integration/remove-enum-prefix-string-enums/remove-enum-prefix-string-enums.bin b/integration/remove-enum-prefix-string-enums/remove-enum-prefix-string-enums.bin new file mode 100644 index 000000000..60eb50eb2 Binary files /dev/null and b/integration/remove-enum-prefix-string-enums/remove-enum-prefix-string-enums.bin differ diff --git a/integration/remove-enum-prefix-string-enums/remove-enum-prefix-string-enums.proto b/integration/remove-enum-prefix-string-enums/remove-enum-prefix-string-enums.proto new file mode 100644 index 000000000..2d908f67d --- /dev/null +++ b/integration/remove-enum-prefix-string-enums/remove-enum-prefix-string-enums.proto @@ -0,0 +1,18 @@ +syntax = "proto3"; + +enum Foo { + FOO_UNSPECIFIED = 0; + FOO_BAR = 1; + FOO_BAZ = 2; +} + +enum Bar { + BAR_UNSPECIFIED = 0; + BAZ = 1; + QUX = 2; +} + +message EnumFields { + Foo foo = 1; + Bar bar = 2; +} \ No newline at end of file diff --git a/integration/remove-enum-prefix-string-enums/remove-enum-prefix-string-enums.ts b/integration/remove-enum-prefix-string-enums/remove-enum-prefix-string-enums.ts new file mode 100644 index 000000000..2661afe27 --- /dev/null +++ b/integration/remove-enum-prefix-string-enums/remove-enum-prefix-string-enums.ts @@ -0,0 +1,213 @@ +/* eslint-disable */ +import * as _m0 from "protobufjs/minimal"; + +export const protobufPackage = ""; + +export enum Foo { + UNSPECIFIED = "FOO_UNSPECIFIED", + BAR = "FOO_BAR", + BAZ = "FOO_BAZ", +} + +export function fooFromJSON(object: any): Foo { + switch (object) { + case 0: + case "FOO_UNSPECIFIED": + return Foo.UNSPECIFIED; + case 1: + case "FOO_BAR": + return Foo.BAR; + case 2: + case "FOO_BAZ": + return Foo.BAZ; + default: + throw new tsProtoGlobalThis.Error("Unrecognized enum value " + object + " for enum Foo"); + } +} + +export function fooToJSON(object: Foo): string { + switch (object) { + case Foo.UNSPECIFIED: + return "FOO_UNSPECIFIED"; + case Foo.BAR: + return "FOO_BAR"; + case Foo.BAZ: + return "FOO_BAZ"; + default: + throw new tsProtoGlobalThis.Error("Unrecognized enum value " + object + " for enum Foo"); + } +} + +export function fooToNumber(object: Foo): number { + switch (object) { + case Foo.UNSPECIFIED: + return 0; + case Foo.BAR: + return 1; + case Foo.BAZ: + return 2; + default: + throw new tsProtoGlobalThis.Error("Unrecognized enum value " + object + " for enum Foo"); + } +} + +export enum Bar { + UNSPECIFIED = "BAR_UNSPECIFIED", + BAZ = "BAZ", + QUX = "QUX", +} + +export function barFromJSON(object: any): Bar { + switch (object) { + case 0: + case "BAR_UNSPECIFIED": + return Bar.UNSPECIFIED; + case 1: + case "BAZ": + return Bar.BAZ; + case 2: + case "QUX": + return Bar.QUX; + default: + throw new tsProtoGlobalThis.Error("Unrecognized enum value " + object + " for enum Bar"); + } +} + +export function barToJSON(object: Bar): string { + switch (object) { + case Bar.UNSPECIFIED: + return "BAR_UNSPECIFIED"; + case Bar.BAZ: + return "BAZ"; + case Bar.QUX: + return "QUX"; + default: + throw new tsProtoGlobalThis.Error("Unrecognized enum value " + object + " for enum Bar"); + } +} + +export function barToNumber(object: Bar): number { + switch (object) { + case Bar.UNSPECIFIED: + return 0; + case Bar.BAZ: + return 1; + case Bar.QUX: + return 2; + default: + throw new tsProtoGlobalThis.Error("Unrecognized enum value " + object + " for enum Bar"); + } +} + +export interface EnumFields { + foo: Foo; + bar: Bar; +} + +function createBaseEnumFields(): EnumFields { + return { foo: Foo.UNSPECIFIED, bar: Bar.UNSPECIFIED }; +} + +export const EnumFields = { + encode(message: EnumFields, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.foo !== Foo.UNSPECIFIED) { + writer.uint32(8).int32(fooToNumber(message.foo)); + } + if (message.bar !== Bar.UNSPECIFIED) { + writer.uint32(16).int32(barToNumber(message.bar)); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): EnumFields { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseEnumFields(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 8) { + break; + } + + message.foo = fooFromJSON(reader.int32()); + continue; + case 2: + if (tag !== 16) { + break; + } + + message.bar = barFromJSON(reader.int32()); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): EnumFields { + return { + foo: isSet(object.foo) ? fooFromJSON(object.foo) : Foo.UNSPECIFIED, + bar: isSet(object.bar) ? barFromJSON(object.bar) : Bar.UNSPECIFIED, + }; + }, + + toJSON(message: EnumFields): unknown { + const obj: any = {}; + if (message.foo !== Foo.UNSPECIFIED) { + obj.foo = fooToJSON(message.foo); + } + if (message.bar !== Bar.UNSPECIFIED) { + obj.bar = barToJSON(message.bar); + } + return obj; + }, + + create, I>>(base?: I): EnumFields { + return EnumFields.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): EnumFields { + const message = createBaseEnumFields(); + message.foo = object.foo ?? Foo.UNSPECIFIED; + message.bar = object.bar ?? Bar.UNSPECIFIED; + return message; + }, +}; + +declare const self: any | undefined; +declare const window: any | undefined; +declare const global: any | undefined; +const tsProtoGlobalThis: any = (() => { + if (typeof globalThis !== "undefined") { + return globalThis; + } + if (typeof self !== "undefined") { + return self; + } + if (typeof window !== "undefined") { + return window; + } + if (typeof global !== "undefined") { + return global; + } + throw "Unable to locate global object"; +})(); + +type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined; + +export type DeepPartial = T extends Builtin ? T + : T extends Array ? Array> : T extends ReadonlyArray ? ReadonlyArray> + : T extends {} ? { [K in keyof T]?: DeepPartial } + : Partial; + +type KeysOfUnion = T extends T ? keyof T : never; +export type Exact = P extends Builtin ? P + : P & { [K in keyof P]: Exact } & { [K in Exclude>]: never }; + +function isSet(value: any): boolean { + return value !== null && value !== undefined; +} diff --git a/src/enums.ts b/src/enums.ts index cc5816744..25da83511 100644 --- a/src/enums.ts +++ b/src/enums.ts @@ -192,7 +192,7 @@ export function generateEnumToNumber(ctx: Context, fullName: string, enumDesc: E return joinCode(chunks, { on: "\n" }); } -function getMemberName(ctx: Context, fullName: string, valueDesc: EnumValueDescriptorProto): string { +export function getMemberName(ctx: Context, fullName: string, valueDesc: EnumValueDescriptorProto): string { if (ctx.options.removeEnumPrefix) { return valueDesc.name.replace(`${camelToSnake(fullName)}_`, ""); } diff --git a/src/types.ts b/src/types.ts index 94df2a604..38386360a 100644 --- a/src/types.ts +++ b/src/types.ts @@ -17,6 +17,7 @@ import { fail, FormattedMethodDescriptor, impProto, maybePrefixPackage } from ". import SourceInfo from "./sourceInfo"; import { uncapitalize } from "./case"; import { Context } from "./context"; +import { getMemberName as getEnumMemberName } from "./enums"; /** Based on https://github.com/dcodeIO/protobuf.js/blob/master/src/types.js#L37. */ export function basicWireType(type: FieldDescriptorProto_Type): number { @@ -192,11 +193,13 @@ export function defaultValue(ctx: Context, field: FieldDescriptorProto): any { // to probe and see if zero is an allowed value. If it's not, pick the first one. // This is probably not great, but it's only used in fromJSON and fromPartial, // and I believe the semantics of those in the proto2 world are generally undefined. - const enumProto = typeMap.get(field.typeName)![2] as EnumDescriptorProto; + const typeInfo = typeMap.get(field.typeName)!; + const enumProto = typeInfo[2] as EnumDescriptorProto; + const enumFullName = typeInfo[1]; const zerothValue = enumProto.value.find((v) => v.number === 0) || enumProto.value[0]; if (options.stringEnums) { const enumType = messageToTypeName(ctx, field.typeName); - return code`${enumType}.${zerothValue.name}`; + return code`${enumType}.${getEnumMemberName(ctx, enumFullName, zerothValue)}`; } else { return zerothValue.number; } @@ -264,11 +267,14 @@ export function notDefaultCheck( // to probe and see if zero is an allowed value. If it's not, pick the first one. // This is probably not great, but it's only used in fromJSON and fromPartial, // and I believe the semantics of those in the proto2 world are generally undefined. - const enumProto = typeMap.get(field.typeName)![2] as EnumDescriptorProto; + const typeInfo = typeMap.get(field.typeName)!; + const enumProto = typeInfo[2] as EnumDescriptorProto; + const enumFullName = typeInfo[1]; const zerothValue = enumProto.value.find((v) => v.number === 0) || enumProto.value[0]; if (options.stringEnums) { const enumType = messageToTypeName(ctx, field.typeName); - return code`${maybeNotUndefinedAnd} ${place} !== ${enumType}.${zerothValue.name}`; + const enumValue = getEnumMemberName(ctx, enumFullName, zerothValue); + return code`${maybeNotUndefinedAnd} ${place} !== ${enumType}.${enumValue}`; } else { return code`${maybeNotUndefinedAnd} ${place} !== ${zerothValue.number}`; }