File size: 3,404 Bytes
488d96b | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 | /**
* Argument splitting shared by the command table: short flags (bundled or
* separate), long flags, `--`, and the operands that follow.
* @module @deepseek-ai/dsh-experimental-webworker-runtime/src/shell/programs/options
*/
/** One parsed argv: which flags were given, and what is left to act on. */
export interface ParsedOptions {
/** Every short letter and long name seen, without their dashes. */
readonly flags: ReadonlySet<string>
/** Values of flags that take one (`-n 5` and `--name=x` both land here). */
readonly values: ReadonlyMap<string, string>
/** Everything that is not a flag, in order. */
readonly operands: readonly string[]
}
/**
* Split one program's arguments.
*
* A short letter listed in `valued` consumes the rest of its token (`-n5`) or
* the next argument (`-n 5`); every other letter is a plain flag, so `-rn`
* sets both `r` and `n`.
* @param argv - the program's argv, including its name at index 0.
* @param valued - short letters that take a value.
* @returns the flags, their values, and the operands.
*/
export function parseOptions(argv: readonly string[], valued: ReadonlySet<string> = new Set()): ParsedOptions {
const flags = new Set<string>()
const values = new Map<string, string>()
const operands: string[] = []
const rest = argv.slice(1)
let literal = false
for (let index = 0; index < rest.length; index += 1) {
const argument = rest[index] as string
if (literal || argument === '-' || !argument.startsWith('-')) {
operands.push(argument)
continue
}
if (argument === '--') {
literal = true
continue
}
if (argument.startsWith('--')) {
const [name, value] = splitLong(argument.slice(2))
flags.add(name)
if (value !== undefined) values.set(name, value)
continue
}
for (let cursor = 1; cursor < argument.length; cursor += 1) {
const letter = argument[cursor] as string
flags.add(letter)
if (!valued.has(letter)) continue
const inline = argument.slice(cursor + 1)
if (inline !== '') {
values.set(letter, inline)
} else {
index += 1
values.set(letter, rest[index] ?? '')
}
break
}
}
return { flags, values, operands }
}
/** Split `name=value`; a long flag without `=` has no value. */
function splitLong(text: string): [string, string | undefined] {
const separator = text.indexOf('=')
return separator < 0 ? [text, undefined] : [text.slice(0, separator), text.slice(separator + 1)]
}
/**
* Read a numeric flag value.
* @param options - the parsed options.
* @param flag - the short letter to read.
* @param fallback - value to use when the flag is absent or unparsable.
* @returns the number the caller should use.
*/
export function numberOption(options: ParsedOptions, flag: string, fallback: number): number {
const raw = options.values.get(flag)
if (raw === undefined) return fallback
const parsed = Number.parseInt(raw, 10)
return Number.isFinite(parsed) ? parsed : fallback
}
/**
* Split text into lines for the line-oriented utilities.
* @param text - the text to split.
* @returns its lines, without the trailing empty line a final newline creates.
*/
export function toLines(text: string): string[] {
if (text === '') return []
const lines = text.split('\n')
if (lines[lines.length - 1] === '') lines.pop()
return lines
}
|