import { MessageType, 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; repeated: 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; }; function ProtoField(no: number, type: T, repeated?: R, optional?: O): ScalarProtoFieldType; function ProtoField ProtoMessageType, O extends boolean = false, R extends O extends true ? false : boolean = false>(no: number, type: T, repeated?: R, optional?: O): MessageProtoFieldType; function ProtoField(no: number, type: ScalarType | (() => ProtoMessageType), repeated?: boolean, optional?: boolean): ProtoFieldType { if (typeof type === 'function') { return { kind: 'message', no: no, type: type, repeated: repeated ?? false, optional: optional ?? false }; } else { return { kind: 'scalar', no: no, type: type, repeated: repeated ?? false, optional: optional ?? false }; } } type ProtoFieldReturnType = T extends ScalarProtoFieldType ? ScalarTypeToTsType : T extends MessageProtoFieldType ? ProtoStructType> : never; type RequiredFieldsType = { [K in keyof T as T[K] extends { optional: true } | MessageProtoFieldType ? never : LowerCamelCase] : T[K] extends { repeated: true } ? ProtoFieldReturnType[] : ProtoFieldReturnType }; type OptionalFieldsType = { [K in keyof T as T[K] extends { optional: true } | MessageProtoFieldType ? LowerCamelCase : never]?: T[K] extends { repeated: true } ? ProtoFieldReturnType[] : ProtoFieldReturnType }; type ProtoStructType = RequiredFieldsType & OptionalFieldsType; const NapProtoMsgCache = new Map>>(); 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.repeated ? [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') { const rt = NapProtoMsgCache.get(field.type()) ?? (() => { const msg = new NapProtoMsg(field.type()); NapProtoMsgCache.set(field.type(), msg._proto_msg); return msg._proto_msg; })(); return { no: field.no, name: key, kind: 'message', repeat: field.repeated ? RepeatType.PACKED : RepeatType.NO, T: () => rt, }; } }) as PartialFieldInfo[]; this._proto_msg = new MessageType>('nya', this._field); } encode(data: ProtoStructType): Uint8Array { return this._proto_msg.toBinary(data); } decode(data: Uint8Array): ProtoStructType { return this._proto_msg.fromBinary(data); } }