import assert from 'assert' import {ValidationFailure} from '../error' import {GetCastType, GetSrcType, Validator} from '../interface' import {Simplify} from '../util' export type GetOneOfCast
= Simplify<{ [K in keyof P]: GetCastType
}[keyof P]> export type GetOneOfSrc
= Simplify<{ [K in keyof P]: GetSrcType
}[keyof P]> export class OneOfValidator
>> implements Validator > {
private patternNames: string[]
private errors: (ValidationFailure | undefined)[]
constructor(public readonly patterns: P) {
this.patternNames = Object.keys(this.patterns)
this.errors = new Array(this.patternNames.length)
assert(this.patternNames.length > 1)
}
private clearErrors(): void {
for (let i = 0; i < this.errors.length; i++) {
this.errors[i] = undefined
}
}
cast(value: unknown): ValidationFailure | GetOneOfCast {
for (let i = 0; i < this.patternNames.length; i++) {
let key = this.patternNames[i]
let validator = this.patterns[key]
let result = validator.cast(value)
if (result instanceof ValidationFailure) {
this.errors[i] = result
} else {
this.clearErrors()
return result
}
}
let failure = new OneOfValidationFailure(value, this.errors.slice() as ValidationFailure[], this.patternNames)
this.clearErrors()
return failure
}
validate(value: unknown): ValidationFailure | undefined {
for (let i = 0; i < this.patternNames.length; i++) {
let key = this.patternNames[i]
let validator = this.patterns[key]
let err = validator.validate(value)
if (err) {
this.errors[i] = err
} else {
this.clearErrors()
return
}
}
let failure = new OneOfValidationFailure(value, this.errors.slice() as ValidationFailure[], this.patternNames)
this.clearErrors()
return failure
}
phantom(): GetOneOfSrc {
throw new Error()
}
}
export class OneOfValidationFailure extends ValidationFailure {
constructor(value: unknown, public errors: ValidationFailure[], public patternNames: string[]) {
super(value, 'given value does not match any of the expected patterns')
}
toString(): string {
let msg = 'given value does not match any of the expected patterns:'
for (let i = 0; i < this.patternNames.length; i++) {
msg += `\n ${this.patternNames[i]}: ${this.errors[i].toString()}`
}
if (this.path.length) {
msg = `invalid value at ${this.getPathString()}: ${msg}`
}
return msg
}
}