chamesu
Version:
This package contains all packages of the chat application.
155 lines (132 loc) • 5.35 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 {IValidator, ValidationResult} from "./index";
import {keys} from "memory-cache";
type StringRule =
| { type: "equal", value: string }
| { type: "notEqual", value: string }
| { type: "minLength", min: number }
| { type: "maxLength", max: number }
export class StringValidator implements IValidator<string> {
constructor(private rules?: StringRule[]) {
if (!Array.isArray(this.rules)) {
this.rules = [];
}
}
/**
* Adds a rule to the array of rules, or replaces a rule if it already exists.
* Use this function to prevent having multiple rules of the same type.
*/
private addRule: (rule: StringRule) => StringRule[] = rule => {
// Filter the current rule set, removing any rule that has the same type of the one being added
const filtered = this.rules.filter(r => r.type !== rule.type);
// Add the new rule to the filtered rule array
return [...filtered, rule]
}
/**
* Fails if the value being validated is not equal to @param value.
*/
equals: (value: string) => StringValidator = value => {
this.rules = this.addRule({ type: "equal", value: value });
return this;
}
/**
* Fails if the value being validated is equal to @param value.
*/
notEquals: (value: string) => StringValidator = value => {
this.rules = this.addRule({ type: "notEqual", value: value });
return this;
}
/**
* Fails if the string's length is less than @param min.
*/
minLength: (min: number) => StringValidator = min => {
this.rules = this.addRule({ type: "minLength", min: min });
return this;
}
/**
* Fails if the string's length is greater than @param max.
*/
maxLength: (max: number) => StringValidator = max => {
this.rules = this.addRule({ type: "maxLength", max: max });
return this;
}
/**
* Fails if the string is empty.
*/
notEmpty: () => StringValidator = () => {
// We don't need to use a specific rule for notEmpty here, we can just set a min length of 1!
this.rules = this.addRule({ type: "minLength", min: 1 });
return this;
}
/**
* Fails if the string is not empty. NOTE that an empty string is _not_ the same as a null or undefined value.
*/
empty: () => StringValidator = () => {
// Again, we don't need a specific rule for empty, we just set a max length of 0
this.rules = this.addRule({ type: "maxLength", max: 0 });
return this;
}
/**
* Checks an individual rule against the value being validated.
*/
checkRule: (rule: StringRule, value: string, key: string) => ValidationResult<string> = (rule: StringRule, value : string, key: string) => {
const err = (msg: string) : ValidationResult<string> => ({ success: false, message: msg });
const ok = () : ValidationResult<string> => ({ success: true, value: value });
switch (rule.type) {
case "equal":
return rule.value !== value
? err(`${key} was expected to be ${rule.value} but was ${value}.`)
: ok();
case "notEqual":
return rule.value === value
? err(`${key} must not be ${rule.value}.`)
: ok();
case "minLength":
return value.length < rule.min
? err(`${key} length must be greater than or equal to ${rule.min} but was ${value.length}.`)
: ok();
case "maxLength":
return value.length > rule.max
? err(`${key} length must be less than or equal to ${rule.max} but was ${value.length}.`)
: ok();
}
}
go: (value: unknown, key?: string) => ValidationResult<string> = (value, key) => {
// Since the value is unknown, we must check that the type is string before validating each rule
key = key ?? 'value';
if (value === null) {
return {
success: false,
message: "expected "+key+" to be string but received null."
}
} else if (value === undefined) {
return {
success: false,
message: "expected "+key+" to be string but received undefined."
}
} else if (typeof value !== "string") {
return {
success: false,
message: `expected ${key} to be string but received ${typeof value}.`
}
}
// TypeScript compiler now knows that value is a string
// Iterate over all rules and short-circuit to return an error if any rule fails
for (let rule of this.rules) {
const result = this.checkRule(rule, value, key);
if (result.success === false) {
return result;
}
}
// If none of the rules in the loop had an error, the value passed validation!
return {
success: true,
value: value
}
}
}