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);
}