src/Rule/index.js
- const ErrorCollector = require('./ErrorCollector');
- const { getErrorFromObject, getErrorFromFunctionOrString } = require('./util');
- const { TEST_FUNCTIONS, OPTIONAL } = require('../testFunctions');
- const { AND, OR, isObject } = require('./../util');
-
- const OPERATORS = {
- '&': AND,
- '|': OR,
- };
-
- /**
- * The Rule class validates only one value
- * once a rule is created it can be used multiple times
- */
- class Rule {
- /**
- *
- * @param {String|Object} obj the rule object it describes a the test that are ran by the Rule
- * @param {String} error the error returned when the tested input is not correct
- */
- constructor(obj, error) {
- if (typeof obj === 'string' || obj instanceof String) {
- this.rule = { type: obj };
- } else {
- this.rule = obj;
- }
- this.error = error;
- this.errorCollector = new ErrorCollector();
- this.testEntryObject();
- }
-
- /**
- *
- * @param {any} val the value to be tested
- * @param {Object|String} obj the error object or string thats showed on error
- * @param {String} path the path to the tested value this is used when
- * using validator to keep track of the prop value ex: obj.min
- *
- * @return {boolean}
- */
-
- test(val, obj, path) {
- this.errorCollector.clear();
- const types = this.getTypes();
- const operators = this.getRuleOperators();
- let ret = this.testOneRule(val, obj, types[0], path);
-
- for (let i = 1; i < types.length; i += 1) {
- const operator = operators[i] || operators[i - 1];
- ret = operator(ret, this.testOneRule(val, obj, types[i], path));
- }
- return ret;
- }
-
- /**
- * converts array from string if multiple types given in type
- * its the case for exemple int|float
- * @private
- * @return {[String]}
- */
-
- getTypes() {
- return this.rule.type.split(/[&|]/);
- }
-
- /**
- * Returns a list of the operators when multiple types given
- * its the case for example int|float
- * @private
- * @returns {[String]}
- */
- getRuleOperators() {
- const ret = [];
- const operators = this.rule.type.match(/[&|]/g) || '&';
- for (let i = 0; i < operators.length; i += 1) {
- ret.push(OPERATORS[operators[i]]);
- }
- return ret;
- }
-
- /**
- * @private
- * @param val value to be tested
- * @param {Object} obj error object
- * @param {String} type the type from getTypes()
- * @param {String} path the path to the value if Validator is used
- *
- * @returns {boolean}
- */
- testOneRule(val, obj, type, path) {
- if (Rule.TEST_FUNCTIONS[type].optional(val, this.rule.optional, obj) === true) {
- return true;
- }
-
- const keys = Object.keys(this.rule);
- keys.sort((key) => {
- if (key === 'type') return -1;
- return 0;
- });
-
- for (let i = 0; i < keys.length; i += 1) {
- const key = keys[i];
- const testFunction = Rule.TEST_FUNCTIONS[type][key];
-
- if (testFunction(val, this.rule[key], obj) === false && testFunction !== OPTIONAL) {
- this.errorCollector.collect(this.getError(path, val, key));
- return false;
- }
- }
- return true;
- }
-
- /**
- * Tests the validity of the constructor object
- * thows an error if the object is invalid
- */
-
- testEntryObject() {
- if (!this.rule.type) {
- throw Error('`type` is required');
- }
- const types = this.getTypes();
- types.forEach((type) => {
- this.testEntryObjectOneType(type);
- });
- }
-
- /**
- * Tests the validity of the constructor object
- * thows an error if the object is invalid
- * tests if all the keys are valid
- */
-
- testEntryObjectOneType(type) {
- const keys = Object.keys(this.rule);
- for (let i = 0; i < keys.length; i += 1) {
- const key = keys[i];
- if (!Rule.TEST_FUNCTIONS[type]) {
- throw Error(`The \`${type}\` type doesn't exist`);
- }
- if (!Rule.TEST_FUNCTIONS[type][key]) {
- throw new Error(`\`${type}\` doesn't have "${key}" test!`);
- }
- }
- }
-
- /**
- * returns a list of errors if they are present
- * @return {[String]}
- */
-
- getError(path, value, key) {
- if (isObject(this.error)) {
- return getErrorFromObject(this.error, path, value, key);
- }
- return getErrorFromFunctionOrString(this.error, path, value);
- }
-
- /**
- * Add custom rule to the Rule class
- * @param {String} name the name of the rule
- * @param {Function} rule the validation function
- */
- static addCustom(name, rule) {
- Rule.TEST_FUNCTIONS[name] = rule;
- Rule.TEST_FUNCTIONS[name].optional = OPTIONAL;
- }
- }
-
- Rule.TEST_FUNCTIONS = TEST_FUNCTIONS;
-
- module.exports = Rule;