mirror of https://github.com/ghostfolio/ghostfolio
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
321 lines
12 KiB
321 lines
12 KiB
"use strict";
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.AvroSchemaEncoder = void 0;
|
|
const AvroEncoder_1 = require("./AvroEncoder");
|
|
const AvroSchemaValidator_1 = require("./AvroSchemaValidator");
|
|
class AvroSchemaEncoder {
|
|
constructor(writer) {
|
|
this.writer = writer;
|
|
this.namedSchemas = new Map();
|
|
this.encoder = new AvroEncoder_1.AvroEncoder(writer);
|
|
this.validator = new AvroSchemaValidator_1.AvroSchemaValidator();
|
|
}
|
|
encode(value, schema, selectedIndex) {
|
|
this.writer.reset();
|
|
this.namedSchemas.clear();
|
|
if (!this.validator.validateSchema(schema)) {
|
|
throw new Error('Invalid Avro schema');
|
|
}
|
|
if (!this.validator.validateValue(value, schema)) {
|
|
throw new Error('Value does not conform to schema');
|
|
}
|
|
this.collectNamedSchemas(schema);
|
|
if (Array.isArray(schema) && selectedIndex !== undefined) {
|
|
this.writeUnion(value, schema, selectedIndex);
|
|
}
|
|
else {
|
|
this.writeValue(value, schema);
|
|
}
|
|
return this.writer.flush();
|
|
}
|
|
writeNull(schema) {
|
|
this.validateSchemaType(schema, 'null');
|
|
this.encoder.writeNull();
|
|
}
|
|
writeBoolean(value, schema) {
|
|
this.validateSchemaType(schema, 'boolean');
|
|
this.encoder.writeBoolean(value);
|
|
}
|
|
writeInt(value, schema) {
|
|
this.validateSchemaType(schema, 'int');
|
|
if (!Number.isInteger(value) || value < -2147483648 || value > 2147483647) {
|
|
throw new Error('Value is not a valid 32-bit integer');
|
|
}
|
|
this.encoder.writeInt(value);
|
|
}
|
|
writeLong(value, schema) {
|
|
this.validateSchemaType(schema, 'long');
|
|
this.encoder.writeLong(value);
|
|
}
|
|
writeFloat(value, schema) {
|
|
this.validateSchemaType(schema, 'float');
|
|
this.encoder.writeFloat(value);
|
|
}
|
|
writeDouble(value, schema) {
|
|
this.validateSchemaType(schema, 'double');
|
|
this.encoder.writeDouble(value);
|
|
}
|
|
writeBytes(value, schema) {
|
|
this.validateSchemaType(schema, 'bytes');
|
|
this.encoder.writeBin(value);
|
|
}
|
|
writeString(value, schema) {
|
|
this.validateSchemaType(schema, 'string');
|
|
this.encoder.writeStr(value);
|
|
}
|
|
writeRecord(value, schema) {
|
|
if (typeof schema === 'object' && schema.type !== 'record') {
|
|
throw new Error('Schema is not a record schema');
|
|
}
|
|
const recordSchema = this.resolveSchema(schema);
|
|
if (recordSchema.type !== 'record') {
|
|
throw new Error('Schema is not a record schema');
|
|
}
|
|
for (let i = 0; i < recordSchema.fields.length; i++) {
|
|
const field = recordSchema.fields[i];
|
|
const fieldValue = value[field.name];
|
|
if (fieldValue !== undefined) {
|
|
this.writeValue(fieldValue, field.type);
|
|
}
|
|
else if (field.default !== undefined) {
|
|
this.writeValue(field.default, field.type);
|
|
}
|
|
else {
|
|
throw new Error(`Missing required field: ${field.name}`);
|
|
}
|
|
}
|
|
}
|
|
writeEnum(value, schema) {
|
|
if (typeof schema === 'object' && schema.type !== 'enum') {
|
|
throw new Error('Schema is not an enum schema');
|
|
}
|
|
const enumSchema = this.resolveSchema(schema);
|
|
if (enumSchema.type !== 'enum') {
|
|
throw new Error('Schema is not an enum schema');
|
|
}
|
|
const index = enumSchema.symbols.indexOf(value);
|
|
if (index === -1) {
|
|
throw new Error(`Invalid enum value: ${value}`);
|
|
}
|
|
this.writeVarIntSigned(this.encodeZigZag32(index));
|
|
}
|
|
writeArray(value, schema) {
|
|
if (typeof schema === 'object' && schema.type !== 'array') {
|
|
throw new Error('Schema is not an array schema');
|
|
}
|
|
const arraySchema = this.resolveSchema(schema);
|
|
if (arraySchema.type !== 'array') {
|
|
throw new Error('Schema is not an array schema');
|
|
}
|
|
this.writeVarIntUnsigned(value.length);
|
|
const length = value.length;
|
|
for (let i = 0; i < length; i++) {
|
|
this.writeValue(value[i], arraySchema.items);
|
|
}
|
|
this.writeVarIntUnsigned(0);
|
|
}
|
|
writeMap(value, schema) {
|
|
if (typeof schema === 'object' && schema.type !== 'map') {
|
|
throw new Error('Schema is not a map schema');
|
|
}
|
|
const mapSchema = this.resolveSchema(schema);
|
|
if (mapSchema.type !== 'map') {
|
|
throw new Error('Schema is not a map schema');
|
|
}
|
|
const entries = Object.entries(value);
|
|
this.writeVarIntUnsigned(entries.length);
|
|
const length = entries.length;
|
|
for (let i = 0; i < length; i++) {
|
|
const entry = entries[i];
|
|
this.encoder.writeStr(entry[0]);
|
|
this.writeValue(entry[1], mapSchema.values);
|
|
}
|
|
this.writeVarIntUnsigned(0);
|
|
}
|
|
writeUnion(value, schema, selectedIndex) {
|
|
if (!Array.isArray(schema)) {
|
|
throw new Error('Schema is not a union schema');
|
|
}
|
|
let index = selectedIndex;
|
|
if (index === undefined) {
|
|
index = schema.findIndex((subSchema) => this.validator.validateValue(value, subSchema));
|
|
if (index === -1) {
|
|
throw new Error('Value does not match any schema in the union');
|
|
}
|
|
}
|
|
if (index < 0 || index >= schema.length) {
|
|
throw new Error('Invalid union index');
|
|
}
|
|
this.writeVarIntSigned(this.encodeZigZag32(index));
|
|
this.writeValue(value, schema[index]);
|
|
}
|
|
writeFixed(value, schema) {
|
|
if (typeof schema === 'object' && schema.type !== 'fixed') {
|
|
throw new Error('Schema is not a fixed schema');
|
|
}
|
|
const fixedSchema = this.resolveSchema(schema);
|
|
if (fixedSchema.type !== 'fixed') {
|
|
throw new Error('Schema is not a fixed schema');
|
|
}
|
|
if (value.length !== fixedSchema.size) {
|
|
throw new Error(`Fixed value length ${value.length} does not match schema size ${fixedSchema.size}`);
|
|
}
|
|
this.writer.buf(value, value.length);
|
|
}
|
|
writeNumber(value, schema) {
|
|
const resolvedSchema = this.resolveSchema(schema);
|
|
const schemaType = typeof resolvedSchema === 'string'
|
|
? resolvedSchema
|
|
: Array.isArray(resolvedSchema)
|
|
? 'union'
|
|
: resolvedSchema.type;
|
|
switch (schemaType) {
|
|
case 'int':
|
|
this.writeInt(value, schema);
|
|
break;
|
|
case 'long':
|
|
this.writeLong(value, schema);
|
|
break;
|
|
case 'float':
|
|
this.writeFloat(value, schema);
|
|
break;
|
|
case 'double':
|
|
this.writeDouble(value, schema);
|
|
break;
|
|
default:
|
|
throw new Error(`Schema type ${schemaType} is not a numeric type`);
|
|
}
|
|
}
|
|
writeValue(value, schema) {
|
|
const resolvedSchema = this.resolveSchema(schema);
|
|
if (typeof resolvedSchema === 'string') {
|
|
switch (resolvedSchema) {
|
|
case 'null':
|
|
this.encoder.writeNull();
|
|
break;
|
|
case 'boolean':
|
|
this.encoder.writeBoolean(value);
|
|
break;
|
|
case 'int':
|
|
this.encoder.writeInt(value);
|
|
break;
|
|
case 'long':
|
|
this.encoder.writeLong(value);
|
|
break;
|
|
case 'float':
|
|
this.encoder.writeFloat(value);
|
|
break;
|
|
case 'double':
|
|
this.encoder.writeDouble(value);
|
|
break;
|
|
case 'bytes':
|
|
this.encoder.writeBin(value);
|
|
break;
|
|
case 'string':
|
|
this.encoder.writeStr(value);
|
|
break;
|
|
default:
|
|
throw new Error(`Unknown primitive type: ${resolvedSchema}`);
|
|
}
|
|
return;
|
|
}
|
|
if (Array.isArray(resolvedSchema)) {
|
|
this.writeUnion(value, resolvedSchema);
|
|
return;
|
|
}
|
|
switch (resolvedSchema.type) {
|
|
case 'record':
|
|
this.writeRecord(value, resolvedSchema);
|
|
break;
|
|
case 'enum':
|
|
this.writeEnum(value, resolvedSchema);
|
|
break;
|
|
case 'array':
|
|
this.writeArray(value, resolvedSchema);
|
|
break;
|
|
case 'map':
|
|
this.writeMap(value, resolvedSchema);
|
|
break;
|
|
case 'fixed':
|
|
this.writeFixed(value, resolvedSchema);
|
|
break;
|
|
default:
|
|
throw new Error(`Unknown schema type: ${resolvedSchema.type}`);
|
|
}
|
|
}
|
|
validateSchemaType(schema, expectedType) {
|
|
const resolvedSchema = this.resolveSchema(schema);
|
|
const actualType = typeof resolvedSchema === 'string'
|
|
? resolvedSchema
|
|
: Array.isArray(resolvedSchema)
|
|
? 'union'
|
|
: resolvedSchema.type;
|
|
if (actualType !== expectedType) {
|
|
throw new Error(`Expected schema type ${expectedType}, got ${actualType}`);
|
|
}
|
|
}
|
|
resolveSchema(schema) {
|
|
if (typeof schema === 'string') {
|
|
const namedSchema = this.namedSchemas.get(schema);
|
|
return namedSchema || schema;
|
|
}
|
|
return schema;
|
|
}
|
|
collectNamedSchemas(schema) {
|
|
if (typeof schema === 'string' || Array.isArray(schema)) {
|
|
return;
|
|
}
|
|
if (typeof schema === 'object' && schema !== null) {
|
|
switch (schema.type) {
|
|
case 'record':
|
|
const recordSchema = schema;
|
|
const recordFullName = this.getFullName(recordSchema.name, recordSchema.namespace);
|
|
this.namedSchemas.set(recordFullName, recordSchema);
|
|
recordSchema.fields.forEach((field) => this.collectNamedSchemas(field.type));
|
|
break;
|
|
case 'enum':
|
|
const enumSchema = schema;
|
|
const enumFullName = this.getFullName(enumSchema.name, enumSchema.namespace);
|
|
this.namedSchemas.set(enumFullName, enumSchema);
|
|
break;
|
|
case 'fixed':
|
|
const fixedSchema = schema;
|
|
const fixedFullName = this.getFullName(fixedSchema.name, fixedSchema.namespace);
|
|
this.namedSchemas.set(fixedFullName, fixedSchema);
|
|
break;
|
|
case 'array':
|
|
this.collectNamedSchemas(schema.items);
|
|
break;
|
|
case 'map':
|
|
this.collectNamedSchemas(schema.values);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
getFullName(name, namespace) {
|
|
return namespace ? `${namespace}.${name}` : name;
|
|
}
|
|
writeVarIntUnsigned(value) {
|
|
const writer = this.writer;
|
|
let n = value >>> 0;
|
|
while (n >= 0x80) {
|
|
writer.u8((n & 0x7f) | 0x80);
|
|
n >>>= 7;
|
|
}
|
|
writer.u8(n & 0x7f);
|
|
}
|
|
writeVarIntSigned(value) {
|
|
const writer = this.writer;
|
|
let n = value >>> 0;
|
|
while (n >= 0x80) {
|
|
writer.u8((n & 0x7f) | 0x80);
|
|
n >>>= 7;
|
|
}
|
|
writer.u8(n & 0x7f);
|
|
}
|
|
encodeZigZag32(value) {
|
|
return (value << 1) ^ (value >> 31);
|
|
}
|
|
}
|
|
exports.AvroSchemaEncoder = AvroSchemaEncoder;
|
|
//# sourceMappingURL=AvroSchemaEncoder.js.map
|