Skip to content

Commit a435d57

Browse files
committed
Add "defaultValue" and "required" options for command-line parameters
1 parent 47e43ac commit a435d57

File tree

4 files changed

+77
-12
lines changed

4 files changed

+77
-12
lines changed

common/reviews/api/ts-command-line.api.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ class CommandLineIntegerParameter extends CommandLineParameterWithArgument {
5252
constructor(definition: ICommandLineIntegerDefinition);
5353
// @internal
5454
_setValue(data: any): void;
55+
readonly defaultValue: number | undefined;
5556
readonly kind: CommandLineParameterKind;
5657
readonly value: number | undefined;
5758
}
@@ -68,7 +69,10 @@ class CommandLineParameter {
6869
readonly kind: CommandLineParameterKind;
6970
readonly longName: string;
7071
protected reportInvalidData(data: any): never;
72+
readonly required: boolean;
7173
readonly shortName: string | undefined;
74+
// (undocumented)
75+
protected validateDefaultValue(hasDefaultValue: boolean): void;
7276
}
7377

7478
// @public
@@ -142,6 +146,7 @@ class CommandLineStringParameter extends CommandLineParameterWithArgument {
142146
constructor(definition: ICommandLineStringDefinition);
143147
// @internal
144148
_setValue(data: any): void;
149+
readonly defaultValue: string | undefined;
145150
readonly kind: CommandLineParameterKind;
146151
readonly value: string | undefined;
147152
}
@@ -165,6 +170,7 @@ interface IBaseCommandLineDefinition {
165170
description: string;
166171
parameterLongName: string;
167172
parameterShortName?: string;
173+
required?: boolean;
168174
}
169175

