n4s
Version:
typed schema validation version of enforce
67 lines (59 loc) • 1.89 kB
text/typescript
import { greaterThan } from 'vest-utils';
import { RuleInstance } from '../../utils/RuleInstance';
import { RuleRunReturn } from '../../utils/RuleRunReturn';
const REQUIRED_COUNT = 1;
/**
* Validates that a value passes exactly one of the provided rules.
* Exactly one rule must pass - not zero, not two or more.
* All rules are evaluated to count passing rules.
*
* @template T - The value type to validate
* @param value - The value to validate
* @param rules - One or more RuleInstances where exactly one must pass
* @returns RuleRunReturn indicating success or failure
*
* @example
* ```typescript
* // Eager API
* enforce(5)
* .oneOf(
* enforce.lessThan(10),
* enforce.greaterThan(100)
* ); // passes (only first rule passes)
*
* // Lazy API - accept either type but not both
* const stringOrNumberRule = enforce.oneOf(
* enforce.isString(),
* enforce.isNumber()
* );
*
* stringOrNumberRule.test('hello'); // true
* stringOrNumberRule.test(42); // true
* stringOrNumberRule.test(true); // false (no rules pass)
*
* // More complex example - exclusive validation
* const exclusiveRule = enforce.oneOf(
* enforce.equals(null),
* enforce.isString().longerThan(0)
* );
*
* exclusiveRule.test(null); // true (exactly one passes)
* exclusiveRule.test('hello'); // true (exactly one passes)
* exclusiveRule.test(''); // false (neither passes)
* ```
*/
export function oneOf<T>(value: T, ...rules: any[]): RuleRunReturn<T> {
let passingCount = 0;
rules.some(rule => {
const res = rule.run(value);
if (res.pass) {
passingCount++;
}
if (greaterThan(passingCount, REQUIRED_COUNT)) {
return RuleRunReturn.Failing(value);
}
});
return RuleRunReturn.create(passingCount === REQUIRED_COUNT, value);
}
// Type for oneOf rule instance
export type OneOfRuleInstance<T> = RuleInstance<T, [T]>;