forked from microsoft/rushstack
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathCommandLineParameterProvider.ts
More file actions
266 lines (234 loc) · 8.95 KB
/
CommandLineParameterProvider.ts
File metadata and controls
266 lines (234 loc) · 8.95 KB
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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
// See LICENSE in the project root for license information.
import * as argparse from 'argparse';
import {
ICommandLineFlagDefinition,
ICommandLineStringDefinition,
ICommandLineStringListDefinition,
ICommandLineIntegerDefinition,
ICommandLineChoiceDefinition
} from './CommandLineDefinition';
import {
CommandLineParameter,
CommandLineParameterWithArgument,
CommandLineFlagParameter,
CommandLineStringParameter,
CommandLineStringListParameter,
CommandLineIntegerParameter,
CommandLineChoiceParameter,
CommandLineParameterKind
} from './CommandLineParameter';
/**
* This is the argparse result data object
* @internal
*/
export interface ICommandLineParserData {
action: string;
[key: string]: any; // eslint-disable-line @typescript-eslint/no-explicit-any
}
/**
* This is the common base class for CommandLineAction and CommandLineParser
* that provides functionality for defining command-line parameters.
*
* @public
*/
export abstract class CommandLineParameterProvider {
private static _keyCounter: number = 0;
private _parameters: CommandLineParameter[];
private _parametersByLongName: Map<string, CommandLineParameter>;
/** @internal */
// Third party code should not inherit subclasses or call this constructor
public constructor() {
this._parameters = [];
this._parametersByLongName = new Map<string, CommandLineParameter>();
}
/**
* Returns a collection of the parameters that were defined for this object.
*/
public get parameters(): ReadonlyArray<CommandLineParameter> {
return this._parameters;
}
/**
* Defines a command-line parameter whose value must be a string from a fixed set of
* allowable choices (similar to an enum).
*
* @remarks
* Example: example-tool --log-level warn
*/
public defineChoiceParameter(definition: ICommandLineChoiceDefinition): CommandLineChoiceParameter {
const parameter: CommandLineChoiceParameter = new CommandLineChoiceParameter(definition);
this._defineParameter(parameter);
return parameter;
}
/**
* Returns the CommandLineChoiceParameter with the specified long name.
* @remarks
* This method throws an exception if the parameter is not defined.
*/
public getChoiceParameter(parameterLongName: string): CommandLineChoiceParameter {
return this._getParameter(parameterLongName, CommandLineParameterKind.Choice);
}
/**
* Defines a command-line switch whose boolean value is true if the switch is provided,
* and false otherwise.
*
* @remarks
* Example: example-tool --debug
*/
public defineFlagParameter(definition: ICommandLineFlagDefinition): CommandLineFlagParameter {
const parameter: CommandLineFlagParameter = new CommandLineFlagParameter(definition);
this._defineParameter(parameter);
return parameter;
}
/**
* Returns the CommandLineFlagParameter with the specified long name.
* @remarks
* This method throws an exception if the parameter is not defined.
*/
public getFlagParameter(parameterLongName: string): CommandLineFlagParameter {
return this._getParameter(parameterLongName, CommandLineParameterKind.Flag);
}
/**
* Defines a command-line parameter whose value is an integer.
*
* @remarks
* Example: example-tool --max-attempts 5
*/
public defineIntegerParameter(definition: ICommandLineIntegerDefinition): CommandLineIntegerParameter {
const parameter: CommandLineIntegerParameter = new CommandLineIntegerParameter(definition);
this._defineParameter(parameter);
return parameter;
}
/**
* Returns the CommandLineIntegerParameter with the specified long name.
* @remarks
* This method throws an exception if the parameter is not defined.
*/
public getIntegerParameter(parameterLongName: string): CommandLineIntegerParameter {
return this._getParameter(parameterLongName, CommandLineParameterKind.Integer);
}
/**
* Defines a command-line parameter whose value is a single text string.
*
* @remarks
* Example: example-tool --message "Hello, world!"
*/
public defineStringParameter(definition: ICommandLineStringDefinition): CommandLineStringParameter {
const parameter: CommandLineStringParameter = new CommandLineStringParameter(definition);
this._defineParameter(parameter);
return parameter;
}
/**
* Returns the CommandLineStringParameter with the specified long name.
* @remarks
* This method throws an exception if the parameter is not defined.
*/
public getStringParameter(parameterLongName: string): CommandLineStringParameter {
return this._getParameter(parameterLongName, CommandLineParameterKind.String);
}
/**
* Defines a command-line parameter whose value is one or more text strings.
*
* @remarks
* Example: example-tool --add file1.txt --add file2.txt --add file3.txt
*/
public defineStringListParameter(definition: ICommandLineStringListDefinition): CommandLineStringListParameter {
const parameter: CommandLineStringListParameter = new CommandLineStringListParameter(definition);
this._defineParameter(parameter);
return parameter;
}
/**
* Returns the CommandLineStringListParameter with the specified long name.
* @remarks
* This method throws an exception if the parameter is not defined.
*/
public getStringListParameter(parameterLongName: string): CommandLineStringListParameter {
return this._getParameter(parameterLongName, CommandLineParameterKind.StringList);
}
/**
* Generates the command-line help text.
*/
public renderHelpText(): string {
return this._getArgumentParser().formatHelp();
}
/**
* The child class should implement this hook to define its command-line parameters,
* e.g. by calling defineFlagParameter().
*/
protected abstract onDefineParameters(): void;
/**
* Retrieves the argparse object.
* @internal
*/
protected abstract _getArgumentParser(): argparse.ArgumentParser;
/** @internal */
protected _processParsedData(data: ICommandLineParserData): void {
// Fill in the values for the parameters
for (const parameter of this._parameters) {
const value: any = data[parameter._parserKey]; // eslint-disable-line @typescript-eslint/no-explicit-any
parameter._setValue(value);
}
}
private _generateKey(): string {
return 'key_' + (CommandLineParameterProvider._keyCounter++).toString();
}
private _getParameter<T extends CommandLineParameter>(parameterLongName: string,
expectedKind: CommandLineParameterKind): T {
const parameter: CommandLineParameter | undefined = this._parametersByLongName.get(parameterLongName);
if (!parameter) {
throw new Error(`The parameter "${parameterLongName}" is not defined`);
}
if (parameter.kind !== expectedKind) {
throw new Error(`The parameter "${parameterLongName}" is of type "${CommandLineParameterKind[parameter.kind]}"`
+ ` whereas the caller was expecting "${CommandLineParameterKind[expectedKind]}".`);
}
return parameter as T;
}
private _defineParameter(parameter: CommandLineParameter): void {
const names: string[] = [];
if (parameter.shortName) {
names.push(parameter.shortName);
}
names.push(parameter.longName);
parameter._parserKey = this._generateKey();
let finalDescription: string = parameter.description;
const supplementaryNotes: string[] = [];
parameter._getSupplementaryNotes(supplementaryNotes);
if (supplementaryNotes.length > 0) {
// If they left the period off the end of their sentence, then add one.
if (finalDescription.match(/[a-z0-9]\s*$/i)) {
finalDescription = finalDescription.trimRight() + '.';
}
// Append the supplementary text
finalDescription += ' ' + supplementaryNotes.join(' ');
}
// NOTE: Our "environmentVariable" feature takes precedence over argparse's "defaultValue",
// so we have to reimplement that feature.
const argparseOptions: argparse.ArgumentOptions = {
help: finalDescription,
dest: parameter._parserKey,
metavar: (parameter as CommandLineParameterWithArgument).argumentName || undefined,
required: parameter.required
};
switch (parameter.kind) {
case CommandLineParameterKind.Choice:
const choiceParameter: CommandLineChoiceParameter = parameter as CommandLineChoiceParameter;
argparseOptions.choices = choiceParameter.alternatives as string[];
break;
case CommandLineParameterKind.Flag:
argparseOptions.action = 'storeTrue';
break;
case CommandLineParameterKind.Integer:
argparseOptions.type = 'int';
break;
case CommandLineParameterKind.String:
break;
case CommandLineParameterKind.StringList:
argparseOptions.action = 'append';
break;
}
this._getArgumentParser().addArgument(names, argparseOptions);
this._parameters.push(parameter);
this._parametersByLongName.set(parameter.longName, parameter);
}
}