import { type FabricArrayPlus, type FabricContainerValuePlus, type FabricInstancePlus, type FabricPlainObjectPlus, type FabricValuePlus, } from "@/interface.ts"; import { type FabricContainerValueTag, type FabricValuePlusTag } from "@/types"; import { debugStr } from "@/value-debug"; import { type MappedResult, ValueVisitor, type VisitingEntryResult, type VisitingGapResult, type VisitingResult, type VisitingStateResult, type VisitResult, } from "./interface.ts"; /** * Base implementation of `ValueVisitor`, which leaves most `ValueVisitor` * methods `abstract`, while providing reasonable defaults for just a couple * methods (see which for details). */ export abstract class BaseValueVisitor< PlusType = never, ResultType = FabricValuePlus, > implements ValueVisitor { // // Subclass contract // /** @inheritDoc */ abstract mappedFabricArrayElement( array: FabricArrayPlus, index: number, value: FabricValuePlus, resultValue: FabricValuePlus, ): MappedResult; /** @inheritDoc */ abstract mappedFabricInstanceState( instance: FabricInstancePlus, state: FabricValuePlus, resultState: FabricValuePlus, ): MappedResult; /** @inheritDoc */ abstract mappedFabricPlainObjectEntry( container: FabricPlainObjectPlus, key: string, value: FabricValuePlus, resultKey: string, resultValue: FabricValuePlus, ): MappedResult; /** @inheritDoc */ abstract visitValue( value: FabricValuePlus, tag: FabricValuePlusTag | null, ): VisitResult; /** @inheritDoc */ abstract visitingFabricArrayElement( array: FabricArrayPlus, index: number, value: FabricValuePlus, ): VisitingResult; /** @inheritDoc */ abstract visitingFabricArrayGap( array: FabricArrayPlus, start: number, count: number, ): VisitingGapResult; /** @inheritDoc */ abstract visitingFabricInstanceState( instance: FabricInstancePlus, state: FabricValuePlus, ): VisitingStateResult; /** @inheritDoc */ abstract visitingFabricPlainObjectEntry( container: FabricPlainObjectPlus, key: string, value: FabricValuePlus, ): VisitingEntryResult; // // Instance methods // /** * @inheritDoc * * If not overridden, this returns `true`, thereby corresponding with the * default binding for the result type to `FabricValuePlus`. * * Any visitor with a non-default `PlusType` or `ResultType` _must_ override * this method if the default could cause the engine to return a type-lying * value _and_ the calling client cares about avoiding type lies. */ isDomainAssignableToResultType(): boolean { return true; } /** * @inheritDoc * * If not overridden, this returns `false`, thereby corresponding with the * default binding for the `PlusType` parameter to `never`. * * Any visitor with a non-default `PlusType` must override this method if it * is to ever receive `PlusType` values. */ isPlusType(_value: unknown): _value is PlusType { return false; } /** * @inheritDoc * * If not overridden, this returns `true`, thereby corresponding with the * default binding for the result type to `FabricValuePlus`. * * Any visitor with a non-default `PlusType` or `ResultType` _must_ override * this method if the default could cause the engine to return a type-lying * value _and_ the calling client cares about avoiding type lies. */ isResultType( _value: FabricValuePlus | FabricValuePlus, ): _value is ResultType { return true; } /** * @inheritDoc * * If not overridden, this throws an error indicating that visiting cycles is * not supported. Rationale: This is the safe choice. */ visitCycle( value: FabricContainerValuePlus, _tag: FabricContainerValueTag, _originalDepth: number, _thisDepth: number, ): VisitResult { this.throwNoCycles(value); } /** * Throws an error indicating that this visitor does not handle cycles. */ protected throwNoCycles(value: FabricValuePlus): never { throw new Error(debugStr`Cannot visit cyclic value: $quote${value}`); } /** * Throws a "shouldn't happen" error, indicating a particular method should * not have been called. */ protected throwShouldntCall(methodName: string): never { throw new Error( debugStr`Shouldn't happen: \`${methodName}()\` called on $quote${this}`, ); } static { Object.freeze(this); Object.freeze(this.prototype); } }