@jsonjoy.com/json-type
Version:
High-performance JSON Pointer implementation
198 lines (197 loc) • 8.24 kB
JavaScript
;
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;