Skip to content

Commit

Permalink
Added offset argument when throwing a ArgParserException.
Browse files Browse the repository at this point in the history
Signed-off-by: ふぁ <[email protected]>
  • Loading branch information
fa0311 committed Oct 16, 2024
1 parent ade36c4 commit 0da04c4
Show file tree
Hide file tree
Showing 7 changed files with 85 additions and 24 deletions.
1 change: 1 addition & 0 deletions pkgs/args/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
## 2.6.1-wip

* Fix the reporitory URL in `pubspec.yaml`.
* Added offset argument when throwing a `ArgParserException`.

## 2.6.0

Expand Down
4 changes: 1 addition & 3 deletions pkgs/args/lib/src/allow_anything_parser.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import 'dart:collection';

import 'arg_parser.dart';
import 'arg_results.dart';
import 'option.dart';
Expand Down Expand Up @@ -83,7 +81,7 @@ class AllowAnythingParser implements ArgParser {

@override
ArgResults parse(Iterable<String> args) =>
Parser(null, this, Queue.of(args)).parse();
Parser(null, this, ArgsQueue(args)).parse();

@override
String get usage => '';
Expand Down
2 changes: 1 addition & 1 deletion pkgs/args/lib/src/arg_parser.dart
Original file line number Diff line number Diff line change
Expand Up @@ -331,7 +331,7 @@ class ArgParser {
/// Parses [args], a list of command-line arguments, matches them against the
/// flags and options defined by this parser, and returns the result.
ArgResults parse(Iterable<String> args) =>
Parser(null, this, Queue.of(args)).parse();
Parser(null, this, ArgsQueue(args)).parse();

/// Generates a string displaying usage information for the defined options.
///
Expand Down
6 changes: 6 additions & 0 deletions pkgs/args/lib/src/arg_parser_exception.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,10 @@ class ArgParserException extends FormatException {
super.source,
super.offset])
: commands = commands == null ? const [] : List.unmodifiable(commands);

/// Returns a string representation of this exception.
@override
String toString() {
return 'FormatException: $message';
}
}
58 changes: 50 additions & 8 deletions pkgs/args/lib/src/parser.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,54 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import 'dart:collection';

import 'arg_parser.dart';
import 'arg_parser_exception.dart';
import 'arg_results.dart';
import 'option.dart';

/// A queue of arguments that can be removed from the front.
/// This is used to keep track of the arguments that have been parsed.
class ArgsQueue<T> {
/// The arguments to be parsed.
final Iterable<T> _args;

/// The index of the next argument to be parsed.
int _index = 0;

/// Creates a new queue of arguments.
ArgsQueue(this._args);

/// The source of the arguments.
Iterable<T> get args => _args;

/// The current index of the queue.
int get index => _index;

/// The number of arguments in the queue.
bool get isEmpty => _index >= _args.length;

/// The number of arguments in the queue.
bool get isNotEmpty => _index < _args.length;

/// The first argument in the queue.
T get first => _args.elementAt(_index);

/// Removes the first argument from the queue.
T removeFirst() {
return _args.elementAt(_index++);
}

/// Returns the remaining arguments in the queue.
List<T> toList() {
return _args.skip(_index).toList();
}

/// Clears the queue.
void clear() {
_index = _args.length;
}
}

/// The actual argument parsing class.
///
/// Unlike [ArgParser] which is really more an "arg grammar", this is the class
Expand All @@ -26,7 +67,7 @@ class Parser {
final ArgParser _grammar;

/// The arguments being parsed.
final Queue<String> _args;
final ArgsQueue<String> _args;

/// The remaining non-option, non-command arguments.
final List<String> _rest;
Expand Down Expand Up @@ -114,7 +155,7 @@ class Parser {
});

