import { ErrorCode, ErrorUtils, hexToBytes } from "js-moi-utils"; import { Depolorizer, Polorizer, WireType } from "js-polo"; import type { LogicManifest } from "../../types/manifest"; import { Schema } from "../schema"; import { BaseManifestCoder } from "./base-manifest-coder"; type SchemaConfig = Record; export class JsonManifestCoder extends BaseManifestCoder { private static MANIFEST_SCHEMA = { kind: "struct", fields: { syntax: { kind: "integer", }, engine: Schema.PISA_ENGINE_SCHEMA, kind: { kind: "string", }, elements: { kind: "array", fields: { values: { kind: "struct", fields: { ptr: { kind: "integer", }, deps: Schema.PISA_DEPS_SCHEMA, kind: { kind: "string", }, data: { kind: "raw", }, }, }, }, }, }, }; private static readonly SCHEMA_CONFIG: SchemaConfig = { literal: { wireType: WireType.WIRE_PACK, schema: Schema.PISA_LITERAL_SCHEMA, }, typedef: { wireType: WireType.WIRE_WORD, schema: Schema.PISA_TYPEDEF_SCHEMA, }, state: { wireType: WireType.WIRE_PACK, schema: Schema.PISA_STATE_SCHEMA, }, callable: { wireType: WireType.WIRE_PACK, schema: Schema.PISA_ROUTINE_SCHEMA, }, method: { wireType: WireType.WIRE_PACK, schema: Schema.PISA_METHOD_SCHEMA, }, class: { wireType: WireType.WIRE_PACK, schema: Schema.PISA_CLASS_SCHEMA, }, interface: { wireType: WireType.WIRE_PACK, schema: Schema.PISA_EXTERN_SCHEMA, }, event: { wireType: WireType.WIRE_PACK, schema: Schema.PISA_EVENT_SCHEMA, }, asset: { wireType: WireType.WIRE_PACK, schema: Schema.PISA_ASSET_SCHEMA, } }; /** * Serializes a given LogicManifest.Element into a Polorizer instance. * * @param element - The LogicManifest.Element to be serialized. * @returns A Polorizer instance containing the serialized data. * * @throws Will throw an error if the element kind is unknown. */ private serializeElement(element: LogicManifest.Element): Polorizer { const polorizer = new Polorizer(); polorizer.polorizeInteger(element.ptr); polorizer.polorize(element.deps, Schema.PISA_DEPS_SCHEMA); polorizer.polorizeString(element.kind); const config = JsonManifestCoder.SCHEMA_CONFIG[element.kind]; if (config == null) { ErrorUtils.throwError( `Unknown element kind: ${element.kind}`, ErrorCode.UNEXPECTED_ARGUMENT ); } polorizer.polorize(element.data, config.schema); return polorizer; } private deserializeElement(element: any) { if (element.deps.length == 0) { delete element.deps; } const config = JsonManifestCoder.SCHEMA_CONFIG[element.kind]; if (config == null) { ErrorUtils.throwError( `Unknown element kind: ${element.kind}`, ErrorCode.UNEXPECTED_ARGUMENT ); } const blob = new Uint8Array([config.wireType, ...element.data]); element.data = new Depolorizer(blob).depolorize(config.schema); const isRoutineOrMethod = element.kind === "routine" || element.kind === "method"; if (!isRoutineOrMethod) { return element; } for (const [key, value] of Object.entries(element.data.executes)) { if (value === "" || (Array.isArray(value) && value.length === 0)) { delete element.data.executes[key]; } if (value instanceof Uint8Array) { element.data.executes[key] = Array.from(value); } } } /** * Serializes an array of elements from the given manifest. * * @param {LogicManifest.Manifest} manifest - The manifest containing the elements to be serialized. * @returns - The polorizer containing the serialized elements. */ private serializeElementArray(manifest: LogicManifest.Manifest) { const polorizer = new Polorizer(); for (const element of manifest.elements) { polorizer.polorizePacked(this.serializeElement(element)); } return polorizer; } private deserializeElements(elements: any[]) { for (const element of elements) { this.deserializeElement(element); } } /** * Serializes a given LogicManifest.Manifest object into a POLO Uint8Array. * * @param {LogicManifest.Manifest} manifest - The manifest object to be serialized. * @returns {Uint8Array} The POLO serialized manifest as a Uint8Array. * @throws {Error} If the manifest is invalid. */ encode(manifest: LogicManifest.Manifest): Uint8Array { if (!super.validate(manifest)) { ErrorUtils.throwArgumentError( "Invalid manifest.", "manifest", manifest ); } const polorizer = new Polorizer(); polorizer.polorizeInteger(manifest.syntax); polorizer.polorize(manifest.engine, Schema.PISA_ENGINE_SCHEMA); polorizer.polorizeString(manifest.kind) const elements = this.serializeElementArray(manifest); polorizer.polorizePacked(elements); return polorizer.bytes(); } private getPoloBytes(data: string | Uint8Array): Uint8Array { return typeof data === "string" ? hexToBytes(data) : data; } decode(data: string | Uint8Array): LogicManifest.Manifest { const depolorizer = new Depolorizer(this.getPoloBytes(data)); const decoded = depolorizer.depolorize(JsonManifestCoder.MANIFEST_SCHEMA); if (!super.validate(decoded)) { ErrorUtils.throwArgumentError("Invalid manifest.", "data", data); } this.deserializeElements(decoded.elements); return decoded; } }