import type { IndexError } from "@fncts/schema/ParseError";
import type { Sized } from "@fncts/test/control/Sized";
/**
* @tsplus static fncts.schema.SchemaOps list
* @tsplus getter fncts.Schema.Schema list
*/
export function list(value: Schema): Schema> {
return Schema.declaration(Vector(value), parser(true), parser(false))
.annotate(ASTAnnotation.Identifier, `List<${value.show()}>`)
.annotate(ASTAnnotation.GenHook, gen);
}
/**
* @tsplus static fncts.schema.SchemaOps listFromArray
* @tsplus getter fncts.Schema.Schema listFromArray
*/
export function listFromArray(value: Schema): Schema> {
return Schema.array(value).transform(
list(value),
(input) => List.from(input),
(input) => Array.from(input),
);
}
/**
* @tsplus derive fncts.schema.Schema[fncts.List]<_> 10
*/
export function deriveList>(
...[value]: [A] extends [List]
? Check>> extends Check.True
? [value: Schema<_A>]
: never
: never
): Schema {
return unsafeCoerce(listFromArray(value));
}
function parser(isDecoding: boolean) {
return (value: Schema): Parser> => {
const schema = list(value);
const parseValue = isDecoding ? value.decode : value.encode;
return Parser.make((u, options) => {
if (!List.is(u)) {
return ParseResult.fail(ParseError.TypeError(schema.ast, u));
}
const out = new ListBuffer();
const errors = Vector.emptyPushable();
const allErrors = options?.allErrors;
let index = 0;
for (const v of u) {
const t = parseValue(v, options);
Either.concrete(t);
if (t.isLeft()) {
errors.push(ParseError.IndexError(index, t.left));
index++;
if (allErrors) {
continue;
}
return ParseResult.fail(ParseError.IterableError(schema.ast, u, errors));
} else {
out.append(t.right);
}
index++;
}
return errors.isNonEmpty()
? ParseResult.fail(ParseError.IterableError(schema.ast, u, errors))
: ParseResult.succeed(out.toList);
});
};
}
function gen(value: Gen): Gen> {
return Gen.array(value).map((array) => List.from(array));
}