// Add in the leftover arguments we didn't parse to the innermost command.
_rest.addAll(_args);
_rest.addAll(_args.toList());
_args.clear();
return newArgResults(
_grammar, _results, _commandName, commandResults, _rest, arguments);
Expand Down Expand Up @@ -274,17 +315,19 @@ class Parser {
bool _handleLongOption(String name, String? value) {
var option = _grammar.findByNameOrAlias(name);
if (option != null) {
_args.removeFirst();
if (option.isFlag) {
_validate(value == null,
'Flag option "--$name" should not be given a value.', '--$name');

_args.removeFirst();
_setFlag(_results, option, true);
} else if (value != null) {
// We have a value like --foo=bar.
_args.removeFirst();
_setOption(_results, option, value, '--$name');
} else {
// Option like --foo, so look for the value as the next arg.
_args.removeFirst();
_readNextArgAsValue(option, '--$name');
}
} else if (name.startsWith('no-')) {
Expand Down Expand Up @@ -318,10 +361,9 @@ class Parser {
/// Called during parsing to validate the arguments.
///
/// Throws an [ArgParserException] if [condition] is `false`.
void _validate(bool condition, String message,
[String? args, List<String>? source, int? offset]) {
void _validate(bool condition, String message, [String? args]) {
if (!condition) {
throw ArgParserException(message, null, args, source, offset);
throw ArgParserException(message, null, args, _args.args, _args.index);
}
}

Expand Down
32 changes: 22 additions & 10 deletions pkgs/args/test/parse_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -771,18 +771,23 @@ void main() {
test('throws exception for unknown option', () {
var parser = ArgParser();
throwsArgParserException(parser, ['--verbose'],
'Could not find an option named "--verbose".', [], '--verbose');
throwsArgParserException(
parser, ['-v'], 'Could not find an option or flag "-v".', [], '-v');
'Could not find an option named "--verbose".', [], '--verbose', 0);
throwsArgParserException(parser, ['-v'],
'Could not find an option or flag "-v".', [], '-v', 0);
});

test('throws exception for flag with value', () {
var parser = ArgParser();
parser.addFlag('flag', abbr: 'f');
throwsArgParserException(parser, ['--flag=1'],
'Flag option "--flag" should not be given a value.', [], '--flag');
throwsArgParserException(
parser,
['--flag=1'],
'Flag option "--flag" should not be given a value.',
[],
'--flag',
0);
throwsArgParserException(parser, ['-f=1'],
'Option "-f" is a flag and cannot handle value "=1".', [], '-f');
'Option "-f" is a flag and cannot handle value "=1".', [], '-f', 0);
});

test('throws exception after parsing multiple options', () {
Expand All @@ -794,14 +799,20 @@ void main() {
['--first', '1', '--second', '2', '--verbose', '3'],
'Could not find an option named "--verbose".',
[],
'--verbose');
'--verbose',
4);
});

test('throws exception for option with invalid value', () {
var parser = ArgParser();
parser.addOption('first', allowed: ['a', 'b']);
throwsArgParserException(parser, ['--first', 'c'],
'"c" is not an allowed value for option "--first".', [], '--first');
throwsArgParserException(
parser,
['--first', 'c'],
'"c" is not an allowed value for option "--first".',
[],
'--first',
1);
});

test('throws exception after parsing command', () {
Expand All @@ -812,7 +823,8 @@ void main() {
['command', '--verbose'],
'Could not find an option named "--verbose".',
['command'],
'--verbose');
'--verbose',
1);
});
});
});
Expand Down
6 changes: 4 additions & 2 deletions pkgs/args/test/test_utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -349,14 +349,16 @@ void throwsFormat(ArgParser parser, List<String> args, {String? reason}) {
}

void throwsArgParserException(ArgParser parser, List<String> args,
String message, List<String> commands, String arg) {
String message, List<String> commands, String argumentName, int offset) {
try {
parser.parse(args);
fail('Expected an ArgParserException');
} on ArgParserException catch (e) {
expect(e.message, message);
expect(e.commands, commands);
expect(e.argumentName, arg);
expect(e.argumentName, argumentName);
expect(e.source, args);
expect(e.offset, offset);
} catch (e) {
fail('Expected an ArgParserException, but got $e');
}
Expand Down

0 comments on commit 0da04c4

Please sign in to comment.