Overhaul the argument parsing API
This commit is contained in:
parent
62d26082c9
commit
82f5c18129
9 changed files with 207 additions and 153 deletions
|
@ -3,7 +3,6 @@ import 'package:libac_dart/argparse/types/Integers.dart';
|
||||||
import 'package:libac_dart/argparse/types/String.dart';
|
import 'package:libac_dart/argparse/types/String.dart';
|
||||||
|
|
||||||
abstract class Argument<T> {
|
abstract class Argument<T> {
|
||||||
bool hasValue = false;
|
|
||||||
String name;
|
String name;
|
||||||
|
|
||||||
String description;
|
String description;
|
||||||
|
@ -46,53 +45,75 @@ abstract class Argument<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
ArgumentType getType();
|
ArgumentType getType();
|
||||||
|
|
||||||
|
bool hasValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
enum ArgumentType { STRING, BOOL, INTEGER, DOUBLE }
|
enum ArgumentType { STRING, BOOL, INTEGER, DOUBLE }
|
||||||
|
|
||||||
class Arguments {
|
class Arguments {
|
||||||
Map<String, Argument> _args = {};
|
Map<String, Argument> args = {};
|
||||||
|
|
||||||
void setArg(Argument arg) {
|
void setArg(Argument arg) {
|
||||||
_args[arg.name] = arg;
|
args[arg.name] = arg;
|
||||||
}
|
}
|
||||||
|
|
||||||
int get count => _args.length;
|
int get count => args.length;
|
||||||
|
|
||||||
List<String> getArgNames() {
|
List<String> getArgNames() {
|
||||||
List<String> args = [];
|
List<String> argsx = [];
|
||||||
for (MapEntry<String, Argument> entry in _args.entries) {
|
for (MapEntry<String, Argument> entry in args.entries) {
|
||||||
args.add(entry.key);
|
argsx.add(entry.key);
|
||||||
}
|
}
|
||||||
|
|
||||||
return args;
|
return argsx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Removes the argument if present. Otherwise does nothing.
|
||||||
|
///
|
||||||
|
/// Returns true if the argument was present and removed. False otherwise.
|
||||||
|
bool removeArg(String name) {
|
||||||
|
if (args.containsKey(name)) {
|
||||||
|
args.remove(name);
|
||||||
|
return true;
|
||||||
|
} else
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Argument? getArg(String name) {
|
Argument? getArg(String name) {
|
||||||
if (hasArg(name))
|
if (hasArg(name))
|
||||||
return _args[name];
|
return args[name];
|
||||||
else
|
else
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool hasArg(String name) {
|
bool hasArg(String name) {
|
||||||
return _args.containsKey(name);
|
return args.containsKey(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool argHasValue(String name) {
|
bool argHasValue(String name) {
|
||||||
if (hasArg(name)) {
|
if (hasArg(name)) {
|
||||||
Argument arg = getArg(name)!;
|
Argument arg = getArg(name)!;
|
||||||
return arg.hasValue;
|
return arg.hasValue();
|
||||||
} else
|
} else
|
||||||
throw new Exception("Warning: No such argument");
|
throw new Exception("Warning: No such argument");
|
||||||
}
|
}
|
||||||
|
|
||||||
Arguments clone() {
|
Arguments clone() {
|
||||||
Arguments args = Arguments();
|
Arguments argsx = Arguments();
|
||||||
for (MapEntry<String, Argument> entry in _args.entries) {
|
for (MapEntry<String, Argument> entry in args.entries) {
|
||||||
args.setArg(entry.value.clone());
|
argsx.setArg(entry.value.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
return args;
|
return argsx;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Argument> getArgumentsList() {
|
||||||
|
List<Argument> argsx = [];
|
||||||
|
for (var entry in args.entries) {
|
||||||
|
argsx.add(entry.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return argsx;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,109 +1,51 @@
|
||||||
import 'package:libac_dart/argparse/types/Bool.dart';
|
import 'package:libac_dart/argparse/Args.dart';
|
||||||
import 'package:libac_dart/argparse/types/Integers.dart';
|
|
||||||
import 'package:libac_dart/argparse/types/String.dart';
|
|
||||||
import 'package:libac_dart/nbt/Stream.dart';
|
|
||||||
|
|
||||||
import 'Args.dart';
|
class ArgumentHelpers {
|
||||||
|
/// Generates a command-line help message for the provided arguments.
|
||||||
class CLIHelper {
|
|
||||||
/// Generates usage info
|
|
||||||
///
|
///
|
||||||
/// This will create a standard CLI Usage info string that can be directly printed to the console after the program header
|
/// [arguments] The list of arguments to generate help for.
|
||||||
static String makeArgCLIHelp(Arguments defaults) {
|
/// [programName] The name of the program.
|
||||||
StringBuilder builder = StringBuilder();
|
/// Returns a string containing the help message.
|
||||||
List<String> argNames = defaults.getArgNames();
|
static String generateHelpMessage(
|
||||||
|
List<Argument<dynamic>> arguments, String programName) {
|
||||||
|
final StringBuffer helpMessage = StringBuffer();
|
||||||
|
helpMessage.writeln('Usage: $programName [options]');
|
||||||
|
|
||||||
for (String name in argNames) {
|
for (var arg in arguments) {
|
||||||
builder.append("--${name}");
|
final description = _getArgumentDescription(arg);
|
||||||
|
final valueType = arg.getType().toString();
|
||||||
Argument arg = defaults.getArg(name)!;
|
helpMessage.writeln(
|
||||||
if (arg.hasValue) {
|
' --${arg.name} [${valueType.split('.').last}] $description');
|
||||||
builder.append("=<...>");
|
|
||||||
}
|
|
||||||
|
|
||||||
builder.append(
|
|
||||||
"\t\t\t${arg.description} ${(arg.getValue().toString() == "%" || arg.getType() == ArgumentType.BOOL) ? "" : "[Default: ${arg.getValue()}]"}\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return builder.toString();
|
return helpMessage.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parses and returns the arguments list with keeping defaults in mind
|
/// Gets a description for an argument. This can be extended to provide more info.
|
||||||
static Future<Arguments> parseArgs(
|
///
|
||||||
List<String> args, Arguments defaults) async {
|
/// [argument] The argument for which to generate a description.
|
||||||
Arguments arguments = defaults.clone();
|
/// Returns a description of the argument.
|
||||||
for (int i = 0; i < args.length; i++) {
|
static String _getArgumentDescription(Argument<dynamic> argument) {
|
||||||
Argument arg = await _parseArgument(args[i]);
|
// You can extend this to add more detailed descriptions for specific arguments
|
||||||
Argument? defArg;
|
return argument.hasValue()
|
||||||
if (defaults.hasArg(arg.name)) defArg = defaults.getArg(arg.name)!;
|
? 'Default value: ${argument.getValue()}'
|
||||||
|
: 'No default value assigned';
|
||||||
|
}
|
||||||
|
|
||||||
if (!arg.hasValue) {
|
/// Retrieves the type of an argument in a human-readable format.
|
||||||
if (defArg != null) {
|
///
|
||||||
arg = defArg;
|
/// [argument] The argument to get the type for.
|
||||||
}
|
/// Returns a string describing the argument type.
|
||||||
}
|
static String _getArgumentType(Argument<dynamic> argument) {
|
||||||
|
switch (argument.getType()) {
|
||||||
arguments.setArg(arg);
|
case ArgumentType.STRING:
|
||||||
|
return 'string';
|
||||||
|
case ArgumentType.INTEGER:
|
||||||
|
return 'integer';
|
||||||
|
case ArgumentType.BOOL:
|
||||||
|
return 'boolean';
|
||||||
|
case ArgumentType.DOUBLE:
|
||||||
|
return 'double';
|
||||||
}
|
}
|
||||||
|
|
||||||
return arguments;
|
|
||||||
}
|
|
||||||
|
|
||||||
static Future<Argument> _parseArgument(String arg) async {
|
|
||||||
if (arg.startsWith("--")) {
|
|
||||||
var parts = arg.split("=");
|
|
||||||
String name = await _getNamePart(parts[0]);
|
|
||||||
if (parts.length == 1)
|
|
||||||
return BoolArgument(name: name);
|
|
||||||
else if (parts.length == 2) {
|
|
||||||
String value = await _getValuePart(parts[1]);
|
|
||||||
ArgumentType typeOfArg = await _getArgumentType(value);
|
|
||||||
|
|
||||||
switch (typeOfArg) {
|
|
||||||
case ArgumentType.INTEGER:
|
|
||||||
{
|
|
||||||
return IntegerArgument(name: name, value: int.parse(value));
|
|
||||||
}
|
|
||||||
case ArgumentType.BOOL:
|
|
||||||
{
|
|
||||||
return BoolArgument(name: name);
|
|
||||||
}
|
|
||||||
case ArgumentType.DOUBLE:
|
|
||||||
{
|
|
||||||
return DoubleArgument(name: name, value: double.parse(value));
|
|
||||||
}
|
|
||||||
case ArgumentType.STRING:
|
|
||||||
{
|
|
||||||
return StringArgument(name: name, value: value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
throw new Exception("Argument is malformed");
|
|
||||||
} else
|
|
||||||
throw new Exception("Not in a valid format");
|
|
||||||
}
|
|
||||||
|
|
||||||
static Future<String> _getNamePart(String value) async {
|
|
||||||
return value.substring(2);
|
|
||||||
}
|
|
||||||
|
|
||||||
static Future<String> _getValuePart(String entry) async {
|
|
||||||
return entry;
|
|
||||||
}
|
|
||||||
|
|
||||||
static Future<ArgumentType> _getArgumentType(String input) async {
|
|
||||||
try {
|
|
||||||
int.parse(input);
|
|
||||||
return ArgumentType.INTEGER;
|
|
||||||
} catch (E) {}
|
|
||||||
try {
|
|
||||||
double.parse(input);
|
|
||||||
return ArgumentType.DOUBLE;
|
|
||||||
} catch (E) {}
|
|
||||||
|
|
||||||
if (input.isEmpty)
|
|
||||||
return ArgumentType.BOOL;
|
|
||||||
else
|
|
||||||
return ArgumentType.STRING;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
52
lib/argparse/Parser.dart
Normal file
52
lib/argparse/Parser.dart
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
import 'package:libac_dart/argparse/Args.dart';
|
||||||
|
import 'package:libac_dart/argparse/types/Bool.dart';
|
||||||
|
import 'package:libac_dart/argparse/types/Integers.dart';
|
||||||
|
import 'package:libac_dart/argparse/types/String.dart';
|
||||||
|
|
||||||
|
class ArgumentParser {
|
||||||
|
/// Parses a list of arguments.
|
||||||
|
///
|
||||||
|
/// [args] The list of arguments to parse.
|
||||||
|
/// Returns an Arguments object representing the input.
|
||||||
|
static Arguments parse(List<String> args) {
|
||||||
|
final ret = Arguments();
|
||||||
|
for (var i = 0; i < args.length; i++) {
|
||||||
|
final arg = args[i];
|
||||||
|
if (arg.startsWith('--')) {
|
||||||
|
var key = arg.substring(2); // Remove the '--' part of the argument
|
||||||
|
dynamic value;
|
||||||
|
|
||||||
|
// Check if the argument has a value attached (either --arg=value or --arg value)
|
||||||
|
if (i + 1 < args.length && !args[i + 1].startsWith('--')) {
|
||||||
|
value = args[i + 1]; // --arg value
|
||||||
|
i++; // Skip the next argument as it is the value
|
||||||
|
} else if (arg.contains('=')) {
|
||||||
|
value = arg.substring(arg.indexOf('=') + 1); // --arg=value
|
||||||
|
key = key.substring(0, key.indexOf('='));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine the argument type and add it to the list
|
||||||
|
if (int.tryParse(value?.toString() ?? '') != null) {
|
||||||
|
ret.setArg(
|
||||||
|
IntegerArgument(name: key, value: int.parse(value.toString())));
|
||||||
|
} else if (value?.toString().toLowerCase() == 'true' ||
|
||||||
|
value?.toString().toLowerCase() == 'false' ||
|
||||||
|
value == null) {
|
||||||
|
if (value != null)
|
||||||
|
ret.setArg(BoolArgument(
|
||||||
|
name: key, value: value.toString().toLowerCase() == 'true'));
|
||||||
|
else
|
||||||
|
ret.setArg(BoolArgument(name: key, value: true));
|
||||||
|
} else if (double.tryParse(value?.toString() ?? '') != null) {
|
||||||
|
ret.setArg(
|
||||||
|
DoubleArgument(name: key, value: double.parse(value.toString())));
|
||||||
|
} else {
|
||||||
|
// Default to StringArgument if no matching type is found
|
||||||
|
ret.setArg(StringArgument(name: key, value: value?.toString() ?? ''));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,9 +2,13 @@ import 'package:libac_dart/argparse/Args.dart';
|
||||||
|
|
||||||
class BoolArgument extends Argument<bool> {
|
class BoolArgument extends Argument<bool> {
|
||||||
bool _value = false;
|
bool _value = false;
|
||||||
BoolArgument({required super.name, super.description = ""}) {
|
bool _hasValue = true;
|
||||||
hasValue = false;
|
|
||||||
_value = true;
|
BoolArgument({required super.name, bool? value = true}) {
|
||||||
|
if (value != null)
|
||||||
|
this._value = value;
|
||||||
|
else
|
||||||
|
_hasValue = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -21,4 +25,9 @@ class BoolArgument extends Argument<bool> {
|
||||||
String toString() {
|
String toString() {
|
||||||
return "BooleanArgument{ ${name}=${_value} }";
|
return "BooleanArgument{ ${name}=${_value} }";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool hasValue() {
|
||||||
|
return _hasValue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,25 @@
|
||||||
import 'package:libac_dart/argparse/Args.dart';
|
import 'package:libac_dart/argparse/Args.dart';
|
||||||
|
|
||||||
class IntegerArgument extends Argument<int> {
|
class IntegerArgument extends Argument<int> {
|
||||||
final int value;
|
int _value = 0;
|
||||||
|
bool _hasValue = false;
|
||||||
|
|
||||||
IntegerArgument(
|
IntegerArgument({required super.name, int? value}) {
|
||||||
{required super.name, required this.value, super.description = ""}) {
|
if (value != null) {
|
||||||
hasValue = value != 0;
|
this._value = value;
|
||||||
|
_hasValue = true;
|
||||||
|
} else
|
||||||
|
_hasValue = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool hasValue() {
|
||||||
|
return _hasValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int getValue() {
|
int getValue() {
|
||||||
return value;
|
return _value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -20,16 +29,25 @@ class IntegerArgument extends Argument<int> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return "IntegerArgument{ ${name}=${value} }";
|
return "IntegerArgument{ ${name}=${_value} }";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class DoubleArgument extends Argument<double> {
|
class DoubleArgument extends Argument<double> {
|
||||||
final double value;
|
double _value = 0.0;
|
||||||
|
bool _hasValue = false;
|
||||||
|
|
||||||
DoubleArgument(
|
DoubleArgument({required super.name, double? value}) {
|
||||||
{required super.name, required this.value, super.description = ""}) {
|
if (value != null) {
|
||||||
hasValue = value != 0.0;
|
_hasValue = true;
|
||||||
|
_value = value;
|
||||||
|
} else
|
||||||
|
_hasValue = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool hasValue() {
|
||||||
|
return _hasValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -39,11 +57,11 @@ class DoubleArgument extends Argument<double> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
double getValue() {
|
double getValue() {
|
||||||
return value;
|
return _value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return "DoubleArgument{ ${name}=${value} }";
|
return "DoubleArgument{ ${name}=${_value} }";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,25 @@
|
||||||
import 'package:libac_dart/argparse/Args.dart';
|
import 'package:libac_dart/argparse/Args.dart';
|
||||||
|
|
||||||
class StringArgument extends Argument<String> {
|
class StringArgument extends Argument<String> {
|
||||||
final String value;
|
String _value = "";
|
||||||
|
bool _hasValue = false;
|
||||||
|
|
||||||
StringArgument(
|
StringArgument({required super.name, String? value}) {
|
||||||
{required super.name, required this.value, super.description}) {
|
if (value != null) {
|
||||||
hasValue = value.isNotEmpty;
|
_hasValue = true;
|
||||||
|
_value = value;
|
||||||
|
} else
|
||||||
|
_hasValue = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool hasValue() {
|
||||||
|
return _hasValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String getValue() {
|
String getValue() {
|
||||||
return value;
|
return _value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -20,6 +29,6 @@ class StringArgument extends Argument<String> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return "StringArgument{ ${name}=${value} }";
|
return "StringArgument{ ${name}=${_value} }";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
class Constants {
|
class Constants {
|
||||||
static const VERSION = "1.3.012225+0304";
|
static const VERSION = "1.4.012225+0357";
|
||||||
static const NBT_REVISION = "1.3.012225+0304";
|
static const NBT_REVISION = "1.3.012225+0304";
|
||||||
|
static const ARGS_REVISION = "1.4.012225+0357";
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
name: libac_dart
|
name: libac_dart
|
||||||
description: "Aria's Creations code library"
|
description: "Aria's Creations code library"
|
||||||
version: 1.3.012225+0304
|
version: 1.4.012225+0357
|
||||||
homepage: "https://zontreck.com"
|
homepage: "https://zontreck.com"
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import 'package:libac_dart/argparse/Args.dart';
|
import 'package:libac_dart/argparse/Args.dart';
|
||||||
import 'package:libac_dart/argparse/Builder.dart';
|
import 'package:libac_dart/argparse/Builder.dart';
|
||||||
import 'package:libac_dart/argparse/CLIHelper.dart';
|
import 'package:libac_dart/argparse/CLIHelper.dart';
|
||||||
|
import 'package:libac_dart/argparse/Parser.dart';
|
||||||
import 'package:libac_dart/argparse/types/Bool.dart';
|
import 'package:libac_dart/argparse/types/Bool.dart';
|
||||||
import 'package:libac_dart/argparse/types/String.dart';
|
import 'package:libac_dart/argparse/types/String.dart';
|
||||||
import 'package:test/expect.dart';
|
import 'package:test/expect.dart';
|
||||||
|
@ -8,33 +9,34 @@ import 'package:test/scaffolding.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
test("Test parsing a argument", () async {
|
test("Test parsing a argument", () async {
|
||||||
List<String> testArgs = ["--test=12", "--enable", "--put=Here"];
|
List<String> testArgs = ["--test", "12", "--enable", "--put=Here"];
|
||||||
|
|
||||||
Arguments defaults = Arguments();
|
Arguments parsed = ArgumentParser.parse(testArgs);
|
||||||
|
|
||||||
Arguments parsed = await CLIHelper.parseArgs(testArgs, defaults);
|
expect(parsed.hasArg("test"), true);
|
||||||
|
expect(parsed.getArg("test")!.getValue() as int, 12);
|
||||||
|
expect(
|
||||||
|
parsed.getArg("test")!.getType(),
|
||||||
|
ArgumentType.INTEGER,
|
||||||
|
);
|
||||||
|
|
||||||
expect(true, parsed.hasArg("test"));
|
expect(parsed.hasArg("enable"), true);
|
||||||
expect(12, parsed.getArg("test")!.getValue() as int);
|
expect(
|
||||||
expect(ArgumentType.INTEGER, parsed.getArg("test")!.getType());
|
parsed.getArg("enable")!.getType(),
|
||||||
|
ArgumentType.BOOL,
|
||||||
|
);
|
||||||
|
|
||||||
expect(true, parsed.hasArg("enable"));
|
expect(parsed.getArg("put")!.getValue() as String, "Here");
|
||||||
expect(ArgumentType.BOOL, parsed.getArg("enable")!.getType());
|
|
||||||
|
|
||||||
expect("Here", parsed.getArg("put")!.getValue() as String);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Test printing Usage", () async {
|
test("Test printing Usage", () async {
|
||||||
Arguments testArgs = ArgumentsBuilder.builder()
|
Arguments testArgs = ArgumentsBuilder.builder()
|
||||||
.withArgument(BoolArgument(
|
.withArgument(BoolArgument(name: "use_legacy"))
|
||||||
name: "use_legacy", description: "Uses legacy settings"))
|
.withArgument(StringArgument(name: "output", value: "./out"))
|
||||||
.withArgument(StringArgument(
|
|
||||||
name: "output",
|
|
||||||
value: "./out",
|
|
||||||
description: "Where to put the output files"))
|
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
String usage = CLIHelper.makeArgCLIHelp(testArgs);
|
String usage = ArgumentHelpers.generateHelpMessage(
|
||||||
|
testArgs.getArgumentsList(), "Testsuite");
|
||||||
print("LibAC_Dart\nTestsuite - 1.0\n\n${usage}");
|
print("LibAC_Dart\nTestsuite - 1.0\n\n${usage}");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue