import { isSet } from '@sindresorhus/is'; import { Arr, memoizeFunction, Result } from 'ts-data-forge'; import { type AnyType, type Type, type TypeOf } from '../type.mjs'; import { createAssertFn, createCastFn, createIsFn, createPrimitiveValidationError, type ValidationError, } from '../utils/index.mjs'; type SetResultType = ReadonlySet>; export const SetType = ( elementType: T, options?: Partial< Readonly<{ typeName: string; }> >, ): Type> => { type S = SetResultType; const typeName = options?.typeName ?? 'Set'; const getDefaultValue = memoizeFunction((): S => new Set()); const validate: Type['validate'] = (a) => { if (!isSet(a)) { return Result.err([ createPrimitiveValidationError({ actualValue: a, expectedType: 'Set', typeName, details: undefined, }), ]); } const errors: readonly ValidationError[] = Arr.generate(function* () { for (const element of a) { const res = elementType.validate(element); if (Result.isErr(res)) { yield { path: [], actualValue: element, expectedType: typeName, typeName, details: { kind: 'set-element', expectedType: elementType.typeName, }, } satisfies ValidationError; yield* res.value; } } }); if (Arr.isNonEmpty(errors)) { return Result.err(errors); } return Result.ok(a); }; const fill: Type['fill'] = (a) => !isSet(a) ? getDefaultValue() : new Set(Array.from(a).filter((v) => elementType.is(v))); const prune = (a: S): S => // eslint-disable-next-line @typescript-eslint/no-unsafe-return new Set(Array.from(a, (e) => elementType.prune(e))); return { typeName, get defaultValue() { return getDefaultValue(); }, fill, prune, validate, is: createIsFn(validate), assertIs: createAssertFn(validate), cast: createCastFn(validate), }; };