Skip to content

Commit f375e0c

Browse files
committed
Add function to retrieve actual input values for command options by name
Signed-off-by: ふぁ <[email protected]>
1 parent ade36c4 commit f375e0c

File tree

4 files changed

+86
-16
lines changed

4 files changed

+86
-16
lines changed

pkgs/args/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
## 2.6.1-wip
22

33
* Fix the reporitory URL in `pubspec.yaml`.
4+
* Add function to retrieve actual input values for command options by name.
45

56
## 2.6.0
67

pkgs/args/lib/src/arg_results.dart

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,12 @@ import 'arg_parser.dart';
1313
ArgResults newArgResults(
1414
ArgParser parser,
1515
Map<String, dynamic> parsed,
16+
Map<String, String> actual,
1617
String? name,
1718
ArgResults? command,
1819
List<String> rest,
1920
List<String> arguments) {
20-
return ArgResults._(parser, parsed, name, command, rest, arguments);
21+
return ArgResults._(parser, parsed, actual, name, command, rest, arguments);
2122
}
2223

2324
/// The results of parsing a series of command line arguments using
@@ -32,6 +33,9 @@ class ArgResults {
3233
/// The option values that were parsed from arguments.
3334
final Map<String, dynamic> _parsed;
3435

36+
/// The actual values that were parsed from arguments.
37+
final Map<String, String> _actual;
38+
3539
/// The name of the command for which these options are parsed, or `null` if
3640
/// these are the top-level results.
3741
final String? name;
@@ -52,8 +56,8 @@ class ArgResults {
5256
/// The original arguments that were parsed.
5357
final List<String> arguments;
5458

55-
ArgResults._(this._parser, this._parsed, this.name, this.command,
56-
List<String> rest, List<String> arguments)
59+
ArgResults._(this._parser, this._parsed, this._actual, this.name,
60+
this.command, List<String> rest, List<String> arguments)
5761
: rest = UnmodifiableListView(rest),
5862
arguments = UnmodifiableListView(arguments);
5963

@@ -134,6 +138,18 @@ class ArgResults {
134138
return result;
135139
}
136140

141+
/// Returns the actual value of the command-line option named [name].
142+
/// Returns `null` if the option was not provided.
143+
///
144+
/// [name] must be a valid option name in the parser.
145+
String? actual(String name) {
146+
if (_actual.containsKey(name)) {
147+
return _actual[name];
148+
} else {
149+
return null;
150+
}
151+
}
152+
137153
/// Returns `true` if the option with [name] was parsed from an actual
138154
/// argument.
139155
///

pkgs/args/lib/src/parser.dart

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ class Parser {
3434
/// The accumulated parsed options.
3535
final Map<String, dynamic> _results = <String, dynamic>{};
3636

37+
/// The actual inputs that were parsed.
38+
final Map<String, String> _actualStores = <String, String>{};
39+
3740
Parser(this._commandName, this._grammar, this._args,
3841
[this._parent, List<String>? rest])
3942
: _rest = [...?rest];
@@ -45,8 +48,8 @@ class Parser {
4548
ArgResults parse() {
4649
var arguments = _args.toList();
4750
if (_grammar.allowsAnything) {
48-
return newArgResults(
49-
_grammar, const {}, _commandName, null, arguments, arguments);
51+
return newArgResults(_grammar, const {}, const {}, _commandName, null,
52+
arguments, arguments);
5053
}
5154

5255
ArgResults? commandResults;
@@ -116,8 +119,8 @@ class Parser {
116119
// Add in the leftover arguments we didn't parse to the innermost command.
117120
_rest.addAll(_args);
118121
_args.clear();
119-
return newArgResults(
120-
_grammar, _results, _commandName, commandResults, _rest, arguments);
122+
return newArgResults(_grammar, _results, _actualStores, _commandName,
123+
commandResults, _rest, arguments);
121124
}
122125

123126
/// Pulls the value for [option] from the second argument in [_args].
@@ -127,7 +130,7 @@ class Parser {
127130
// Take the option argument from the next command line arg.
128131
_validate(_args.isNotEmpty, 'Missing argument for "$arg".', arg);
129132

130-
_setOption(_results, option, _current, arg);
133+
_setOption(_results, option, _current, _actualStores, arg);
131134
_args.removeFirst();
132135
}
133136

@@ -158,7 +161,7 @@ class Parser {
158161
_args.removeFirst();
159162

160163
if (option.isFlag) {
161-
_setFlag(_results, option, true);
164+
_setFlag(_results, option, true, _actualStores, '-$opt');
162165
} else {
163166
_readNextArgAsValue(option, '-$opt');
164167
}
@@ -207,7 +210,7 @@ class Parser {
207210
// The first character is a non-flag option, so the rest must be the
208211
// value.
209212
var value = '${lettersAndDigits.substring(1)}$rest';
210-
_setOption(_results, first, value, '-$c');
213+
_setOption(_results, first, value, _actualStores, '-$c');
211214
} else {
212215
// If we got some non-flag characters, then it must be a value, but
213216
// if we got here, it's a flag, which is wrong.
@@ -246,7 +249,7 @@ class Parser {
246249
_validate(option.isFlag,
247250
'Option "-$c" must be a flag to be in a collapsed "-".', '-$c');
248251

249-
_setFlag(_results, option, true);
252+
_setFlag(_results, option, true, _actualStores, '-$c');
250253
}
251254

252255
/// Tries to parse the current argument as a long-form named option, which
@@ -279,10 +282,10 @@ class Parser {
279282
_validate(value == null,
280283
'Flag option "--$name" should not be given a value.', '--$name');
281284

282-
_setFlag(_results, option, true);
285+
_setFlag(_results, option, true, _actualStores, '--$name');
283286
} else if (value != null) {
284287
// We have a value like --foo=bar.
285-
_setOption(_results, option, value, '--$name');
288+
_setOption(_results, option, value, _actualStores, '--$name');
286289
} else {
287290
// Option like --foo, so look for the value as the next arg.
288291
_readNextArgAsValue(option, '--$name');
@@ -304,7 +307,7 @@ class Parser {
304307
_validate(
305308
option.negatable!, 'Cannot negate option "--$name".', '--$name');
306309

307-
_setFlag(_results, option, false);
310+
_setFlag(_results, option, false, _actualStores, '--$name');
308311
} else {
309312
// Walk up to the parent command if possible.
310313
_validate(_parent != null, 'Could not find an option named "--$name".',
@@ -327,8 +330,10 @@ class Parser {
327330

328331
/// Validates and stores [value] as the value for [option], which must not be
329332
/// a flag.
330-
void _setOption(Map results, Option option, String value, String arg) {
333+
void _setOption(Map results, Option option, String value,
334+
Map<String, String> actualStores, String arg) {
331335
assert(!option.isFlag);
336+
actualStores[option.name] = arg;
332337

333338
if (!option.isMultiple) {
334339
_validateAllowed(option, value, arg);
@@ -351,9 +356,11 @@ class Parser {
351356

352357
/// Validates and stores [value] as the value for [option], which must be a
353358
/// flag.
354-
void _setFlag(Map results, Option option, bool value) {
359+
void _setFlag(Map results, Option option, bool value,
360+
Map<String, String> actualStores, String source) {
355361
assert(option.isFlag);
356362
results[option.name] = value;
363+
actualStores[option.name] = source;
357364
}
358365

359366
/// Validates that [value] is allowed as a value of [option].

pkgs/args/test/parse_test.dart

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -730,6 +730,52 @@ void main() {
730730
});
731731
});
732732

733+
group('actual()', () {
734+
test('returns value if present', () {
735+
var parser = ArgParser();
736+
parser.addFlag('verbose');
737+
parser.addOption('mode');
738+
parser.addMultiOption('define');
739+
740+
var args = parser.parse(['--verbose', '--mode=release', '--define=1']);
741+
expect(args.actual('verbose'), '--verbose');
742+
expect(args.actual('mode'), '--mode');
743+
expect(args.actual('define'), '--define');
744+
});
745+
746+
test('returns null if missing', () {
747+
var parser = ArgParser();
748+
parser.addFlag('a', defaultsTo: true);
749+
parser.addOption('b', defaultsTo: 'c');
750+
parser.addMultiOption('d', defaultsTo: ['e']);
751+
752+
var args = parser.parse([]);
753+
expect(args.actual('a'), isNull);
754+
expect(args.actual('b'), isNull);
755+
expect(args.actual('d'), isNull);
756+
});
757+
758+
test('can match by alias', () {
759+
var parser = ArgParser()..addFlag('verbose', abbr: 'v');
760+
var results = parser.parse(['-v']);
761+
expect(results.actual('verbose'), '-v');
762+
});
763+
764+
test('can be negated by alias', () {
765+
var parser = ArgParser()
766+
..addFlag('a', aliases: ['b'], defaultsTo: true, negatable: true);
767+
var results = parser.parse(['--no-b']);
768+
expect(results.actual('a'), '--no-b');
769+
});
770+
771+
// abbr test
772+
test('can match by abbreviation', () {
773+
var parser = ArgParser()..addFlag('a', abbr: 'b');
774+
var results = parser.parse(['-b']);
775+
expect(results.actual('a'), '-b');
776+
});
777+
});
778+
733779
group('remaining args', () {
734780
test('stops parsing args when a non-option-like arg is encountered', () {
735781
var parser = ArgParser();

0 commit comments

Comments
 (0)