protobuf-ts
protobuf-ts copied to clipboard
Provide access to custom `EnumOptions`, `EnumValueOptions`, and `OneofOptions` by utiliting `WeakMap`
Similar to https://github.com/timostamm/protobuf-ts/issues/306#issuecomment-1131606572 it seems like it should be possible to store and retrieve custom EnumOptions
and EnumValueOptions
by having the generated code hold onto a reference to the custom options in a WeakMap
.
// generated code
import { registerEnumOptions } from "@protobuf-ts/runtime";
export enum ExampleEnum {
/**
* @generated from protobuf enum value: EXAMPLE = 0;
*/
EXAMPLE = 0,
};
registerEnumOptions(
ExampleEnum,
{
options: {
"foo.bar": {
"baz_qux": 123,
}
},
valueOptions: {
"EXAMPLE": { // Use enum "label" instead of "value" to avoid collisions with `allow_alias`
"my.example" {
"enum_value_option": true,
},
},
},
}
);
// usage
import { readEnumOption, readEnumValueOption } from "@protobuf-ts/runtime";
import { ExampleEnum } from "../generated/code";
let enumOption = readEnumOption(ExampleEnum, "foo.bar", OptionalMessageType);
// { bazQux: 123 }
let enumValueOption = readEnumValueOption(ExampleEnum, ExampleEnum[ExampleEnum.EXAMPLE], "my.example", OptionalMessageType);
// { enumValueOption: true }
// @protobuf-ts/runtime implementation
interface EnumOptions {
options?: JsonObject;
valueOptions?: Record<string, JsonObject | undefined>;
}
const enumOptionsMap = new WeakMap<object, EnumOptions>();
export function registerEnumOptions(enum: object, options: EnumOptions): void {
enumOptionsMap.set(enum, options);
}
export function readEnumOption<T extends object>(enum: object, extensionName: string): JsonValue | undefined;
export function readEnumOption<T extends object>(enum: object, extensionName: string, extensionType: IMessageType<T>): T | undefined;
export function readEnumOption<T extends object>(enum: object, extensionName: string, extensionType?: IMessageType<T>): T | JsonValue | undefined {
const optionVal = enumOptionsMap.get(enum)?.options?.[extensionName];
if (optionVal === undefined) {
return optionVal;
}
return extensionType ? extensionType.fromJson(optionVal) : optionVal;
}
export function readEnumValueOption<T extends object>(enum: object, enumValueName: string, extensionName: string): JsonValue | undefined;
export function readEnumValueOption<T extends object>(enum: object, enumValueName: string, extensionName: string, extensionType: IMessageType<T>): T | undefined;
export function readEnumValueOption<T extends object>(enum: object, enumValueName: string, extensionName: string, extensionType?: IMessageType<T>): T | JsonValue | undefined {
const optionVal = enumOptionsMap.get(enum)?.valueOptions?.[extensionName];
if (optionVal === undefined) {
return optionVal;
}
return extensionType ? extensionType.fromJson(optionVal) : optionVal;
}
This same method could be used to provide access to OneofOptions
as well:
// generated code
registerOneofOptions(ExampleMessageType, 'my_oneof_name', { ...JsonObjectOptions });
// usage
const myOneofOptions = readOneofOption(ExampleMessageType, 'my_oneof_name', OptionalMessageType);