"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });
const fs = require("fs");
const path_1 = require("path");
const ArrayUtil = require("./util/array");
const FsUtil = require("./util/fs");
/**
 * Provides information based on the programs on your PATH
 */
class Executables {
    constructor(executables) {
        this.executables = new Set(executables);
    }
    /**
     * @param path is expected to to be a ':' separated list of paths.
     */
    static fromPath(path) {
        const paths = path.split(':');
        const promises = paths.map((x) => findExecutablesInPath(x));
        return Promise.all(promises)
            .then(ArrayUtil.flattenArray)
            .then(ArrayUtil.uniq)
            .then((executables) => new Executables(executables));
    }
    /**
     * Find all programs in your PATH
     */
    list() {
        return Array.from(this.executables.values());
    }
    /**
     * Check if the the given {{executable}} exists on the PATH
     */
    isExecutableOnPATH(executable) {
        return this.executables.has(executable);
    }
}
exports.default = Executables;
/**
 * Only returns direct children, or the path itself if it's an executable.
 */
function findExecutablesInPath(path) {
    return __awaiter(this, void 0, void 0, function* () {
        path = FsUtil.untildify(path);
        try {
            const pathStats = yield fs.promises.lstat(path);
            if (pathStats.isDirectory()) {
                const childrenPaths = yield fs.promises.readdir(path);
                const files = [];
                for (const childrenPath of childrenPaths) {
                    try {
                        const stats = yield fs.promises.lstat((0, path_1.join)(path, childrenPath));
                        if (isExecutableFile(stats)) {
                            files.push((0, path_1.basename)(childrenPath));
                        }
                    }
                    catch (error) {
                        // Ignore error
                    }
                }
                return files;
            }
            else if (isExecutableFile(pathStats)) {
                return [(0, path_1.basename)(path)];
            }
        }
        catch (error) {
            // Ignore error
        }
        return [];
    });
}
function isExecutableFile(stats) {
    const isExecutable = !!(1 & parseInt((stats.mode & parseInt('777', 8)).toString(8)[0]));
    return stats.isFile() && isExecutable;
}
//# sourceMappingURL=executables.js.map