diff --git a/pkgs/args/CHANGELOG.md b/pkgs/args/CHANGELOG.md index 03e693c5..33b523c5 100644 --- a/pkgs/args/CHANGELOG.md +++ b/pkgs/args/CHANGELOG.md @@ -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 diff --git a/pkgs/args/lib/src/allow_anything_parser.dart b/pkgs/args/lib/src/allow_anything_parser.dart index 46dfc940..ff6634c3 100644 --- a/pkgs/args/lib/src/allow_anything_parser.dart +++ b/pkgs/args/lib/src/allow_anything_parser.dart @@ -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'; @@ -83,7 +81,7 @@ class AllowAnythingParser implements ArgParser { @override ArgResults parse(Iterable args) => - Parser(null, this, Queue.of(args)).parse(); + Parser(null, this, ArgsQueue(args)).parse(); @override String get usage => ''; diff --git a/pkgs/args/lib/src/arg_parser.dart b/pkgs/args/lib/src/arg_parser.dart index c47c0030..f4c7c52b 100644 --- a/pkgs/args/lib/src/arg_parser.dart +++ b/pkgs/args/lib/src/arg_parser.dart @@ -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 args) => - Parser(null, this, Queue.of(args)).parse(); + Parser(null, this, ArgsQueue(args)).parse(); /// Generates a string displaying usage information for the defined options. /// diff --git a/pkgs/args/lib/src/arg_parser_exception.dart b/pkgs/args/lib/src/arg_parser_exception.dart index fbee82b3..c267db7f 100644 --- a/pkgs/args/lib/src/arg_parser_exception.dart +++ b/pkgs/args/lib/src/arg_parser_exception.dart @@ -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'; + } } diff --git a/pkgs/args/lib/src/parser.dart b/pkgs/args/lib/src/parser.dart index 660e56de..957d5a8e 100644 --- a/pkgs/args/lib/src/parser.dart +++ b/pkgs/args/lib/src/parser.dart @@ -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 { + /// The arguments to be parsed. + final Iterable _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 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 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 @@ -26,7 +67,7 @@ class Parser { final ArgParser _grammar; /// The arguments being parsed. - final Queue _args; + final ArgsQueue _args; /// The remaining non-option, non-command arguments. final List _rest; @@ -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); @@ -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-')) { @@ -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? 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); } } diff --git a/pkgs/args/test/parse_test.dart b/pkgs/args/test/parse_test.dart index 9501b5db..0c632d37 100644 --- a/pkgs/args/test/parse_test.dart +++ b/pkgs/args/test/parse_test.dart @@ -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', () { @@ -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', () { @@ -812,7 +823,8 @@ void main() { ['command', '--verbose'], 'Could not find an option named "--verbose".', ['command'], - '--verbose'); + '--verbose', + 1); }); }); }); diff --git a/pkgs/args/test/test_utils.dart b/pkgs/args/test/test_utils.dart index f7d8b8a1..419b20bf 100644 --- a/pkgs/args/test/test_utils.dart +++ b/pkgs/args/test/test_utils.dart @@ -349,14 +349,16 @@ void throwsFormat(ArgParser parser, List args, {String? reason}) { } void throwsArgParserException(ArgParser parser, List args, - String message, List commands, String arg) { + String message, List 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'); }