Home Reference Source

src/util/util.js

const has = (o, k) => Object.prototype.hasOwnProperty.call(o, k);
const isObject = d => typeof d === 'object' && d !== 'null';
const CustomError = require('./error');
const { DataTypes } = require('./constants');

/**
 * Contains various utility methods
 * @private
 */
class Util {
    constructor() {
        throw new CustomError(`The class ${this.constructor.name} may not be initialized`, 'STATIC_CLASS');
    }

    /**
     * Sets default properties on an object that aren't already specified
     * @param {Object} def Default properties
     * @param {Object} given Object to assign defaults to
     * @returns {Object}
     * @private
     */
    static mergeDefault(def, given) {
        if (!given) return def;
        for (const key in def) {
            if (!has(given, key) || given[key] === undefined) {
                given[key] = def[key];
            } else if (given[key] === Object(given[key])) {
                given[key] = Util.mergeDefault(def[key], given[key]);
            }
        }
        return given;
    }

    /**
     * Checks a table name against basic SQL naming rules
     * @param {string} name The name of the table
     * @returns {boolean}
     * @private
     */
    static checkTableName(name) {
        if (name.length > 18) throw new CustomError(`The name ${name} is invalid: Above 18 characters`, 'INVALID_TABLE_NAME');
        if (/^[0-9]/.test(name)) throw new CustomError(`The name ${name} is invalid: Starts with a number`, 'INVALID_TABLE_NAME');
        if (!/^[a-zA-Z0-9_]+$/.test(name)) throw new CustomError(`The name ${name} is invalid: Contains invalid character ${name.replace(/[a-zA-Z0-9_]+/, '').slice(0, 1)}`, 'INVALID_TABLE_NAME');
    }

    /**
     * Flatten an object. Any properties that are Maps will get converted to an array of keys
     * @param {Object} obj The object to flatten
     * @param {...Object<string, boolean|string>} [props] Specific properties to include/exclude
     * @returns {Object}
     * @private
     */
    static flatten(obj, ...props) {
        if (!isObject(obj)) return obj;

        props = Object.assign(...Object.keys(obj).filter(k => !k.startsWith('_')).map(k => ({ [k]: true })), ...props);

        const out = {};

        // eslint-disable-next-line prefer-const
        for (let [prop, newProp] of Object.entries(props)) {
            if (!newProp) continue;

            newProp = newProp === true ? prop : newProp;

            const element = obj[prop];
            const elemIsObj = isObject(element);
            const valueOf = elemIsObj && typeof element.valueOf === 'function' ? element.valueOf() : null;

            if (element instanceof Map) out[newProp] = this.mapMap(element, e => Util.flatten(e));
            else if (Array.isArray(element)) out[newProp] = element.map(e => Util.flatten(e));
            else if (typeof valueOf !== 'object') out[newProp] = valueOf;
            else if (!elemIsObj) out[newProp] = element;
        }

        return out;
    }

    /**
     * Does the same as [Array.map()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map)
     * @param {Map} map Map to map
     * @param {Function} fn Function that produces an element of the new array, taking three arguments
     * @param {*} [thisArg] Value to use as `this` when executing function
     * @returns {Array}
     * @private
     */
    static mapMap(map, fn, thisArg) {
        if (typeof thisArg !== 'undefined') fn = fn.bind(thisArg);
        const arr = new Array(map.size);
        let i = 0;
        for (const [key, val] of map) arr[i++] = fn(val, key, map);
        return arr;
    }

    /**
     * Parses an SQL string into an array of {@link KeyOptions}
     * @param {string} string String to parse
     * @param {string} tableName The name of the table (used internally)
     * @returns {KeyOptions[]}
     * @private
     */
    static _parseSql(string, tableName) {
        string = string.replace(new RegExp(`${tableName}`, 'g'), '');
        console.log(string);
        const strArr = string.split(' ');
        strArr.splice(0, 2);
        console.log(strArr);
    }
}

module.exports = Util;