Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[package:args] Added offset argument when throwing a ArgParserException. #142

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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