"use strict";
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
// See LICENSE in the project root for license information.
Object.defineProperty(exports, "__esModule", { value: true });
exports.ScopedCommandLineAction = void 0;
const Constants_1 = require("../Constants");
const CommandLineAction_1 = require("./CommandLineAction");
const CommandLineParser_1 = require("./CommandLineParser");
const CommandLineParserExitError_1 = require("./CommandLineParserExitError");
/**
 * A CommandLineParser used exclusively to parse the scoped command-line parameters
 * for a ScopedCommandLineAction.
 */
class InternalScopedCommandLineParser extends CommandLineParser_1.CommandLineParser {
    constructor(options) {
        // We can run the parser directly because we are not going to use it for any other actions,
        // so construct a special options object to make the "--help" text more useful.
        const scopingArgs = [];
        for (const parameter of options.unscopedActionParameters) {
            parameter.appendToArgList(scopingArgs);
        }
        const unscopedToolName = `${options.toolFilename} ${options.actionOptions.actionName}`;
        const scopedCommandLineParserOptions = {
            toolFilename: `${unscopedToolName}${scopingArgs.length ? ' ' + scopingArgs.join(' ') : ''} --`,
            toolDescription: options.actionOptions.documentation,
            toolEpilog: `For more information on available unscoped parameters, use "${unscopedToolName} --help"`,
            enableTabCompletionAction: false
        };
        super(scopedCommandLineParserOptions);
        this._canExecute = false;
        this._internalOptions = options;
        this._internalOptions.onDefineScopedParameters(this);
    }
    get canExecute() {
        return this._canExecute;
    }
    onDefineParameters() {
        // No-op. Parameters are manually defined in the constructor.
    }
    async onExecute() {
        // override
        // Only set if we made it this far, which may not be the case if an error occurred or
        // if '--help' was specified.
        this._canExecute = true;
    }
}
/**
 * Represents a sub-command that is part of the CommandLineParser command-line.
 * Applications should create subclasses of ScopedCommandLineAction corresponding to
 * each action that they want to expose.
 *
 * The action name should be comprised of lower case words separated by hyphens
 * or colons. The name should include an English verb (e.g. "deploy"). Use a
 * hyphen to separate words (e.g. "upload-docs"). A group of related commands
 * can be prefixed with a colon (e.g. "docs:generate", "docs:deploy",
 * "docs:serve", etc).
 *
 * Scoped commands allow for different parameters to be specified for different
 * provided scoping values. For example, the "scoped-action --scope A" command
 * may allow for different scoped arguments to be specified than the "scoped-action
 * --scope B" command.
 *
 * Scoped arguments are specified after the "--" pseudo-argument. For example,
 * "scoped-action --scope A -- --scopedFoo --scopedBar".
 *
 * @public
 */
class ScopedCommandLineAction extends CommandLineAction_1.CommandLineAction {
    constructor(options) {
        super(options);
        this._options = options;
        this._scopingParameters = [];
    }
    /**
     * {@inheritDoc CommandLineParameterProvider.parameters}
     */
    get parameters() {
        if (this._scopedCommandLineParser) {
            return [...super.parameters, ...this._scopedCommandLineParser.parameters];
        }
        else {
            return super.parameters;
        }
    }
    /**
     * {@inheritdoc CommandLineAction._processParsedData}
     * @internal
     */
    _processParsedData(parserOptions, data) {
        // override
        super._processParsedData(parserOptions, data);
        this._unscopedParserOptions = parserOptions;
        // Generate the scoped parser using the parent parser information. We can only create this after we
        // have parsed the data, since the parameter values are used during construction.
        this._scopedCommandLineParser = new InternalScopedCommandLineParser(Object.assign(Object.assign({}, parserOptions), { actionOptions: this._options, unscopedActionParameters: this.parameters, onDefineScopedParameters: this.onDefineScopedParameters.bind(this) }));
    }
    /**
     * {@inheritdoc CommandLineAction._execute}
     * @internal
     */
    async _execute() {
        // override
        if (!this._unscopedParserOptions || !this._scopedCommandLineParser) {
            throw new Error('The CommandLineAction parameters must be processed before execution.');
        }
        if (!this.remainder) {
            throw new Error('CommandLineAction.onDefineParameters must be called before execution.');
        }
        // The '--' argument is required to separate the action parameters from the scoped parameters,
        // so it needs to be trimmed. If remainder values are provided but no '--' is found, then throw.
        const scopedArgs = [];
        if (this.remainder.values.length) {
            if (this.remainder.values[0] !== '--') {
                // Immitate argparse behavior and log out usage text before throwing.
                console.log(this.renderUsageText());
                throw new CommandLineParserExitError_1.CommandLineParserExitError(
                // argparse sets exit code 2 for invalid arguments
                2, 
                // model the message off of the built-in "unrecognized arguments" message
                `${this._unscopedParserOptions.toolFilename} ${this.actionName}: error: Unrecognized ` +
                    `arguments: ${this.remainder.values[0]}.`);
            }
            scopedArgs.push(...this.remainder.values.slice(1));
        }
        // Call the scoped parser using only the scoped args to handle parsing
        await this._scopedCommandLineParser.executeWithoutErrorHandling(scopedArgs);
        // Only call execute if the parser reached the execute stage. This may not be true if
        // the parser exited early due to a specified '--help' parameter.
        if (this._scopedCommandLineParser.canExecute) {
            await super._execute();
        }
        return;
    }
    /**
     * {@inheritdoc CommandLineParameterProvider.onDefineParameters}
     */
    onDefineParameters() {
        this.onDefineUnscopedParameters();
        if (!this._scopingParameters.length) {
            throw new Error('No scoping parameters defined. At least one scoping parameter must be defined. ' +
                'Scoping parameters are defined by setting the parameterGroupName to ' +
                'ScopedCommandLineAction.ScopingParameterGroupName.');
        }
        if (this.remainder) {
            throw new Error('Unscoped remainder parameters are not allowed. Remainder parameters can only be defined on ' +
                'the scoped parameter provider in onDefineScopedParameters().');
        }
        // Consume the remainder of the command-line, which will later be passed the scoped parser.
        // This will also prevent developers from calling this.defineCommandLineRemainder(...) since
        // we will have already defined it.
        this.defineCommandLineRemainder({
            description: 'Scoped parameters.  Must be prefixed with "--", ex. "-- --scopedParameter ' +
                'foo --scopedFlag".  For more information on available scoped parameters, use "-- --help".'
        });
    }
    /**
     * Retrieves the scoped CommandLineParser, which is populated after the ScopedCommandLineAction is executed.
     * @internal
     */
    _getScopedCommandLineParser() {
        if (!this._scopedCommandLineParser) {
            throw new Error('The scoped CommandLineParser is only populated after the action is executed.');
        }
        return this._scopedCommandLineParser;
    }
    /** @internal */
    _defineParameter(parameter) {
        super._defineParameter(parameter);
        if (parameter.parameterGroup === ScopedCommandLineAction.ScopingParameterGroup) {
            this._scopingParameters.push(parameter);
        }
    }
}
exports.ScopedCommandLineAction = ScopedCommandLineAction;
/**
 * The required group name to apply to all scoping parameters. At least one parameter
 * must be defined with this group name.
 */
ScopedCommandLineAction.ScopingParameterGroup = Constants_1.SCOPING_PARAMETER_GROUP;
//# sourceMappingURL=ScopedCommandLineAction.js.map