import type { KeyError } from "@fncts/schema/ParseError"; import type { Sized } from "@fncts/test/control/Sized"; import { ASTAnnotation } from "@fncts/schema/ASTAnnotation"; export function hashSet(value: Schema): Schema> { return Schema.declaration(Vector(value), hashSetParser(true), hashSetParser(false)) .annotate(ASTAnnotation.Identifier, `HashSet<${value.show()}>`) .annotate(ASTAnnotation.GenHook, gen); } /** * @tsplus static fncts.schema.SchemaOps hashSetFromArray */ export function hashSetFromArray(value: Schema): Schema> { return Schema.array(value).transform( hashSet(value), (input) => { const out = HashSet.empty().beginMutation; for (const v of input) { out.add(v); } return out.endMutation; }, (input) => { const out: Array = []; input.forEach((v) => { out.push(v); }); return out; }, ); } /** * @tsplus derive fncts.schema.Schema[fncts.HashSet]<_> 10 */ export function deriveHashSet>( ...[value]: [A] extends [HashSet] ? Check>> extends Check.True ? [value: Schema] : never : never ): Schema { return unsafeCoerce(hashSetFromArray(value)); } function hashSetParser(isDecoding: boolean) { return (value: Schema): Parser> => { const schema = hashSet(value); const parseValue = isDecoding ? value.decode : value.encode; return Parser.make((u, options) => { if (!HashSet.is(u)) { return ParseResult.fail(ParseError.TypeError(schema.ast, u)); } const allErrors = options?.allErrors; const errors = Vector.emptyPushable(); const out = HashSet.empty().beginMutation; for (const v of u) { const tv = parseValue(v, options); Either.concrete(tv); if (tv.isLeft()) { errors.push(ParseError.KeyError(value.ast, v, tv.left)); if (!allErrors) { return ParseResult.fail(ParseError.IterableError(schema.ast, u, errors)); } continue; } out.add(tv.right); } return errors.isNonEmpty() ? ParseResult.fail(ParseError.IterableError(schema.ast, u, errors)) : ParseResult.succeed(out.endMutation); }); }; } function gen(value: Gen): Gen> { return Gen.array(value).map(HashSet.from); }