@jsonjoy.com/json-type
Version:
High-performance JSON Pointer implementation
147 lines (146 loc) • 6.41 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 constants_1 = require("../../constants");
const CborEncoderCodegenContext_1 = require("../../codegen/binary/CborEncoderCodegenContext");
const MessagePackEncoderCodegenContext_1 = require("../../codegen/binary/MessagePackEncoderCodegenContext");
const AbsType_1 = require("./AbsType");
class MapType extends AbsType_1.AbsType {
constructor(valueType, keyType, options) {
super();
this.valueType = valueType;
this.keyType = keyType;
this.schema = { kind: 'map', value: schema.s.any, ...(keyType && { key: schema.s.any }), ...options };
}
getSchema(ctx) {
return {
...this.schema,
value: this.valueType.getSchema(ctx),
...(this.keyType && { key: this.keyType.getSchema(ctx) }),
};
}
getOptions() {
const { kind, value, key, ...options } = this.schema;
return options;
}
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.valueType.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.valueType.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.valueType.codegenJsonTextEncoder(ctx, new JsExpression_1.JsExpression(() => `${r}[${rKey}]`));
ctx.js(`}`);
ctx.writeText('}');
}
codegenBinaryEncoder(ctx, value) {
const type = this.valueType;
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.valueType;
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);
}
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.valueType;
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.valueType.toString(tab)]);
}
}
exports.MapType = MapType;