parse method
ParsedArguments
parse(
- List<
String> tokens, { - List<
String> ? defaultPath, - CommandRegistry defaultTarget()?,
Reads tokens against the resolved registry and returns typed values.
A --help or --version token ends validation for everything after it:
the remaining tokens are skipped rather than rejected, so
--typo --help fails while --help --typo succeeds. This is deliberate —
an invocation that is only asking for help is answered, not corrected —
but it does mean validation depends on argument order.
Implementation
ParsedArguments parse(
List<String> tokens, {
List<String>? defaultPath,
CommandRegistry Function(CommandRegistry)? defaultTarget,
}) {
final resolution = _registry.resolveCommandPath(
tokens,
defaultTarget: defaultTarget,
);
final commandPath = defaultPath ?? resolution.path;
final registry = defaultPath == null
? resolution.registry
: _registry.descendant(defaultPath);
final values = <Object, Object?>{};
final positionals = <String>[];
final trailing = <String>[];
var help = false;
var version = false;
final consumed = <int>{};
final optionInputs = [
...registry.applicableOptions,
for (final group in registry.pairedOptionGroups) ...group.options,
for (final group in registry.selectedOptions) ...group.options,
];
for (var index = 0; index < tokens.length; index++) {
if (consumed.contains(index) || resolution.tokenIndices.contains(index)) {
continue;
}
final token = tokens[index];
if (token == '--') {
trailing.addAll(tokens.skip(index + 1));
break;
}
if (MambaBuiltInFlags.isHelp(token)) {
values[MambaBuiltInFlags.help] = true;
help = true;
continue;
}
if (MambaBuiltInFlags.isVersion(token)) {
values[MambaBuiltInFlags.version] = true;
version = true;
continue;
}
if ((help || version) && !MambaBuiltInFlags.isControl(token)) continue;
if (token.startsWith('--') && token.length > 2) {
final (name, inline) = _split(token.substring(2));
final accessor = _accessorFor(name, registry.applicableAccessors);
if (accessor != null) {
values[accessor] = _parseValue(
accessor,
_takeValue(tokens, index, consumed, name, inline, accessor),
);
continue;
}
final input = optionInputs
.where((item) => item.name == name)
.firstOrNull;
if (input != null) {
_put(
values,
input,
_parseValue(
input,
_takeValue(tokens, index, consumed, name, inline, input),
),
);
continue;
}
final flag =
registry.applicableFlags
.where((item) => item.name == name)
.firstOrNull ??
registry.applicableFlags
.where(
(item) =>
item is BooleanFlag &&
item.negatable &&
name == 'no-${item.name}',
)
.firstOrNull;
if (flag == null) {
throw _unknownInput(registry, name);
}
if (inline != null) {
throw MambaParseException('Flag --$name does not accept a value');
}
if (flag is BooleanFlag) {
values[flag] = name == 'no-${flag.name}' ? false : true;
} else if (flag is CountFlag) {
values[flag] = ((values[flag] as int?) ?? 0) + 1;
}
continue;
}
if (token.startsWith('-') && token.length > 1) {
final (short, inline) = _split(token.substring(1));
if (short.isEmpty) {
throw MambaParseException(
'An attached value requires a short option name.',
);
}
final letters = short.split('');
for (final (letterIndex, letter) in letters.indexed) {
final input = optionInputs
.where((item) => _shortOf(item) == letter)
.firstOrNull;
if (input != null) {
if (letterIndex != letters.length - 1 ||
(letters.length > 1 && inline == null)) {
throw MambaParseException(
'A bundled value option must be final and use equals-attached supply.',
);
}
_put(
values,
input,
_parseValue(
input,
_takeValue(tokens, index, consumed, input.name, inline, input),
),
);
continue;
}
if (letterIndex == letters.length - 1 && inline != null) {
throw MambaParseException('Flag -$letter does not accept a value');
}
if (letter == MambaBuiltInFlags.help.short) {
// A group does not publish the built-in help flag to its
// descendants, so the clustered path cannot find it by lookup. The
// handle is recorded here so a clustered `-h` reads the same as a
// lone one.
values[MambaBuiltInFlags.help] = true;
help = true;
continue;
}
final flag = registry.applicableFlags
.where((item) => item.short == letter)
.firstOrNull;
if (flag == null) {
throw MambaParseException(
'"-$letter" isn\'t a registered short flag or option.'
'${_shortInventory(registry)}',
);
}
if (identical(flag, MambaBuiltInFlags.version)) version = true;
if (flag is BooleanFlag) values[flag] = true;
if (flag is CountFlag) {
values[flag] = ((values[flag] as int?) ?? 0) + 1;
}
}
continue;
}
positionals.add(token);
}
if (!help && !version) {
final occurrences = values.keys.toSet();
_addDefaults(registry, values);
_validateRequired(registry, values);
_validateConflicts(registry, occurrences);
_validateGroups(registry, values);
_parsePositionals(registry, positionals, values);
_validateVariadic(registry.variadic, trailing);
}
for (final flag in registry.applicableFlags) {
if (flag is BooleanFlag) {
values.putIfAbsent(flag, () => flag.defaultValue);
}
if (flag is CountFlag) values.putIfAbsent(flag, () => 0);
}
_addAccessorMaps(registry, values);
for (final entry in registry.inputLineage.entries) {
if (values.containsKey(entry.value)) {
values[entry.key] = values[entry.value];
}
}
return (
commandPath,
ParsedInputs(values, _knownInputs(registry)),
List.unmodifiable(trailing),
help: help,
version: version,
);
}