"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;

var _os = require("os");

var _fs = require("fs");

var _core = require("@kui-shell/core");

/*
 * Copyright 2020 IBM Corporation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
var __awaiter = void 0 && (void 0).__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());
  });
};

const strings = (0, _core.i18n)('plugin-bash-like');

function formatPermissions(stats, isFile, isDirectory, isSymbolicLink) {
  const {
    mode
  } = stats;
  const d = isDirectory ? 'd' : isSymbolicLink ? 'l' : isFile ? '-' : 's';
  const ur = mode & _fs.constants.S_IRUSR ? 'r' : '-';
  const uw = mode & _fs.constants.S_IWUSR ? 'w' : '-';
  const ux = mode & _fs.constants.S_IXUSR ? 'x' : '-';
  const gr = mode & _fs.constants.S_IRGRP ? 'r' : '-';
  const gw = mode & _fs.constants.S_IWGRP ? 'w' : '-';
  const gx = mode & _fs.constants.S_IXGRP ? 'x' : '-';
  const or = mode & _fs.constants.S_IROTH ? 'r' : '-';
  const ow = mode & _fs.constants.S_IWOTH ? 'w' : '-';
  const ox = mode & _fs.constants.S_IXOTH ? 'x' : '-';
  return `${d}${ur}${uw}${ux}${gr}${gw}${gx}${or}${ow}${ox}`;
}
/**
 * promisey form of stat
 *
 */


function isDir(filepath) {
  return __awaiter(this, void 0, void 0, function* () {
    return new Promise((resolve, reject) => {
      (0, _fs.stat)(filepath, (err, stats) => {
        if (err) {
          if (err.code === 'ENOENT') {
            resolve(undefined);
          } else {
            reject(err);
          }
        } else {
          resolve(stats);
        }
      });
    });
  });
}

const nope = () => false;

const yup = () => true;
/**
 * Kui command for globbing readdir
 *
 */


function kuiglob({
  tab,
  argvNoOptions,
  parsedOptions
}) {
  return __awaiter(this, void 0, void 0, function* () {
    // Intentional require versus import... some typing issues right
    // now. Also intentionally lazy here, to avoid complications with
    // webpack bundling with browser targets.
    const globby = require('globby'); // this is the list of top-level entry points the user asked us to
    // traverse


    const inputs = argvNoOptions.slice(argvNoOptions.indexOf('kuiglob') + 1).map(_core.expandHomeDir) // ~ -> /home/me/
    .map(_ => _.replace(/([^\\])"/g, '$1')); // /tmp/"foo bar" -> /tmp/foo bar

    const stats = inputs.length === 0 ? [] : yield Promise.all(inputs.map(isDir));
    const startingPointsAreDirs = stats.map(_ => _ && _.isDirectory()); // assemble the list of top-level glob entry points to traverse: 1)
    // filter out directories, if the user specified -d; 2a) add a
    // trailing wildcard, for starting points that are known to be
    // directories (e.g. `ls dirname` should traverse into dirname,
    // unless -d is specified hence the first filter; 2b) add a trailing
    // wildcard, if the user specified a trailing wildcard themselves
    // (e.g. `ls foo*` should traverse into foobar/ if it is a
    // directory)

    const toGlob = inputs.filter((_, idx) => !parsedOptions.d || !startingPointsAreDirs[idx]).reduce((A, _, idx) => {
      if (!parsedOptions.d) {
        if (startingPointsAreDirs[idx]) {
          // ls dirname -> dirname/* as the glob
          // ls dirname/ -> dirname/* as the glob
          A.push(_.endsWith('/') ? `${_}*` : `${_}/*`);
          return A;
        } else if (_.endsWith('*')) {
          // ls foo* -> [foo*, foo*/*]
          A.push(_);
          A.push(`${_}/*`);
          return A;
        }
      } // otherwise, what the user typed is what we'll use as the glob


      A.push(_);
      return A;
    }, []);
    const needStats = parsedOptions.l || parsedOptions.C;
    const globbedEntries = toGlob.length === 0 && inputs.length > 0 ? [] : yield globby(toGlob.length === 0 ? '*' : toGlob, {
      followSymbolicLinks: false,
      onlyFiles: false,
      dot: parsedOptions.a || parsedOptions.all,
      stats: needStats,
      objectMode: !needStats,
      cwd: (0, _core.isHeadless)() ? process.cwd() : tab.state.cwd
    }); //  ^^^^^^ re: type conversion; globby type declaration issue #139
    // handle -d; fast-glob doesn't seem to handle this very well on its
    // own; it doesn't have a way for clients to express "stop
    // traversing at any directories"

    const dirEntries = !parsedOptions.d ? [] : inputs.map((name, idx) => {
      if (!startingPointsAreDirs[idx]) {
        return undefined;
      } else {
        return {
          name,
          path: name,
          dirent: {
            isFile: nope,
            isSymbolicLink: nope,
            isDirectory: yup
          },
          stats: stats[idx]
        };
      }
    }).filter(_ => _);
    const entries = globbedEntries.concat(dirEntries);

    if (entries.length === 0 && !inputs.some(_ => /\*/.test(_)) && !startingPointsAreDirs.some(_ => _)) {
      // we found nothing; be careful only to report a 404 if: a) user
      // specified no wildcards b) none of the inputs were directories;
      // e.g. `ls emptyDir` will glob nothing, but shouldn't be a 404
      const error = new Error(strings('No such file or directory', inputs.join(' ')));
      error.code = 404;
      throw error;
    }

    const user = (0, _os.userInfo)();
    return {
      mode: 'raw',
      content: entries.map(({
        name,
        path,
        dirent,
        stats
      }) => {
        const isFile = dirent.isFile();
        const isDirectory = dirent.isDirectory();
        const isSymbolicLink = dirent.isSymbolicLink();
        const isExecutable = stats && !!(stats.mode & (_fs.constants.S_IXUSR | _fs.constants.S_IXGRP | _fs.constants.S_IXOTH));
        const isSpecial = !isFile && !isDirectory && !isSymbolicLink; // dirent.isBlockDevice() || dirent.isCharacterDevice() || dirent.isFIFO() || dirent.isSocket()
        // e.g. ls dir1 dir2... we want to display the dir1 and dir2 as part of the response

        const nameForDisplay = toGlob.length > 1 ? path : name;
        return {
          name,
          path,
          nameForDisplay,
          stats,
          dirent: {
            isFile,
            isDirectory,
            isSymbolicLink,
            isExecutable,
            isSpecial,
            permissions: parsedOptions.l ? formatPermissions(stats, isFile, isDirectory, isSymbolicLink) : '',
            username: stats && user.uid === stats.uid ? user.username : ''
          }
        };
      })
    };
  });
}
/**
 * Register command handlers
 *
 */


var _default = commandTree => {
  commandTree.listen('/kuiglob', kuiglob, {
    hidden: true,
    requiresLocal: true,
    flags: {
      boolean: ['a', 'all', 'd', 'l', 'C']
    }
  });
};

exports.default = _default;