/
c-cli-arg-group.ts
100 lines (84 loc) · 3.02 KB
/
c-cli-arg-group.ts
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
98
99
100
import { CCliDescription } from './c-cli-description';
import { CCliUsageError } from './c-cli-usage-error';
import { CCliConditionalValue } from './c-cli-conditional-value';
/** Defines the type of the args passed to an {@link CCliArgGroup.parse} */
export type CCliParseArgs<Optional extends boolean> = CCliConditionalValue<
string[],
Optional
>;
/** Options for creating an argument group, a group of adjacent command-line
* arguments
* @typeParam Optional If `true`, the type of `args` passed to
* {@link CCliArgGroupOptions.parse} does not include `undefined`. */
export type CCliArgGroupOptions<Optional extends boolean> = {
/** A text description of this argument group. Paragraphs are re-wrapped when
* printed to the terminal so don't worry about whitespace. */
description?: CCliDescription;
/** If `true`, command-line usage won't show this arg group unless it's
* invoked directly */
hidden?: boolean;
/** A short placeholder for this argument group in command-line usage. By
* convention, **@carnesen/cli** placeholders are wrapped in angled brackets
* e.g. `"<email>"` */
placeholder?: string;
/** Unless `true`, a {@link CCliUsageError} thrown if no argument is provided
* for this argument group */
optional?: Optional;
};
export type InferParsedValueFromCCliArgGroup<ArgGroup> =
ArgGroup extends CCliArgGroup<infer Value, true>
? Value | undefined
: ArgGroup extends CCliArgGroup<infer Value>
? NonNullable<Value>
: never;
/** A group of consecutive command-line arguments parsed together into a single
* well-typed value
* @param Value Type of the value returned by {@link CCliArgGroup.parse}
* @param Optional If `true`, the type of `args` passed to
* {@link CCliArgGroup.parse} includes `undefined`. */
export abstract class CCliArgGroup<
Value = unknown,
Optional extends boolean = boolean,
> {
protected constructor(
protected readonly options: CCliArgGroupOptions<Optional>,
) {
this.parse = this.parse.bind(this);
}
public get description(): CCliDescription {
return this.options.description;
}
public get hidden(): boolean {
return this.options.hidden || false;
}
public get placeholder(): string {
return this.options.placeholder ?? '<val>';
}
public get optional(): boolean {
return Boolean(this.options.optional);
}
/** Function that parses a well-typed value from string arguments */
public abstract parse(
args: CCliParseArgs<Optional>,
): CCliConditionalValue<Value, Optional>;
/** Experimental support for autocompletion. Optionally implemented by
* subclass. */
public _suggest(_args: string[], _search?: string): string[] {
return [];
}
protected assertSingleArg(args: unknown[]): void {
if (args.length !== 1) {
throw new CCliUsageError(`Expected a single ${this.options.placeholder}`);
}
}
protected assertOneOrMoreArgs(args: unknown[]): void {
if (args.length === 0) {
throw new CCliUsageError(
`Expected one or more arguments ${this.options.placeholder}`,
);
}
}
public undefinedAsValue(): Value {
return undefined as unknown as Value;
}
}