import { MessageType, PartialMessage, RepeatType, ScalarType } from '@protobuf-ts/runtime'; import { PartialFieldInfo } from "@protobuf-ts/runtime/build/types/reflection-info"; type LowerCamelCase = CamelCaseHelper; type CamelCaseHelper< S extends string, CapNext extends boolean, IsFirstChar extends boolean > = S extends `${infer F}${infer R}` ? F extends '_' ? CamelCaseHelper : F extends `${number}` ? `${F}${CamelCaseHelper}` : CapNext extends true ? `${Uppercase}${CamelCaseHelper}` : IsFirstChar extends true ? `${Lowercase}${CamelCaseHelper}` : `${F}${CamelCaseHelper}` : ''; type ScalarTypeToTsType = T extends ScalarType.DOUBLE | ScalarType.FLOAT | ScalarType.INT32 | ScalarType.FIXED32 | ScalarType.UINT32 | ScalarType.SFIXED32 | ScalarType.SINT32 ? number : T extends ScalarType.INT64 | ScalarType.UINT64 | ScalarType.FIXED64 | ScalarType.SFIXED64 | ScalarType.SINT64 ? bigint : T extends ScalarType.BOOL ? boolean : T extends ScalarType.STRING ? string : T extends ScalarType.BYTES ? Uint8Array : never; interface BaseProtoFieldType { kind: 'scalar' | 'message'; no: number; type: T; optional: O; repeat: R; } interface ScalarProtoFieldType extends BaseProtoFieldType { kind: 'scalar'; } interface MessageProtoFieldType ProtoMessageType, O extends boolean, R extends O extends true ? false : boolean> extends BaseProtoFieldType { kind: 'message'; } type ProtoFieldType = | ScalarProtoFieldType | MessageProtoFieldType<() => ProtoMessageType, boolean, boolean>; type ProtoMessageType = { [key: string]: ProtoFieldType; }; export function ProtoField(no: number, type: T, optional?: O, repeat?: R): ScalarProtoFieldType; export function ProtoField ProtoMessageType, O extends boolean = false, R extends O extends true ? false : boolean = false>(no: number, type: T, optional?: O, repeat?: R): MessageProtoFieldType; export function ProtoField(no: number, type: ScalarType | (() => ProtoMessageType), optional?: boolean, repeat?: boolean): ProtoFieldType { if (typeof type === 'function') { return { kind: 'message', no: no, type: type, optional: optional ?? false, repeat: repeat ?? false }; } else { return { kind: 'scalar', no: no, type: type, optional: optional ?? false, repeat: repeat ?? false }; } } type ProtoFieldReturnType = NonNullable extends ScalarProtoFieldType ? ScalarTypeToTsType : T extends NonNullable> ? NonNullable, E>> : never; type RequiredFieldsBaseType = { [K in keyof T as T[K] extends { optional: true } ? never : LowerCamelCase]: T[K] extends { repeat: true } ? ProtoFieldReturnType[] : ProtoFieldReturnType } type OptionalFieldsBaseType = { [K in keyof T as T[K] extends { optional: true } ? LowerCamelCase : never]?: T[K] extends { repeat: true } ? ProtoFieldReturnType[] : ProtoFieldReturnType } type RequiredFieldsType = E extends true ? Partial> : RequiredFieldsBaseType; type OptionalFieldsType = E extends true ? Partial> : OptionalFieldsBaseType; type NapProtoStructType = RequiredFieldsType & OptionalFieldsType; export type NapProtoEncodeStructType = NapProtoStructType; export type NapProtoDecodeStructType = NapProtoStructType; const NapProtoMsgCache = new Map>>(); export class NapProtoMsg { private readonly _msg: T; private readonly _field: PartialFieldInfo[]; private readonly _proto_msg: MessageType>; constructor(fields: T) { this._msg = fields; this._field = Object.keys(fields).map(key => { const field = fields[key]; if (field.kind === 'scalar') { const repeatType = field.repeat ? [ScalarType.STRING, ScalarType.BYTES].includes(field.type) ? RepeatType.UNPACKED : RepeatType.PACKED : RepeatType.NO; return { no: field.no, name: key, kind: 'scalar', T: field.type, opt: field.optional, repeat: repeatType, }; } else if (field.kind === 'message') { return { no: field.no, name: key, kind: 'message', repeat: field.repeat ? RepeatType.PACKED : RepeatType.NO, T: () => new NapProtoMsg(field.type())._proto_msg, }; } }) as PartialFieldInfo[]; this._proto_msg = new MessageType>('nya', this._field); } encode(data: NapProtoEncodeStructType): Uint8Array { return this._proto_msg.toBinary(this._proto_msg.create(data as PartialMessage>)); } decode(data: Uint8Array): NapProtoDecodeStructType { return this._proto_msg.fromBinary(data) as NapProtoDecodeStructType; } }