import { isArray } from './isArray.js'; import { isDefined } from './isDefined.js'; import { isGreaterThan } from './isGreaterThan.js'; import { Failure, Predicate, Result, Success } from './Predicate.js'; /** * @desc Ensures that the `value` meets at least one of the provided {@link Predicate}s. * * @example * import { ensure, isEqualTo, isGreaterThan, isLessThan, or } from 'tiny-type'l * * class Percentage extends TinyType { * constructor(public readonly value: number) { * ensure('Percentage', value, or(isEqualTo(0), isGreaterThan(0)), or(isLessThan(100), isEqualTo(100)); * } * } * * @param {Predicate} predicates * @returns {Predicate} */ export function or(...predicates: Array>): Predicate { return new Or(predicates); } /** @access private */ class Or extends Predicate { constructor(private readonly predicates: Array>) { super(); const results = [ _ => isDefined().check(_), _ => isArray().check(_), _ => isGreaterThan(0).check(_.length), ]; if (results.some(check => check(this.predicates) instanceof Failure)) { throw new Error(`Looks like you haven't specified any predicates to check the value against?`); } } /** @override */ check(value: T): Result { const results = this.predicates.map(predicate => predicate.check(value)); const anySuccess = results.some(result => result instanceof Success); const failures = results.filter(_ => _ instanceof Failure) .map((_: Result) => (_ as Failure).description); return anySuccess ? new Success(value) : new Failure(value, describe(failures)); } } function describe(issues: string[]): string { return `either ${issues.join(', ').replace(/,([^,]*)$/, ' or$1')}`; }