import { AnyCodec, Codec, PackParam, PackResult, UnpackParam, UnpackResult, } from "../base"; import { trackCodeExecuteError } from "../utils"; import { CODEC_OPTIONAL_PATH } from "../error"; export interface NullableCodec extends AnyCodec { pack(packable?: PackParam): PackResult; unpack(unpackable?: UnpackParam): UnpackResult; } export function createNullableCodec( codec: C ): NullableCodec { return { pack: (packable) => { if (packable == null) return packable; return trackCodeExecuteError(CODEC_OPTIONAL_PATH, () => codec.pack(packable) ); }, unpack: (unpackable) => { if (unpackable == null) return unpackable; return codec.unpack(unpackable); }, }; } type ObjectCodecShape = Record; export type ObjectCodec = Codec< { [key in keyof Shape]: PackResult }, { [key in keyof Shape]: UnpackResult }, { [key in keyof Shape]: PackParam }, { [key in keyof Shape]: UnpackParam } >; /** * a high-order codec that helps to organize multiple codecs together into a single object * @param codecShape * @example * ```ts * const codec = createObjectCodec({ * r: Uint8, * g: Uint8, * b: Uint8, * }); * * // { r: ArrayBuffer([0xff]), g: ArrayBuffer([0x00]), b: ArrayBuffer([0x00]) } * codec.pack({ r: 255, g: 0, b: 0 }); * ``` */ export function createObjectCodec( codecShape: Shape ): ObjectCodec { const codecEntries = Object.entries(codecShape); return { pack: (packableObj) => { const result = {} as { [key in keyof Shape]: PackResult }; codecEntries.forEach(([key, itemCodec]) => { Object.assign(result, { [key]: trackCodeExecuteError(key, () => itemCodec.pack(packableObj[key]) ), }); }); return result; }, unpack: (unpackableObj) => { const result = {} as { [key in keyof Shape]: UnpackResult }; codecEntries.forEach(([key, itemCodec]) => { Object.assign(result, { [key]: itemCodec.unpack(unpackableObj[key]) }); }); return result; }, }; } export type ArrayCodec = Codec< PackResult[], UnpackResult[], PackParam[], UnpackParam[] >; export function createArrayCodec(codec: C): ArrayCodec { return { pack: (items) => items.map((item, index) => trackCodeExecuteError(index, () => codec.pack(item)) ), unpack: (items) => items.map((item) => codec.unpack(item)), }; } /** * @param codec * @param afterCodecPack * @param beforeCodecUnpack */ export function enhancePack( codec: C, afterCodecPack: (arg: PackResult) => Packed, beforeCodecUnpack: (arg: Packed) => UnpackParam ): Codec, PackParam> { return { pack: (packable) => afterCodecPack(codec.pack(packable)), unpack: (unpackable) => codec.unpack(beforeCodecUnpack(unpackable)), }; }