-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy patharg_parser_extension.dart
More file actions
112 lines (95 loc) · 3.22 KB
/
arg_parser_extension.dart
File metadata and controls
112 lines (95 loc) · 3.22 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
import 'package:args/args.dart';
final _optionRegex = RegExp(r'^--(([a-zA-Z\-_0-9]+)(=(.*))?)?$');
/// Defines if [string] complies with the GNU argument syntax.
///
/// Does not match abbreviated options.
bool isOption(String string) => _optionRegex.hasMatch(string);
final _abbrRegex = RegExp(r'^-(([a-zA-Z0-9]+)(.*))?$');
/// Defines if [string] complies with the GNU argument syntax in an
/// abbreviated form.
bool isAbbr(String string) => _abbrRegex.hasMatch(string);
/// Extends [ArgParser] with utility methods that allow parsing a completion
/// input, which in most cases only regards part of the rules.
extension ArgParserExtension on ArgParser {
/// Tries to parse the minimal subset of valid [args] as valid options.
ArgResults? findValidOptions(List<String> args) {
final loosenOptionsGramamar = _looseOptions();
var currentArgs = args;
while (currentArgs.isNotEmpty) {
try {
return loosenOptionsGramamar.parse(currentArgs);
} on Exception catch (_) {
currentArgs = currentArgs.take(currentArgs.length - 1).toList();
}
}
return null;
}
/// Parses [args] with this [ArgParser]'s command structure only, ignore
/// option strict rules (mandatory, allowed values, non negatable flags,
/// default values, etc);
///
/// Still breaks if an unknown option/alias is passed.
///
/// Returns null if there is an error when parsing, which means the given args
/// do not respect the known command structure.
ArgResults? tryParseCommandsOnly(Iterable<String> args) {
final commandsOnlyGrammar = _cloneCommandsOnly();
final filteredArgs = args.where((element) {
return !isAbbr(element) && !isOption(element) && element.isNotEmpty;
});
try {
return commandsOnlyGrammar.parse(
filteredArgs.where((element) => element.isNotEmpty),
);
} on ArgParserException {
return null;
}
}
/// Recursively copies this [ArgParser] without options.
ArgParser _cloneCommandsOnly() {
final clonedArgParser = ArgParser(
allowTrailingOptions: allowTrailingOptions,
);
for (final entry in commands.entries) {
final parser = entry.value._cloneCommandsOnly();
clonedArgParser.addCommand(entry.key, parser);
}
return clonedArgParser;
}
/// Copies this [ArgParser] with a less strict option mapping.
///
/// It preserves only the options names, types, abbreviations and aliases.
///
/// It disregard subcommands.
ArgParser _looseOptions() {
final clonedArgParser = ArgParser(
allowTrailingOptions: allowTrailingOptions,
);
for (final entry in options.entries) {
final option = entry.value;
if (option.isFlag) {
clonedArgParser.addFlag(
option.name,
abbr: option.abbr,
aliases: option.aliases,
negatable: option.negatable ?? true,
);
}
if (option.isSingle) {
clonedArgParser.addOption(
option.name,
abbr: option.abbr,
aliases: option.aliases,
);
}
if (option.isMultiple) {
clonedArgParser.addMultiOption(
option.name,
abbr: option.abbr,
aliases: option.aliases,
);
}
}
return clonedArgParser;
}
}