170176
// @public
@@ -191,6 +197,7 @@ interface ICommandLineFlagDefinition extends IBaseCommandLineDefinition {
191197

192198
// @public
193199
interface ICommandLineIntegerDefinition extends IBaseCommandLineDefinitionWithArgument {
200+
defaultValue?: number;
194201
}
195202

196203
// @public
@@ -201,6 +208,7 @@ interface ICommandLineParserOptions {
201208

202209
// @public
203210
interface ICommandLineStringDefinition extends IBaseCommandLineDefinitionWithArgument {
211+
defaultValue?: string;
204212
}
205213

206214
// @public

libraries/ts-command-line/src/CommandLineDefinition.ts

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,12 @@ export interface IBaseCommandLineDefinition {
2121
* Documentation for the flag, that will be shown when invoking the tool with "--help"
2222
*/
2323
description: string;
24+
25+
/**
26+
* If true, then an error occurs if the parameter was not included on the command-line
27+
* or provided via an environment variable.
28+
*/
29+
required?: boolean;
2430
}
2531

2632
/**
@@ -56,7 +62,7 @@ export interface ICommandLineChoiceDefinition extends IBaseCommandLineDefinition
5662
alternatives: string[];
5763

5864
/**
59-
* The default value which will be used if the parameter is omitted from the command line
65+
* {@inheritdoc ICommandLineStringDefinition.defaultValue}
6066
*/
6167
defaultValue?: string;
6268
}
@@ -75,15 +81,30 @@ export interface ICommandLineFlagDefinition extends IBaseCommandLineDefinition {
7581
*
7682
* @public
7783
*/
78-
export interface ICommandLineIntegerDefinition extends IBaseCommandLineDefinitionWithArgument { }
84+
export interface ICommandLineIntegerDefinition extends IBaseCommandLineDefinitionWithArgument {
85+
/**
86+
* {@inheritdoc ICommandLineStringDefinition.defaultValue}
87+
*/
88+
defaultValue?: number;
89+
}
7990

8091
/**
8192
* For use with CommandLineParser, this interface represents a command line parameter
8293
* whose argument is a string value.
8394
*
8495
* @public
8596
*/
86-
export interface ICommandLineStringDefinition extends IBaseCommandLineDefinitionWithArgument { }
97+
export interface ICommandLineStringDefinition extends IBaseCommandLineDefinitionWithArgument {
98+
/**
99+
* The default value which will be used if the parameter is omitted from the command line.
100+
*
101+
* @remarks
102+
* If a default value is specified, then {@link IBaseCommandLineDefinition.required}
103+
* must not be true. Instead, a custom error message should be used to report cases
104+
* where the value could not be determined.
105+
*/
106+
defaultValue?: string;
107+
}
87108

88109
/**
89110
* For use with CommandLineParser, this interface represents a command line parameter

libraries/ts-command-line/src/CommandLineParameter.ts

Lines changed: 38 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -53,22 +53,27 @@ export abstract class CommandLineParameter {
5353
/** {@inheritdoc IBaseCommandLineDefinition.description} */
5454
public readonly description: string;
5555

56+
/** {@inheritdoc IBaseCommandLineDefinition.required} */
57+
public readonly required: boolean;
58+
5659
/** @internal */
5760
constructor(definition: IBaseCommandLineDefinition) {
58-
if (!CommandLineParameter._longNameRegExp.test(definition.parameterLongName)) {
59-
throw new Error(`Invalid name: "${definition.parameterLongName}". The parameter long name must be`
61+
this.longName = definition.parameterLongName;
62+
this.shortName = definition.parameterShortName;
63+
this.description = definition.description;
64+
this.required = !!definition.required;
65+
66+
if (!CommandLineParameter._longNameRegExp.test(this.longName)) {
67+
throw new Error(`Invalid name: "${this.longName}". The parameter long name must be`
6068
+ ` lower-case and use dash delimiters (e.g. "--do-a-thing")`);
6169
}
62-
this.longName = definition.parameterLongName;
6370

64-
if (definition.parameterShortName) {
65-
if (!CommandLineParameter._shortNameRegExp.test(definition.parameterShortName)) {
66-
throw new Error(`Invalid name: "${definition.parameterShortName}". The parameter short name must be`
71+
if (this.shortName) {
72+
if (!CommandLineParameter._shortNameRegExp.test(this.shortName)) {
73+
throw new Error(`Invalid name: "${this.shortName}". The parameter short name must be`
6774
+ ` a dash followed by a single upper-case or lower-case letter (e.g. "-a")`);
6875
}
6976
}
70-
this.shortName = definition.parameterShortName;
71-
this.description = definition.description;
7277
}
7378

7479
/**
@@ -89,6 +94,18 @@ export abstract class CommandLineParameter {
8994
throw new Error(`Unexpected data object for parameter "${this.longName}": `
9095
+ JSON.stringify(data));
9196
}
97+
98+
protected validateDefaultValue(hasDefaultValue: boolean): void {
99+
if (this.required && hasDefaultValue) {
100+
// If a parameter is "required", then the user understands that they always need to
101+
// specify a value for this parameter (either via the command line or via an environment variable).
102+
// It would be confusing to allow a default value that sometimes allows the "required" parameter
103+
// to be omitted. If you sometimes don't have a suitable default value, then the better approach
104+
// is to throw a custom error explaining why the parameter is required in that case.
105+
throw new Error(`A default value cannot be specified for "${this.longName}"`
106+
+ ` because it is a "required" parameter`);
107+
}
108+
}
92109
}
93110

94111
/**
@@ -134,7 +151,7 @@ export class CommandLineChoiceParameter extends CommandLineParameter {
134151
/** {@inheritdoc ICommandLineChoiceDefinition.alternatives} */
135152
public readonly alternatives: ReadonlyArray<string>;
136153

137-
/** {@inheritdoc ICommandLineChoiceDefinition.defaultValue} */
154+
/** {@inheritdoc ICommandLineStringDefinition.defaultValue} */
138155
public readonly defaultValue: string | undefined;
139156

140157
private _value: string | undefined = undefined;
@@ -153,6 +170,7 @@ export class CommandLineChoiceParameter extends CommandLineParameter {
153170

154171
this.alternatives = definition.alternatives;
155172
this.defaultValue = definition.defaultValue;
173+
this.validateDefaultValue(!!this.defaultValue);
156174
}
157175

158176
/** {@inheritdoc CommandLineParameter.kind} */
@@ -236,11 +254,16 @@ export class CommandLineFlagParameter extends CommandLineParameter {
236254
* @public
237255
*/
238256
export class CommandLineIntegerParameter extends CommandLineParameterWithArgument {
257+
/** {@inheritdoc ICommandLineStringDefinition.defaultValue} */
258+
public readonly defaultValue: number | undefined;
259+
239260
private _value: number | undefined = undefined;
240261

241262
/** @internal */
242263
constructor(definition: ICommandLineIntegerDefinition) {
243264
super(definition);
265+
this.defaultValue = definition.defaultValue;
266+
this.validateDefaultValue(!!this.defaultValue);
244267
}
245268

246269
/** {@inheritdoc CommandLineParameter.kind} */
@@ -280,11 +303,17 @@ export class CommandLineIntegerParameter extends CommandLineParameterWithArgumen
280303
* @public
281304
*/
282305
export class CommandLineStringParameter extends CommandLineParameterWithArgument {
306+
/** {@inheritdoc ICommandLineStringDefinition.defaultValue} */
307+
public readonly defaultValue: string | undefined;
308+
283309
private _value: string | undefined = undefined;
284310

285311
/** @internal */
286312
constructor(definition: ICommandLineStringDefinition) {
287313
super(definition);
314+
315+
this.defaultValue = definition.defaultValue;
316+
this.validateDefaultValue(!!this.defaultValue);
288317
}
289318

290319
/** {@inheritdoc CommandLineParameter.kind} */

libraries/ts-command-line/src/CommandLineParameterProvider.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,7 @@ export abstract class CommandLineParameterProvider {
221221
const argparseOptions: argparse.ArgumentOptions = {
222222
help: parameter.description,
223223
dest: parameter._parserKey,
224+
required: parameter.required,
224225
metavar: (parameter as CommandLineParameterWithArgument).argumentName || undefined
225226
};
226227

@@ -234,7 +235,13 @@ export abstract class CommandLineParameterProvider {
234235
argparseOptions.action = 'storeTrue';
235236
break;
236237
case CommandLineParameterKind.Integer:
238+
const integerParameter: CommandLineIntegerParameter = parameter as CommandLineIntegerParameter;
237239
argparseOptions.type = 'int';
240+
argparseOptions.defaultValue = integerParameter.defaultValue;
241+
break;
242+
case CommandLineParameterKind.String:
243+
const stringDefaultValue: CommandLineStringParameter = parameter as CommandLineStringParameter;
244+
argparseOptions.defaultValue = stringDefaultValue.defaultValue;
238245
break;
239246
case CommandLineParameterKind.StringList:
240247
argparseOptions.action = 'append';

0 commit comments

Comments
 (0)