UNPKG

@jsonjoy.com/json-type

Version:

High-performance JSON Pointer implementation

198 lines (197 loc) 8.24 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.MapType = void 0; const tslib_1 = require("tslib"); const JsExpression_1 = require("@jsonjoy.com/util/lib/codegen/util/JsExpression"); const asString_1 = require("@jsonjoy.com/util/lib/strings/asString"); const printTree_1 = require("tree-dump/lib/printTree"); const schema = tslib_1.__importStar(require("../../schema")); const json_random_1 = require("@jsonjoy.com/util/lib/json-random"); const validate_1 = require("../../schema/validate"); const constants_1 = require("../../constants"); const CborEncoderCodegenContext_1 = require("../../codegen/binary/CborEncoderCodegenContext"); const MessagePackEncoderCodegenContext_1 = require("../../codegen/binary/MessagePackEncoderCodegenContext"); const AbstractType_1 = require("./AbstractType"); class MapType extends AbstractType_1.AbstractType { constructor(type, options) { super(); this.type = type; this.schema = schema.s.Map(schema.s.any, options); } getSchema(ctx) { return { ...this.schema, type: this.type.getSchema(ctx), }; } toJsonSchema() { const jsonSchema = { type: 'object', patternProperties: { '.*': this.type.toJsonSchema(), }, }; return jsonSchema; } getOptions() { const { kind, type, ...options } = this.schema; return options; } validateSchema() { const schema = this.getSchema(); (0, validate_1.validateTType)(schema, 'map'); this.type.validateSchema(); } codegenValidator(ctx, path, r) { const err = ctx.err(constants_1.ValidationError.MAP, path); ctx.js(`if (!${r} || (typeof ${r} !== 'object') || (${r}.constructor !== Object)) return ${err};`); const rKeys = ctx.codegen.var(`Object.keys(${r});`); const rLength = ctx.codegen.var(`${rKeys}.length`); const rKey = ctx.codegen.r(); const rValue = ctx.codegen.r(); ctx.js(`for (var ${rKey}, ${rValue}, i = 0; i < ${rLength}; i++) {`); ctx.js(`${rKey} = ${rKeys}[i];`); ctx.js(`${rValue} = ${r}[${rKey}];`); this.type.codegenValidator(ctx, [...path, { r: rKey }], rValue); ctx.js(`}`); ctx.emitCustomValidators(this, path, r); } codegenJsonTextEncoder(ctx, value) { ctx.writeText('{'); const r = ctx.codegen.var(value.use()); const rKeys = ctx.codegen.var(`Object.keys(${r})`); const rLength = ctx.codegen.var(`${rKeys}.length`); const rKey = ctx.codegen.var(); ctx.codegen.if(`${rLength}`, () => { ctx.js(`${rKey} = ${rKeys}[0];`); ctx.js(`s += asString(${rKey}) + ':';`); this.type.codegenJsonTextEncoder(ctx, new JsExpression_1.JsExpression(() => `${r}[${rKey}]`)); }); ctx.js(`for (var i = 1; i < ${rLength}; i++) {`); ctx.js(`${rKey} = ${rKeys}[i];`); ctx.js(`s += ',' + asString(${rKey}) + ':';`); this.type.codegenJsonTextEncoder(ctx, new JsExpression_1.JsExpression(() => `${r}[${rKey}]`)); ctx.js(`}`); ctx.writeText('}'); } codegenBinaryEncoder(ctx, value) { const type = this.type; const codegen = ctx.codegen; const r = codegen.var(value.use()); const rKeys = codegen.var(`Object.keys(${r})`); const rKey = codegen.var(); const rLength = codegen.var(`${rKeys}.length`); const ri = codegen.var('0'); ctx.js(`encoder.writeObjHdr(${rLength});`); ctx.js(`for(; ${ri} < ${rLength}; ${ri}++){`); ctx.js(`${rKey} = ${rKeys}[${ri}];`); ctx.js(`encoder.writeStr(${rKey});`); const expr = new JsExpression_1.JsExpression(() => `${r}[${rKey}]`); if (ctx instanceof CborEncoderCodegenContext_1.CborEncoderCodegenContext) type.codegenCborEncoder(ctx, expr); else if (ctx instanceof MessagePackEncoderCodegenContext_1.MessagePackEncoderCodegenContext) type.codegenMessagePackEncoder(ctx, expr); else throw new Error('Unknown encoder'); ctx.js(`}`); } codegenCborEncoder(ctx, value) { this.codegenBinaryEncoder(ctx, value); } codegenMessagePackEncoder(ctx, value) { this.codegenBinaryEncoder(ctx, value); } codegenJsonEncoder(ctx, value) { const type = this.type; const objStartBlob = ctx.gen((encoder) => encoder.writeStartObj()); const objEndBlob = ctx.gen((encoder) => encoder.writeEndObj()); const separatorBlob = ctx.gen((encoder) => encoder.writeObjSeparator()); const keySeparatorBlob = ctx.gen((encoder) => encoder.writeObjKeySeparator()); const codegen = ctx.codegen; const r = codegen.var(value.use()); const rKeys = codegen.var(`Object.keys(${r})`); const rKey = codegen.var(); const rLength = codegen.var(`${rKeys}.length`); ctx.blob(objStartBlob); ctx.codegen.if(`${rLength}`, () => { ctx.js(`${rKey} = ${rKeys}[0];`); codegen.js(`encoder.writeStr(${rKey});`); ctx.blob(keySeparatorBlob); type.codegenJsonEncoder(ctx, new JsExpression_1.JsExpression(() => `${r}[${rKey}]`)); }); ctx.js(`for (var i = 1; i < ${rLength}; i++) {`); ctx.js(`${rKey} = ${rKeys}[i];`); ctx.blob(separatorBlob); codegen.js(`encoder.writeStr(${rKey});`); ctx.blob(keySeparatorBlob); type.codegenJsonEncoder(ctx, new JsExpression_1.JsExpression(() => `${r}[${rKey}]`)); ctx.js(`}`); ctx.blob(objEndBlob); } codegenCapacityEstimator(ctx, value) { const codegen = ctx.codegen; ctx.inc(5 /* MaxEncodingOverhead.Object */); const r = codegen.var(value.use()); const rKeys = codegen.var(`Object.keys(${r})`); const rKey = codegen.var(); const rLen = codegen.var(`${rKeys}.length`); codegen.js(`size += ${2 /* MaxEncodingOverhead.ObjectElement */} * ${rLen}`); const type = this.type; const fn = type.compileCapacityEstimator({ system: ctx.options.system, name: ctx.options.name, }); const rFn = codegen.linkDependency(fn); const ri = codegen.var('0'); codegen.js(`for (; ${ri} < ${rLen}; ${ri}++) {`); codegen.js(`${rKey} = ${rKeys}[${ri}];`); codegen.js(`size += ${5 /* MaxEncodingOverhead.String */} + ${5 /* MaxEncodingOverhead.StringLengthMultiplier */} * ${rKey}.length;`); codegen.js(`size += ${rFn}(${r}[${rKey}]);`); codegen.js(`}`); } random() { const length = Math.round(Math.random() * 10); const res = {}; for (let i = 0; i < length; i++) res[json_random_1.RandomJson.genString(length)] = this.type.random(); return res; } toTypeScriptAst() { const node = { node: 'TypeReference', typeName: 'Record', typeArguments: [{ node: 'StringKeyword' }, this.type.toTypeScriptAst()], }; // augmentWithComment(this.schema, node); return node; } toJson(value, system = this.system) { const map = value; const keys = Object.keys(map); const length = keys.length; if (!length) return '{}'; const last = length - 1; const type = this.type; let str = '{'; for (let i = 0; i < last; i++) { const key = keys[i]; const val = value[key]; if (val === undefined) continue; str += (0, asString_1.asString)(key) + ':' + type.toJson(val, system) + ','; } const key = keys[last]; const val = value[key]; if (val !== undefined) { str += (0, asString_1.asString)(key) + ':' + type.toJson(val, system); } else if (str.length > 1) str = str.slice(0, -1); return (str + '}'); } toString(tab = '') { return super.toString(tab) + (0, printTree_1.printTree)(tab, [(tab) => this.type.toString(tab)]); } } exports.MapType = MapType;