import type { ObservableSet } from "mobx" import type { ModelPropTransform } from "../../modelShared/prop" import { isSet } from "../../utils" import { asSet } from "../../wrappers/asSet" import { typesArray } from "../arrayBased/typesArray" import type { AnyType, ArrayType, TypeToData } from "../schemas" import { TypeCheckerBaseType } from "../TypeChecker" import type { CodecFromEncoded, RuntimeAdapter } from "./typesCodecCore" import { createCodecType } from "./typesCodecCore" import { resolveCodecSupport } from "./typesCodecSupport" function makeArrayBackedSetTransform( valueAdapter: RuntimeAdapter ): ModelPropTransform, Set | ObservableSet> { const storedByRuntime = new WeakMap | ObservableSet, Array>() return { transform({ originalValue, cachedTransformedValue }) { if (cachedTransformedValue) { return cachedTransformedValue } const storedSet = asSet(originalValue) const runtimeSet: Set | ObservableSet = new Proxy(storedSet, { get(target, prop, receiver) { switch (prop) { case "has": return (value: TRuntime) => target.has(valueAdapter.toStored(value)) case "add": return (value: TRuntime) => { target.add(valueAdapter.toStored(value)) return receiver } case "delete": return (value: TRuntime) => target.delete(valueAdapter.toStored(value)) case "clear": return () => target.clear() case "size": return target.size case "forEach": return ( callback: ( value: TRuntime, key: TRuntime, set: Set | ObservableSet ) => void, thisArg?: unknown ) => { target.forEach((value) => { const runtimeValue = valueAdapter.toRuntime(value) callback.call(thisArg, runtimeValue, runtimeValue, receiver) }) } case "entries": return function* () { for (const value of target.values()) { const runtimeValue = valueAdapter.toRuntime(value) yield [runtimeValue, runtimeValue] as const } } case "keys": case "values": case Symbol.iterator: return function* () { for (const value of target.values()) { yield valueAdapter.toRuntime(value) } } default: return Reflect.get(target, prop, receiver) } }, }) as unknown as Set | ObservableSet storedByRuntime.set(runtimeSet, originalValue) return runtimeSet }, untransform({ transformedValue }) { const cachedStored = storedByRuntime.get(transformedValue) if (cachedStored) { return cachedStored } return Array.from(transformedValue.values()).map((value) => valueAdapter.toStored(value)) }, } } export function typesSetFromArray( valueType: TValueType ): CodecFromEncoded< ArrayType, Set> | ObservableSet> > { const valueSupport = resolveCodecSupport(valueType) return createCodecType( { typeName: "setFromArray", encodedType: typesArray(valueSupport.storedType), is(value): value is Set> | ObservableSet> { return isSet(value) }, ...makeArrayBackedSetTransform(valueSupport.adapter), }, TypeCheckerBaseType.Object ) }