ArgvObject.js

/**
 * @module @alexeyp0708/argv_patterns
 */

import {ArgvArray} from './export.js';

/**
 *  Class ArgvObject
 *  @prop {ArgvElement[]} commands - command list
 *  @prop {Object.<string,ArgvElement>} options - set of options
 */

export class ArgvObject {
    /**
     * @param {string|Array|ArgvArray|undefined} params
     * @returns {ArgvObject}
     */
    constructor(param) {
        Object.defineProperties(this, {
            commands: {
                enumerable: true,
                writable: true,
                value: []
            },
            options: {
                enumerable: true,
                writable: true,
                value: {}
            }
        });
        let self = this;
        if (param instanceof ArgvArray) {
            self = param.toObject();
        } else if (param instanceof Array || typeof param === 'string') {
            self = new ArgvArray(param).toObject();
        }
        if (self !== this) {
            this.commands.splice(0, 0, ...self.commands);
            Object.assign(this.options, self.options);
        }
    }

    /**
     * Converts this object to [array .ArgvArray]
     * @returns {ArgvArray}
     */
    toArray() {
        return Argv.objectToArray(this);
    }

    /**
     * Converts this object to command line
     * @returns {string}
     */
    toString() {
        return Argv.elementsToString(this.toArray());
    }

    /**
     * search argv element
     * @param {string|object|ArgvElement} element  where string is the command line element for the parser.
     * If an object, then this is a set of element parameters (template for ArgvElement)
     * @param {object|ArgvElement} params if 1 argument is string, it is used as parameter setting for the element (template for  [object .ArgvElement])
     * @returns {ArgvElement|boolean}  If false, then the item was not found. If found, it returns  [object .ArgvElement]
     *
     * @exemple
     * ```js
     *  let argv=new ArgvObject('command1 command2 --option1=value');
     *  argv.get('command2'); // result =>[object ArgvElement]
     *  argv.get('command2',{order:2});//result => [object ArgvElement]
     *  argv.get('command2',{order:1});//result => false
     *  argv.get('--option'); // result=>[object ArgvElement]
     *  argv.get('--option=value');// result=>[object ArgvElement]
     *  argv.get('--option=val');// result=>false
     * ```
     */
 /*   get(element, params = undefined) {
        if (!(element instanceof Argv.elementClass)) {
            element = new Argv.elementClass(element, params);
        } else if (params instanceof Object) {
            element.setParams(params);
        }
        let check = false;
        if (element.type === 'command') {
            let order = 0;
            this.commands.forEach((value, key) => {
                order++;
                if (element.order !== undefined && element.order === order) {
                    if (element.key !== undefined) {
                        if (element.key === value.key) {
                            check = this.commands[key];
                        }
                    } else {
                        check = this.commands[key];
                    }
                    return;
                } else if (element.order === undefined) {
                    if (element.key === value.key) {
                        check = this.commands[key];
                    }
                }
            });
        } else if (element.type === 'option') {
            let keys = Object.keys(this.options);
            let ekey;
            if (element.key !== undefined && keys.includes(element.key)) {
                ekey = element.key;
            } else if (element.shortKey && keys.includes(element.shortKey)) {
                ekey = element.shortKey;
            }
            if (ekey !== undefined) {
                if (![undefined, true].includes(element.value)) {
                    if (element.value === this.options[ekey].value) {
                        check = this.options[ekey];
                    }
                } else {
                    check = this.options[ekey];
                }
            }
        }
        delete element.order;
        return check;
    }*/

