chamesu
Version:
This package contains all packages of the chat application.
79 lines (64 loc) • 3.22 kB
text/typescript
/*
* Copyright (c) 2022.
* Author Peter Placzek (tada5hi)
* For the full copyright and license information,
* view the LICENSE file that was distributed with this source code.
*/
import {isValidator, IValidator, ValidationResult, Shape, PropertyValidator} from "./index";
export class ShapeValidator<T extends object> implements IValidator<T> {
constructor(private shape: Shape<T>) { }
go(value: unknown, key?: string) : ValidationResult<T> {
const err: (msg: string) => ValidationResult<any> = msg => ({ success: false, message: msg });
// First check that the value is an object and not an array, null, undefined, etc
if (value === null || value === undefined) {
return err((key ?? 'value') + " cannot be null or undefined.");
} else if (Array.isArray(value)) {
return err((key ?? 'value') + " must be an object but was an array.");
} else if (typeof value !== "object") {
return err((key ?? 'value') + ` must be an object but was ${typeof value}.`);
}
// Get the keys of both the expected shape, and the value
const expectedKeys = Object.getOwnPropertyNames(this.shape);
const actualKeys = Object.keys(value);
// Check if any expected property is missing from the value
for (let expected of expectedKeys) {
if (actualKeys.indexOf(expected) === -1) {
//return err(`Value is missing expected property ${expected}.`);
}
}
// All properties are accounted for! Now loop through each validator on the expected shape and test the value
for (let expected of expectedKeys) {
const result : ValidationResult<T> = this.validateShape(expected as keyof T, value, key);
if (result.success === false) return result;
}
// All validation passed!
return {
success: true,
value: value as T
}
}
private validateShape(key: keyof T, value: unknown, parentKey?: string) : ValidationResult<T> {
const validator = this.shape[key];
const propertyValue: unknown = value[key];
let propertyKey = '';
if(typeof parentKey !== 'undefined') propertyKey += parentKey+'.';
propertyKey += key;
return this.evaluateValidator(validator, propertyValue, propertyKey);
}
protected evaluateValidator(propertyValidator: PropertyValidator<any>, propertyValue: unknown, propertyKey: string) : ValidationResult<T> {
const result : ValidationResult<T> | IValidator<T> | boolean =
isValidator(propertyValidator) ?
propertyValidator.go(propertyValue, propertyKey) :
propertyValidator(propertyValue, propertyKey);
if(typeof result === 'boolean') {
if(!result) return {success: false, message: propertyKey + ' is not valid.'};
} else {
if(isValidator(result)) {
return this.evaluateValidator(result, propertyValue, propertyKey);
} else {
if (result.success === false) return result;
}
}
return {success: true, value: propertyValue as T};
}
}