import { JSONObject, Serializable, SERIALIZABLE_CLASS_MAP,} from "./serializable.ts";import { ERROR_FAILED_TO_RESOLVE_POLYMORPHIC_CLASS } from "./error_messages.ts";import { fromJSONDefault } from "./strategy/from_json/default.ts";
export type InitializerFunction = () => Serializable;
export function PolymorphicResolver(): PropertyDecorator { return ( target: unknown, propertyKey: string | symbol, ): void => { registerPolymorphicResolver( target, (target as Record<typeof propertyKey, () => Serializable>)[ propertyKey as string ], ); };}
export type ResolverFunction = ( json: string | JSONObject,) => Serializable | null;
const POLYMORPHIC_RESOLVER_MAP = new Map<unknown, ResolverFunction>();
function registerPolymorphicResolver( classPrototype: unknown, resolver: ResolverFunction,): void { POLYMORPHIC_RESOLVER_MAP.set(classPrototype, resolver);}
export function PolymorphicSwitch( initializerFunction: InitializerFunction, propertyValueTest: PropertyValueTest,): PropertyDecorator;
export function PolymorphicSwitch<T>( initializerFunction: InitializerFunction, value: Exclude<T, PropertyValueTest>,): PropertyDecorator;
export function PolymorphicSwitch( initializerFunction: InitializerFunction, valueOrTest: PropertyValueTest | unknown,): PropertyDecorator { return ( target: unknown, propertyKey: string | symbol, ) => { registerPolymorphicSwitch( Object.getPrototypeOf(target).constructor, target, propertyKey, valueOrTest, initializerFunction, ); };}
const POLYMORPHIC_SWITCH_MAP = new Map<unknown, Set<PolymorphicClassOptions>>();
type PolymorphicClassOptions = { classDefinition: unknown; propertyKey: string | symbol; propertyValueTest: PropertyValueTest; initializer: InitializerFunction;};
export type PropertyValueTest = (propertyValue: unknown) => boolean;
function registerPolymorphicSwitch<T>( parentClassConstructor: unknown, classDefinition: unknown, propertyKey: string | symbol, propertyValueTest: PropertyValueTest, initializer: InitializerFunction,): void;
function registerPolymorphicSwitch<T>( parentClassConstructor: unknown, classDefinition: unknown, propertyKey: string | symbol, propertyValue: Exclude<T, PropertyValueTest>, initializer: InitializerFunction,): void;
function registerPolymorphicSwitch( parentClassConstructor: unknown, classDefinition: unknown, propertyKey: string | symbol, valueOrTest: PropertyValueTest | unknown, initializer: InitializerFunction,): void { let classPropertiesSet = POLYMORPHIC_SWITCH_MAP.get(parentClassConstructor);
if (!classPropertiesSet) { classPropertiesSet = new Set<PolymorphicClassOptions>(); POLYMORPHIC_SWITCH_MAP.set(parentClassConstructor, classPropertiesSet); }
if (valueOrTest instanceof Function) { classPropertiesSet.add({ classDefinition, propertyKey, propertyValueTest: valueOrTest as PropertyValueTest, initializer, }); } else { classPropertiesSet.add({ classDefinition, propertyKey, propertyValueTest: (propertyValue) => propertyValue === valueOrTest, initializer, }); }}
function resolvePolymorphicSwitch( parentClassConstructor: unknown, json: string | JSONObject,): Serializable | null { const classOptionsSet = POLYMORPHIC_SWITCH_MAP.get( parentClassConstructor, );
if (!classOptionsSet) { return null; }
const _json = typeof json === "string" ? JSON.parse(json) : json;
for ( const { classDefinition, propertyKey, propertyValueTest, initializer, } of classOptionsSet.values() ) { const classMap = SERIALIZABLE_CLASS_MAP.get( classDefinition, );
if (!classMap) { continue; }
const serializePropertyOptions = classMap.getByPropertyKey(propertyKey);
if (!serializePropertyOptions) { continue; }
const fromJSONStrategy = serializePropertyOptions.fromJSONStrategy || fromJSONDefault; const deserializedValue = fromJSONStrategy( _json[serializePropertyOptions.serializedKey], );
if (propertyValueTest(deserializedValue)) { return initializer(); } }
return null;}
export function polymorphicClassFromJSON<T extends Serializable>( classPrototype: unknown & { prototype: T }, json: string | JSONObject,): T { return resolvePolymorphicClass(classPrototype, json).fromJSON(json);}
function resolvePolymorphicClass<T extends Serializable>( classPrototype: unknown & { prototype: T }, json: string | JSONObject,): T { const classResolver = POLYMORPHIC_RESOLVER_MAP.get(classPrototype); if (classResolver) { return classResolver(json) as T; }
const resolvedClass = resolvePolymorphicSwitch(classPrototype, json);
if (resolvedClass) { return resolvedClass as T; }
throw new Error(ERROR_FAILED_TO_RESOLVE_POLYMORPHIC_CLASS);}