    /**
     * Sets a new parameter or overwrites the properties of the specified parameter
     * @param {string|object|ArgvElement} element where string is the command line element for the parser.
     * If an object, then this is a set of element parameters (template for {@link ArgvElement})
     * @param {object|ArgvElement} params if 1 argument is string, it is used as parameter setting for the element (template for  [object .ArgvElement])
     * @returns {this}
     * @exemple
     * ```js
     * let argv=new ArgvObject();
     * argv
     *      .set('command',{order:1,description:'test command'})
     *
     *      // if the element type is "command" and you specify an order (command position),
     *      //then the parameters of the command element at the corresponding position will be overwritten
     *      // If such a position does not exist, then the command will be added to the end of the array.
     *      // Warning: the order is intended to overwrite or set the next command.
     *      .set({
     *          type:'command'
     *          order:1, // command position. Example  command  order -`order1 --option order2 -o order3`
     *          key:'new_command',
     *      })
     *      // search for command new_command and set description
     *      .set({
     *          type:'command',
     *          key:'new_command',
     *          description:'Overwrite description for command'     *
     *      })
     *      // search for option "option" and set parameter "required"
     *      .set('--option',{required:true})
     *      .set('-o=value') // search for option "o" and set value
     *
     * ```
     */
/*    set(element, params = undefined) {
        if (!(element instanceof Argv.elementClass)) {
            element = new Argv.elementClass(element, params);
        } else if (params instanceof Object) {
            element.setParams(params);
        }
        let find = Object.assign({}, element);
        if (find.type === 'command' && find.order !== undefined) {
            find.key = undefined;
        } else if (find.type === 'option') {
            find.value = undefined;
        }
        let check = this.get(find);
        delete element.order;
        if (check === false) {
            if (element.type === 'command') {
                this.commands.push(element);
            } else if (element.type === 'option') {
                let ekey = element.key ?? element.shortKey;
                this.options[ekey] = element;
            }
        } else {
            check.setParams(element);
        }
        return this;
    }*/


    /**
     * Add a new parameter
     * @param {string|object|ArgvElement} element  where string is the command line element for the parser.
     * If an object, then this is a set of element parameters (template for  [object .ArgvElement])
     * @param {object|ArgvElement} params if 1 argument is string, it is used as parameter setting for the element (template for  [object .ArgvElement])
     * @returns {this}
     * @exemple
     * ```js
     * let argv=new ArgvObject();
     * argv
     *      .add('command',{description:'test command'})
     *      .add('--option',{required:true})
     *      .add('-o=value')
     * ```
     */
/*    add(element, params = undefined) {
        if (!(element instanceof Argv.elementClass)) {
            element = new Argv.elementClass(element, params);
        } else if (params instanceof Object) {
            element.setParams(params);
        }
        let find = Object.assign({}, element);
        if (find.type === 'command' && find.order !== undefined) {
            find.key = undefined;
        } else if (find.type === 'option') {
            find.value = undefined;
        }
        let check = this.get(find);
        delete element.order;
        if (check === false) {
            if (element.type === 'command') {
                this.commands.push(element);
            } else if (element.type === 'option') {
                let ekey = element.key ?? element.shortKey;
                this.options[ekey] = element;
            }
        }
        return this;
    }*/

    /**
     * Converting [array .ArgvArray] to [object .ArgvObject]
     * @param {ArgvArray} argv
     * @returns {ArgvObject}
     */
    static elementsToObject(argv) {
        let result = new ArgvObject();
        if (!(argv instanceof ArgvArray)) {
            throw new Error('Bad argument. Must be [object ArgvArray]');
        }
        for (let param of argv) {
            if (param.type === 'command') {
                result.commands.push(param);
            } else if (param.type === 'option') {
                if (param.key !== undefined) {
                    result.options[param.key] = param;
                } else if (param.shortKey !== undefined) {
                    result.options[param.shortKey] = param;
                }
            }
        }
        result.commands.sort((a, b) => {
            return a.order - b.order;
        });
        return result;
    }


    /**
     * Converting [object .ArgvObject] to [array .ArgvArray]
     * @param {ArgvObject} argv
     * @returns {ArgvArray}
     * @throws
     * Error: Bad argument - Invalid argument.
     */
    static objectToArray(argv) {
        if (!argv instanceof ArgvObject) {
            throw new Error('Bad argument. Must be [object ArgvObject] ');
        }
        let result = new ArgvArray();
        for (let command of argv.commands) {
            result.push(command);
        }
        for (let key in argv.options) {
            let option = argv.options[key];
            let check = false;
            if (!result.includes(option)) {
                result.push(option);
            }
        }
        return result;
    }
}