UNPKG

js-moi-logic

Version:
157 lines (130 loc) 4.85 kB
import { isPrimitiveType, Schema, type ElementDescriptor, type LogicManifest } from "js-moi-manifest"; import { ErrorCode, ErrorUtils } from "js-moi-utils"; import { ArrayIndexAccessor, ClassFieldAccessor, LengthAccessor, PropertyAccessor, type Accessor, type AccessorProvider, type StorageTypeProvider, } from "./accessor"; const VALUE_TYPE_INDEX = 1; export interface AccessorBuilder { /** * Adds a length accessor to the accessor builder. * This accessor can be used to retrieve the length of an array or map. * * @returns The accessor builder instance. */ length(): void; /** * Adds a property accessor to the accessor builder. * * Used to access to value of key in a map. * * @param key - The label of the property. * @returns The accessor builder instance. */ property(key: string | number | boolean | Uint8Array | Buffer): AccessorBuilder; /** * Adds an accessor for the specified index to the AccessorBuilder. * * @param index - The index of the accessor. * @returns The AccessorBuilder instance. */ at(index: number): AccessorBuilder; /** * Adds a field to the accessor builder. * * Used to access the value of a field in a class. * * @param fieldName - The label of the field. * @returns The accessor builder instance. */ field(fieldName: string): AccessorBuilder; } export class SlotAccessorBuilder implements AccessorBuilder, AccessorProvider, StorageTypeProvider { private accessors: Accessor[] = []; public readonly elementDescriptor: ElementDescriptor; private slotType: string; private readonly typeField: LogicManifest.TypeField; public constructor(field: LogicManifest.TypeField, logicDescriptor: ElementDescriptor) { this.elementDescriptor = logicDescriptor; this.typeField = field; this.slotType = field.type; } /** * Retrieves the storage type of the accessor builder. * @returns The storage type. */ public getStorageType(): string { return this.slotType; } /** * Retrieves the base slot of the accessor builder. * @returns The base slot. */ public getBaseSlot(): number { return this.typeField.slot; } /** * Retrieves the accessors of the accessor builder. * @returns The accessors. */ public getAccessors(): Accessor[] { return this.accessors; } public length(): void { if (isPrimitiveType(this.slotType)) { ErrorUtils.throwError( `Attempting to access the length of primitive on type "${this.slotType}"`, ErrorCode.UNEXPECTED_ARGUMENT ); } this.slotType = "u64"; this.accessors.push(new LengthAccessor()); } public property(key: string): SlotAccessorBuilder { this.slotType = Schema.extractMapDataType(this.slotType)[VALUE_TYPE_INDEX]; this.accessors.push(new PropertyAccessor(key)); return this; } public at(index: number): SlotAccessorBuilder { this.slotType = Schema.extractArrayDataType(this.slotType); this.accessors.push(new ArrayIndexAccessor(index)); return this; } public field(fieldName: string): SlotAccessorBuilder { if (!this.elementDescriptor.getClassDefs().has(this.slotType)) { ErrorUtils.throwError( `Attempting to access a field '${fieldName}' in ${this.slotType}, which is not a recognized class.`, ErrorCode.UNEXPECTED_ARGUMENT ); } const element = this.elementDescriptor.getClassElement(this.slotType); element.data = element.data as LogicManifest.Class; const field = element.data.fields.find((field) => field.label === fieldName); if (field == null) { ErrorUtils.throwError( `The field '${fieldName}' is not a recognized member of the class '${this.slotType}'. Please ensure that the field name is correct and that it is defined within the class context.`, ErrorCode.PROPERTY_NOT_DEFINED, { field: fieldName, } ); } this.slotType = field.type; this.accessors.push(new ClassFieldAccessor(field.slot)); return this; } /** * Checks if the given `builder` is an instance of `SlotAccessorBuilder`. * * @param builder - The accessor builder to check. * @returns `true` if the `builder` is an instance of `SlotAccessorBuilder`, `false` otherwise. */ public static isSlotAccessorBuilder(builder: unknown): builder is SlotAccessorBuilder { return builder instanceof SlotAccessorBuilder; } }