cli/options/Option.js

/**
 * @module flitter-cli/options/Option
 */

/**
 * A CLI option. Supports basic comparative, and set-based validation.
 * @class
 */
class Option {
    /**
     * Instantiate the option.
     */
    constructor() {
        /**
         * Do we use the whitelist?
         * @type {boolean}
         * @private
         */
        this._use_whitelist = false

        /**
         * Do we use the blacklist?
         * @type {boolean}
         * @private
         */
        this._use_blacklist = false

        /**
         * Do we use the less-than comparison?
         * @type {boolean}
         * @private
         */
        this._use_less_than = false

        /**
         * Do we use the greater-than comparison?
         * @type {boolean}
         * @private
         */
        this._use_greater_than = false

        /**
         * Do we use the equality operator?
         * @type {boolean}
         * @private
         */
        this._use_equality = false

        /**
         * Is this option optional?
         * @type {boolean}
         * @private
         */
        this._optional = false

        /**
         * Whitelisted values.
         * @type {Array<*>}
         * @private
         */
        this._whitelist = []

        /**
         * Blacklisted values.
         * @type {Array<*>}
         * @private
         */
        this._blacklist = []

        /**
         * Value to be compared in less than.
         * @type {*}
         * @private
         */
        this._less_than_value = null

        /**
         * If true, the less than will be less than or equal to.
         * @type {boolean}
         * @private
         */
        this._less_than_bit = false

        /**
         * Value to be compared in greater than.
         * @type {*}
         * @private
         */
        this._greater_than_value = null

        /**
         * If true, the greater than will be greater than or equal to.
         * @type {boolean}
         * @private
         */
        this._greater_than_bit = false

        /**
         * The value to be used to check equality.
         * @type {*}
         * @private
         */
        this._equality_value = null
    }

    /**
     * Whitelist the specified item or items and enable the whitelist.
     * @param {...*} items - the items to whitelist
     */
    whitelist(...items) {
        this._use_whitelist = true
        items.forEach(item => this._whitelist.push(item))
    }

    /**
     * Blacklist the specified item or items and enable the blacklist.
     * @param {...*} items - the items to blacklist
     */
    blacklist(...items) {
        this._use_blacklist = true
        items.forEach(item => this._blacklist.push(item))
    }

    /**
     * Specifies the value to be used in less-than comparison and enables less-than comparison.
     * @param {*} value
     */
    less_than(value) {
        this._use_less_than = true
        this._less_than_value = value
    }

    /**
     * Specifies the value to be used in less-than or equal-to comparison and enables that comparison.
     * @param {*} value
     */
    less_than_equal_to(value) {
        this._less_than_bit = true
        this.less_than(value)
    }

    /**
     * Specifies the value to be used in greater-than comparison and enables that comparison.
     * @param {*} value
     */
    greater_than(value) {
        this._use_greater_than = true
        this._greater_than_value = value
    }

    /**
     * Specifies the value to be used in greater-than or equal-to comparison and enables that comparison.
     * @param {*} value
     */
    greater_than_equal_to(value) {
        this._greater_than_bit = true
        this.greater_than(value)
    }

    /**
     * Specifies the value to be used in equality comparison and enables that comparison.
     * @param {*} value
     */
    equals(value) {
        this._use_equality = true
        this._equality_value = value
    }

    /**
     * Checks if the specified value passes the configured comparisons.
     * @param {*} value
     * @returns {boolean}
     */
    validate(value) {
        let is_valid = true
        if ( this._use_equality ) {
            is_valid = is_valid && (this._equality_value === value)
        }

        if ( this._use_less_than ) {
            if ( this._less_than_bit ) {
                is_valid = is_valid && (value <= this._less_than_value)
            } else {
                is_valid = is_valid && (value < this._less_than_value)
            }
        }

        if ( this._use_greater_than ) {
            if ( this._greater_than_bit ) {
                is_valid = is_valid && (value >= this._greater_than_value)
            } else {
                is_valid = is_valid && (value > this._greater_than_value)
            }
        }

        if ( this._use_whitelist ) {
            is_valid = is_valid && this._whitelist.some(x => {
                return x === value
            })
        }

        if ( this._use_blacklist ) {
            is_valid = is_valid && !(this._blacklist.some(x => x === value))
        }

        return is_valid
    }

    /**
     * Sets the Option as optional.
     */
    optional() {
        this._optional = true
    }

    /**
     * Get the argument name. Should be overridden by child classes.
     * @returns {string}
     */
    get argument_name() {
        return false
    }

    /**
     * Get an array of strings denoting the human-readable requirements for this option to be valid.
     * @returns {Array<string>}
     */
    get requirement_displays() {
        const clauses = []

        if ( this._use_blacklist ) {
            clauses.push(`must not be one of: ${this._blacklist.map(x => String(x)).join(', ')}`)
        }

        if ( this._use_whitelist ) {
            clauses.push(`must be one of: ${this._whitelist.map(x => String(x)).join(', ')}`)
        }

        if ( this._use_greater_than ) {
            clauses.push(`must be greater than${this._greater_than_bit ? ' or equal to' : ''}: ${String(this._greater_than_value)}`)
        }

        if ( this._use_less_than ) {
            clauses.push(`must be less than${this._less_than_bit ? ' or equal to' : ''}: ${String(this._less_than_value)}`)
        }

        if ( this._use_equality ) {
            clauses.push(`must be equal to: ${String(this._equality_value)}`)
        }

        return clauses
    }
}

module.exports = exports = Option