"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getRegistryUrlVarsFromPackageFile = exports.extractHeaderCommand = exports.allowedPipOptions = exports.optionsWithArguments = exports.disallowedPipOptions = exports.constraintLineRegex = exports.getExecOptions = exports.getPipToolsConstraint = exports.getPythonConstraint = void 0;
const tslib_1 = require("tslib");
const is_1 = tslib_1.__importDefault(require("@sindresorhus/is"));
const shlex_1 = require("shlex");
const logger_1 = require("../../../logger");
const array_1 = require("../../../util/array");
const fs_1 = require("../../../util/fs");
const hostRules = tslib_1.__importStar(require("../../../util/host-rules"));
const regex_1 = require("../../../util/regex");
function getPythonConstraint(config) {
    const { constraints = {} } = config;
    const { python } = constraints;
    if (python) {
        logger_1.logger.debug('Using python constraint from config');
        return python;
    }
    return undefined;
}
exports.getPythonConstraint = getPythonConstraint;
// TODO(not7cd): rename to getPipToolsVersionConstraint, as constraints have their meaning in pip
function getPipToolsConstraint(config) {
    const { constraints = {} } = config;
    const { pipTools } = constraints;
    if (is_1.default.string(pipTools)) {
        logger_1.logger.debug('Using pipTools constraint from config');
        return pipTools;
    }
    return '';
}
exports.getPipToolsConstraint = getPipToolsConstraint;
async function getExecOptions(config, inputFileName, extraEnv) {
    const constraint = getPythonConstraint(config);
    const pipToolsConstraint = getPipToolsConstraint(config);
    const execOptions = {
        cwdFile: inputFileName,
        docker: {},
        toolConstraints: [
            {
                toolName: 'python',
                constraint,
            },
            {
                toolName: 'pip-tools',
                constraint: pipToolsConstraint,
            },
        ],
        extraEnv: {
            PIP_CACHE_DIR: await (0, fs_1.ensureCacheDir)('pip'),
            ...extraEnv,
        },
    };
    return execOptions;
}
exports.getExecOptions = getExecOptions;
exports.constraintLineRegex = (0, regex_1.regEx)(/^(#.*?\r?\n)+# {4}(?<command>\S*)(?<arguments> .*?)?\r?\n/);
exports.disallowedPipOptions = [
    '--no-header', // header is required by this manager
];
exports.optionsWithArguments = [
    '--output-file',
    '--extra',
    '--extra-index-url',
    '--resolver',
    '--constraint',
];
exports.allowedPipOptions = [
    '-v',
    '--allow-unsafe',
    '--generate-hashes',
    '--no-emit-index-url',
    '--emit-index-url',
    '--strip-extras',
    '--index-url',
    ...exports.optionsWithArguments,
];
// TODO(not7cd): test on all correct headers, even with CUSTOM_COMPILE_COMMAND
function extractHeaderCommand(content, fileName) {
    const compileCommand = exports.constraintLineRegex.exec(content);
    if (compileCommand?.groups === undefined) {
        throw new Error(`Failed to extract command from header in ${fileName}`);
    }
    logger_1.logger.trace(`pip-compile: found header in ${fileName}: \n${compileCommand[0]}`);
    const command = compileCommand.groups.command;
    const argv = [command];
    const isCustomCommand = command !== 'pip-compile';
    if (isCustomCommand) {
        logger_1.logger.debug(`pip-compile: custom command ${command} detected (${fileName})`);
    }
    if (compileCommand.groups.arguments) {
        argv.push(...(0, shlex_1.split)(compileCommand.groups.arguments));
    }
    logger_1.logger.debug(`pip-compile: extracted command from header: ${JSON.stringify(argv)}`);
    const result = {
        argv,
        command,
        isCustomCommand,
        outputFile: '',
        sourceFiles: [],
    };
    for (const arg of argv.slice(1)) {
        // TODO(not7cd): check for "--option -- argument" case
        if (!arg.startsWith('-')) {
            result.sourceFiles.push(arg);
            continue;
        }
        throwForDisallowedOption(arg);
        throwForNoEqualSignInOptionWithArgument(arg);
        throwForUnknownOption(arg);
        if (arg.includes('=')) {
            const [option, value] = arg.split('=');
            if (option === '--extra') {
                result.extra = result.extra ?? [];
                result.extra.push(value);
            }
            else if (option === '--extra-index-url') {
                result.extraIndexUrl = result.extraIndexUrl ?? [];
                result.extraIndexUrl.push(value);
                // TODO: add to secrets? next PR
            }
            else if (option === '--constraint') {
                result.constraintsFiles = result.constraintsFiles ?? [];
                result.constraintsFiles.push(value);
            }
            else if (option === '--output-file') {
                if (result.outputFile) {
                    throw new Error('Cannot use multiple --output-file options');
                }
                result.outputFile = value;
            }
            else if (option === '--index-url') {
                if (result.indexUrl) {
                    throw new Error('Cannot use multiple --index-url options');
                }
                result.indexUrl = value;
                // TODO: add to secrets? next PR
            }
            else {
                logger_1.logger.warn(`pip-compile: option ${arg} not handled`);
            }
            continue;
        }
        if (arg === '--no-emit-index-url') {
            result.noEmitIndexUrl = true;
            continue;
        }
        if (arg === '--emit-index-url') {
            result.emitIndexUrl = true;
            continue;
        }
        logger_1.logger.warn(`pip-compile: option ${arg} not handled`);
    }
    logger_1.logger.trace({
        ...result,
    }, 'Parsed pip-compile command from header');
    if (result.noEmitIndexUrl && result.emitIndexUrl) {
        throw new Error('Cannot use both --no-emit-index-url and --emit-index-url');
    }
    if (result.sourceFiles.length === 0) {
        throw new Error('No source files detected in command, pass at least one package file explicitly');
    }
    return result;
}
exports.extractHeaderCommand = extractHeaderCommand;
function throwForDisallowedOption(arg) {
    if (exports.disallowedPipOptions.includes(arg)) {
        throw new Error(`Option ${arg} not allowed for this manager`);
    }
}
function throwForNoEqualSignInOptionWithArgument(arg) {
    if (exports.optionsWithArguments.includes(arg)) {
        throw new Error(`Option ${arg} must have equal sign '=' separating it's argument`);
    }
}
function throwForUnknownOption(arg) {
    if (arg.includes('=')) {
        const [option] = arg.split('=');
        if (exports.allowedPipOptions.includes(option)) {
            return;
        }
    }
    if (exports.allowedPipOptions.includes(arg)) {
        return;
    }
    throw new Error(`Option ${arg} not supported (yet)`);
}
function buildRegistryUrl(url) {
    try {
        const ret = new URL(url);
        const hostRule = hostRules.find({ url });
        if (!ret.username && !ret.password) {
            ret.username = hostRule.username ?? '';
            ret.password = hostRule.password ?? '';
        }
        return ret;
    }
    catch {
        return null;
    }
}
function getRegistryUrlVarFromUrls(varName, urls) {
    if (!urls.length) {
        return {
            haveCredentials: false,
            environmentVars: {},
        };
    }
    let haveCredentials = false;
    for (const url of urls) {
        if (url.username || url.password) {
            haveCredentials = true;
        }
    }
    const registryUrlsString = urls.map((url) => url.href).join(' ');
    const ret = {
        haveCredentials,
        environmentVars: {},
    };
    if (registryUrlsString) {
        ret.environmentVars[varName] = registryUrlsString;
    }
    return ret;
}
function getRegistryUrlVarsFromPackageFile(packageFile) {
    // There should only ever be one element in registryUrls, since pip_requirements gets them from --index-url
    // flags in the input file, and that only makes sense once
    const indexUrl = getRegistryUrlVarFromUrls('PIP_INDEX_URL', packageFile?.registryUrls
        ?.map(buildRegistryUrl)
        .filter(array_1.isNotNullOrUndefined) ?? []);
    const extraIndexUrls = getRegistryUrlVarFromUrls('PIP_EXTRA_INDEX_URL', packageFile?.additionalRegistryUrls
        ?.map(buildRegistryUrl)
        .filter(array_1.isNotNullOrUndefined) ?? []);
    return {
        haveCredentials: indexUrl.haveCredentials || extraIndexUrls.haveCredentials,
        environmentVars: {
            ...indexUrl.environmentVars,
            ...extraIndexUrls.environmentVars,
        },
    };
}
exports.getRegistryUrlVarsFromPackageFile = getRegistryUrlVarsFromPackageFile;
//# sourceMappingURL=common.js.map