// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
// This module is browser compatible.
/**
 * Command line arguments parser based on
 * {@link https://github.com/minimistjs/minimist | minimist}.
 *
 * This module is browser compatible.
 *
 * @example
 * ```ts
 * import { parse } from "@std/flags";
 *
 * console.dir(parse(Deno.args));
 * ```
 *
 * @deprecated Use
 * {@linkcode https://jsr.io/@std/cli/doc/parse-args/~/parseArgs | parseArgs}
 * instead. This module will be removed once the Standard Library migrates to
 * {@link https://jsr.io/ | JSR}.
 *
 * @module
 */ import { assertExists } from "jsr:/@std/assert@^0.224.0/assert-exists";
const { hasOwn } = Object;
function get(obj, key) {
  if (hasOwn(obj, key)) {
    return obj[key];
  }
}
function getForce(obj, key) {
  const v = get(obj, key);
  assertExists(v);
  return v;
}
function isNumber(x) {
  if (typeof x === "number") return true;
  if (/^0x[0-9a-f]+$/i.test(String(x))) return true;
  return /^[-+]?(?:\d+(?:\.\d*)?|\.\d+)(e[-+]?\d+)?$/.test(String(x));
}
function hasKey(obj, keys) {
  let o = obj;
  keys.slice(0, -1).forEach((key)=>{
    o = get(o, key) ?? {};
  });
  const key = keys.at(-1);
  return key !== undefined && hasOwn(o, key);
}
/**
 * Take a set of command line arguments, optionally with a set of options, and
 * return an object representing the flags found in the passed arguments.
 *
 * By default, any arguments starting with `-` or `--` are considered boolean
 * flags. If the argument name is followed by an equal sign (`=`) it is
 * considered a key-value pair. Any arguments which could not be parsed are
 * available in the `_` property of the returned object.
 *
 * By default, the flags module tries to determine the type of all arguments
 * automatically and the return type of the `parse` method will have an index
 * signature with `any` as value (`{ [x: string]: any }`).
 *
 * If the `string`, `boolean` or `collect` option is set, the return value of
 * the `parse` method will be fully typed and the index signature of the return
 * type will change to `{ [x: string]: unknown }`.
 *
 * Any arguments after `'--'` will not be parsed and will end up in `parsedArgs._`.
 *
 * Numeric-looking arguments will be returned as numbers unless `options.string`
 * or `options.boolean` is set for that argument name.
 *
 * @example
 * ```ts
 * import { parse } from "@std/flags";
 * const parsedArgs = parse(Deno.args);
 * ```
 *
 * @example
 * ```ts
 * import { parse } from "@std/flags";
 * const parsedArgs = parse(["--foo", "--bar=baz", "./quux.txt"]);
 * // parsedArgs: { foo: true, bar: "baz", _: ["./quux.txt"] }
 * ```
 *
 * @deprecated Use
 * {@linkcode https://jsr.io/@std/cli/doc/parse-args/~/parseArgs | parseArgs}
 * instead. This module will be removed once the Standard Library migrates to
 * {@link https://jsr.io/ | JSR}.
 */ export function parse(args, { "--": doubleDash = false, alias = {}, boolean = false, default: defaults = {}, stopEarly = false, string = [], collect = [], negatable = [], unknown = (i)=>i } = {}) {
  const aliases = {};
  const flags = {
    bools: {},
    strings: {},
    unknownFn: unknown,
    allBools: false,
    collect: {},
    negatable: {}
  };
  if (alias !== undefined) {
    for(const key in alias){
      const val = getForce(alias, key);
      if (typeof val === "string") {
        aliases[key] = [
          val
        ];
      } else {
        aliases[key] = val;
      }
      const aliasesForKey = getForce(aliases, key);
      for (const alias of aliasesForKey){
        aliases[alias] = [
          key
        ].concat(aliasesForKey.filter((y)=>alias !== y));
      }
    }
  }
  if (boolean !== undefined) {
    if (typeof boolean === "boolean") {
      flags.allBools = !!boolean;
    } else {
      const booleanArgs = typeof boolean === "string" ? [
        boolean
      ] : boolean;
      for (const key of booleanArgs.filter(Boolean)){
        flags.bools[key] = true;
        const alias = get(aliases, key);
        if (alias) {
          for (const al of alias){
            flags.bools[al] = true;
          }
        }
      }
    }
  }
  if (string !== undefined) {
    const stringArgs = typeof string === "string" ? [
      string
    ] : string;
    for (const key of stringArgs.filter(Boolean)){
      flags.strings[key] = true;
      const alias = get(aliases, key);
      if (alias) {
        for (const al of alias){
          flags.strings[al] = true;
        }
      }
    }
  }
  if (collect !== undefined) {
    const collectArgs = typeof collect === "string" ? [
      collect
    ] : collect;
    for (const key of collectArgs.filter(Boolean)){
      flags.collect[key] = true;
      const alias = get(aliases, key);
      if (alias) {
        for (const al of alias){
          flags.collect[al] = true;
        }
      }
    }
  }
  if (negatable !== undefined) {
    const negatableArgs = typeof negatable === "string" ? [
      negatable
    ] : negatable;
    for (const key of negatableArgs.filter(Boolean)){
      flags.negatable[key] = true;
      const alias = get(aliases, key);
      if (alias) {
        for (const al of alias){
          flags.negatable[al] = true;
        }
      }
    }
  }
  const argv = {
    _: []
  };
  function argDefined(key, arg) {
    return flags.allBools && /^--[^=]+$/.test(arg) || get(flags.bools, key) || !!get(flags.strings, key) || !!get(aliases, key);
  }
  function setKey(obj, name, value, collect = true) {
    let o = obj;
    const keys = name.split(".");
    keys.slice(0, -1).forEach(function(key) {
      if (get(o, key) === undefined) {
        o[key] = {};
      }
      o = get(o, key);
    });
    const key = keys.at(-1);
    const collectable = collect && !!get(flags.collect, name);
    if (!collectable) {
      o[key] = value;
    } else if (get(o, key) === undefined) {
      o[key] = [
        value
      ];
    } else if (Array.isArray(get(o, key))) {
      o[key].push(value);
    } else {
      o[key] = [
        get(o, key),
        value
      ];
    }
  }
  function setArg(key, val, arg = undefined, collect) {
    if (arg && flags.unknownFn && !argDefined(key, arg)) {
      if (flags.unknownFn(arg, key, val) === false) return;
    }
    const value = !get(flags.strings, key) && isNumber(val) ? Number(val) : val;
    setKey(argv, key, value, collect);
    const alias = get(aliases, key);
    if (alias) {
      for (const x of alias){
        setKey(argv, x, value, collect);
      }
    }
  }
  function aliasIsBoolean(key) {
    return getForce(aliases, key).some((x)=>typeof get(flags.bools, x) === "boolean");
  }
  let notFlags = [];
  // all args after "--" are not parsed
  if (args.includes("--")) {
    notFlags = args.slice(args.indexOf("--") + 1);
    args = args.slice(0, args.indexOf("--"));
  }
  for(let i = 0; i < args.length; i++){
    const arg = args[i];
    assertExists(arg);
    if (/^--.+=/.test(arg)) {
      const m = arg.match(/^--([^=]+)=(.*)$/s);
      assertExists(m);
      const [, key, value] = m;
      assertExists(key);
      if (flags.bools[key]) {
        const booleanValue = value !== "false";
        setArg(key, booleanValue, arg);
      } else {
        setArg(key, value, arg);
      }
    } else if (/^--no-.+/.test(arg) && get(flags.negatable, arg.replace(/^--no-/, ""))) {
      const m = arg.match(/^--no-(.+)/);
      assertExists(m);
      assertExists(m[1]);
      setArg(m[1], false, arg, false);
    } else if (/^--.+/.test(arg)) {
      const m = arg.match(/^--(.+)/);
      assertExists(m);
      assertExists(m[1]);
      const [, key] = m;
      const next = args[i + 1];
      if (next !== undefined && !/^-/.test(next) && !get(flags.bools, key) && !flags.allBools && (get(aliases, key) ? !aliasIsBoolean(key) : true)) {
        setArg(key, next, arg);
        i++;
      } else if (next !== undefined && (next === "true" || next === "false")) {
        setArg(key, next === "true", arg);
        i++;
      } else {
        setArg(key, get(flags.strings, key) ? "" : true, arg);
      }
    } else if (/^-[^-]+/.test(arg)) {
      const letters = arg.slice(1, -1).split("");
      let broken = false;
      for (const [j, letter] of letters.entries()){
        const next = arg.slice(j + 2);
        if (next === "-") {
          setArg(letter, next, arg);
          continue;
        }
        if (/[A-Za-z]/.test(letter) && next.includes("=")) {
          setArg(letter, next.split(/=(.+)/)[1], arg);
          broken = true;
          break;
        }
        if (/[A-Za-z]/.test(letter) && /-?\d+(\.\d*)?(e-?\d+)?$/.test(next)) {
          setArg(letter, next, arg);
          broken = true;
          break;
        }
        if (letters[j + 1]?.match(/\W/)) {
          setArg(letter, arg.slice(j + 2), arg);
          broken = true;
          break;
        } else {
          setArg(letter, get(flags.strings, letter) ? "" : true, arg);
        }
      }
      const key = arg.at(-1);
      if (!broken && key !== "-") {
        const nextArg = args[i + 1];
        if (nextArg && !/^(-|--)[^-]/.test(nextArg) && !get(flags.bools, key) && (get(aliases, key) ? !aliasIsBoolean(key) : true)) {
          setArg(key, nextArg, arg);
          i++;
        } else if (nextArg && (nextArg === "true" || nextArg === "false")) {
          setArg(key, nextArg === "true", arg);
          i++;
        } else {
          setArg(key, get(flags.strings, key) ? "" : true, arg);
        }
      }
    } else {
      if (!flags.unknownFn || flags.unknownFn(arg) !== false) {
        argv._.push(flags.strings["_"] ?? !isNumber(arg) ? arg : Number(arg));
      }
      if (stopEarly) {
        argv._.push(...args.slice(i + 1));
        break;
      }
    }
  }
  for (const [key, value] of Object.entries(defaults)){
    if (!hasKey(argv, key.split("."))) {
      setKey(argv, key, value, false);
      const alias = aliases[key];
      if (alias !== undefined) {
        for (const x of alias){
          setKey(argv, x, value, false);
        }
      }
    }
  }
  for (const key of Object.keys(flags.bools)){
    if (!hasKey(argv, key.split("."))) {
      const value = get(flags.collect, key) ? [] : false;
      setKey(argv, key, value, false);
    }
  }
  for (const key of Object.keys(flags.strings)){
    if (!hasKey(argv, key.split(".")) && get(flags.collect, key)) {
      setKey(argv, key, [], false);
    }
  }
  if (doubleDash) {
    argv["--"] = [];
    for (const key of notFlags){
      argv["--"].push(key);
    }
  } else {
    for (const key of notFlags){
      argv._.push(key);
    }
  }
  return argv;
}
//# sourceMappingURL=data:application/json;base64,[encoded data, 41352 characters]
// denoCacheMetadata=16139098194153641719,15120888521369075668