1// SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
2//
3// SPDX-License-Identifier: GPL-3.0-or-later
4
5export interface ParsedArgs {
6 command?: string;
7 options: Record<string, string | boolean>;
8 positional: string[];
9}
10
11export function parseArgs(args: string[]): ParsedArgs {
12 const [, , command, ...rest] = args;
13 const options: Record<string, string | boolean> = {};
14 const positional: string[] = [];
15
16 for (let i = 0; i < rest.length; i += 1) {
17 const arg = rest[i];
18 if (!arg) continue;
19
20 if (arg.startsWith("--")) {
21 const eqIndex = arg.indexOf("=", 2);
22 let key: string;
23 let value: string | undefined;
24 if (eqIndex !== -1) {
25 key = arg.slice(2, eqIndex);
26 value = arg.slice(eqIndex + 1);
27 } else {
28 key = arg.slice(2);
29 }
30 if (!key) continue;
31 if (value !== undefined) {
32 options[key] = value;
33 } else if (rest[i + 1] && !rest[i + 1]?.startsWith("-")) {
34 options[key] = rest[i + 1] as string;
35 i += 1;
36 } else {
37 options[key] = true;
38 }
39 } else if (arg.startsWith("-")) {
40 const short = arg.slice(1);
41 if (short === "u") {
42 if (rest[i + 1] && !rest[i + 1]!.startsWith("-")) {
43 options["uri"] = rest[i + 1] as string;
44 i += 1;
45 }
46 // else: -u with no value — uri stays unset, command handler validates
47 } else if (short === "f") {
48 options["full"] = true;
49 } else if (short === "v") {
50 options["version"] = true;
51 } else {
52 options[short] = true;
53 }
54 } else {
55 positional.push(arg);
56 }
57 }
58
59 return { command, options, positional };
60}