import { Arr, memoizeFunction, Result } from 'ts-data-forge';
import { type Type } from '../type.mjs';
import {
createAssertFn,
createCastFn,
createIsFn,
createPrimitiveValidationError,
prependIndexToValidationErrors,
type ValidationError,
} from '../utils/index.mjs';
export const array = (
elementType: Type,
options?: Partial<
Readonly<{
typeName: string;
defaultValue: readonly A[];
}>
>,
): Type => {
type T = readonly A[];
const typeName = options?.typeName ?? `${elementType.typeName}[]`;
const getDefaultValue = memoizeFunction((): T => options?.defaultValue ?? []);
const validate: Type['validate'] = (a) => {
if (!Arr.isArray(a)) {
return Result.err([
createPrimitiveValidationError({
actualValue: a,
expectedType: 'array',
typeName,
details: undefined,
}),
]);
}
const errors: readonly ValidationError[] = Arr.generate(function* () {
for (const [index, el] of a.entries()) {
const res = elementType.validate(el);
if (Result.isErr(res)) {
yield* prependIndexToValidationErrors(res.value, index);
}
}
});
if (Arr.isNonEmpty(errors)) {
return Result.err(errors);
}
// eslint-disable-next-line total-functions/no-unsafe-type-assertion
return Result.ok(a as T);
};
const fill: Type['fill'] = (a) =>
!Arr.isArray(a) ? getDefaultValue() : a.map((e) => elementType.fill(e));
const prune = (a: T): T => a.map((e) => elementType.prune(e));
return {
typeName,
get defaultValue() {
return getDefaultValue();
},
fill,
prune,
validate,
is: createIsFn(validate),
assertIs: createAssertFn(validate),
cast: createCastFn(validate),
};
};