node-insim
Version:
An InSim library for NodeJS with TypeScript support
129 lines (128 loc) • 4.69 kB
JavaScript
import { getFormat } from '../../decorators';
import { InSimError } from '../../errors';
import { determineLength, unpack } from '../../lfspack';
import { log as baseLog } from '../../log';
const log = baseLog.extend('struct');
export class Struct {
constructor() {
/** @ignore */
this.SIZE_MULTIPLIER = 4;
/** All string properties in a raw format as it was received from LFS */
this._raw = {};
}
initialize(data) {
if (!data) {
return;
}
Object.assign(this, data);
}
/** @ignore */
getValidPropertyNames() {
const prototype = Object.getPrototypeOf(this);
const ownPropertyNames = Object.getOwnPropertyNames(this);
const prototypePropertyNames = Object.keys(Object.getOwnPropertyDescriptors(prototype));
return [...ownPropertyNames, ...prototypePropertyNames].filter((propertyName) => getFormat(this, propertyName) !== undefined);
}
/** @ignore */
getFormat(propertyFormats) {
const propertyNames = this.getValidPropertyNames();
return propertyNames
.map((propertyName) => {
var _a;
return (_a = propertyFormats === null || propertyFormats === void 0 ? void 0 : propertyFormats[propertyName]) !== null && _a !== void 0 ? _a : getFormat(this, propertyName);
})
.join('');
}
/** @ignore */
getFormatSize() {
return determineLength(`<${this.getFormat()}`);
}
/** @ignore */
unpack(buffer, propertyFormatOverrides) {
const format = this.getFormat(propertyFormatOverrides);
const data = unpack(`<${format}`, buffer.buffer);
if (!data) {
throw new InSimError(`Unpacked no data using ${format} from buffer ${buffer.join()}`);
}
const propertyNames = this.getValidPropertyNames();
let i = 0;
propertyNames.forEach((propertyName) => {
const value = data[i];
if (i >= data.length) {
return;
}
const propertyType = typeof this[propertyName];
if (value === undefined) {
i++;
return;
}
if (propertyName === 'Size') {
this[propertyName] =
value * this.SIZE_MULTIPLIER;
i++;
return;
}
// Tuple of [rawString, decodedString]
if (Array.isArray(value) && value.length === 2) {
this[propertyName] = value[1];
this._raw[propertyName] = value[0];
i++;
return;
}
if (Array.isArray(value) &&
Array.isArray(this[propertyName])) {
this[propertyName] = value;
i++;
return;
}
if (propertyType == 'object') {
i = this.parseObject(this[propertyName], data, i, propertyName);
return;
}
this[propertyName] = value;
i++;
});
log('Data unpacked:', this);
return this;
}
parseArray(instance, data, i, instanceName) {
for (let j = 0; j < instance.length; j++) {
const item = instance[j];
if (typeof item === 'object') {
i = this.parseObject(item, data, i, `${instanceName}[${j}]`);
}
else {
instance[j] = data[i];
i++;
}
}
return i;
}
parseObject(instance, data, i, instanceName) {
if (Array.isArray(instance)) {
return this.parseArray(instance, data, i, instanceName);
}
if (instance instanceof Struct) {
const propertyNames = instance.getValidPropertyNames();
propertyNames.forEach((propertyName) => {
const propInstance = instance[propertyName];
const sval = data[i];
const propType = typeof propInstance;
const fullName = `${instanceName}.${propertyName}`;
if (propType === 'object') {
i = this.parseObject(propInstance, data, i, fullName);
return;
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
instance[propertyName] = sval;
i++;
});
}
else {
// should be handled by other parts of unpack function
// because instance here is a reference to the target property and setter can't be accessed
i++;
}
return i;
}
}