Overhaul the argument parsing API

This commit is contained in:
zontreck 2025-01-22 03:58:52 -07:00
parent 62d26082c9
commit 82f5c18129
9 changed files with 207 additions and 153 deletions

View file

@ -1,109 +1,51 @@
import 'package:libac_dart/argparse/types/Bool.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 'package:libac_dart/argparse/Args.dart';
import 'Args.dart';
class CLIHelper {
/// Generates usage info
class ArgumentHelpers {
/// Generates a command-line help message for the provided arguments.
///
/// This will create a standard CLI Usage info string that can be directly printed to the console after the program header
static String makeArgCLIHelp(Arguments defaults) {
StringBuilder builder = StringBuilder();
List<String> argNames = defaults.getArgNames();
/// [arguments] The list of arguments to generate help for.
/// [programName] The name of the program.
/// Returns a string containing the help message.
static String generateHelpMessage(
List<Argument<dynamic>> arguments, String programName) {
final StringBuffer helpMessage = StringBuffer();
helpMessage.writeln('Usage: $programName [options]');
for (String name in argNames) {
builder.append("--${name}");
Argument arg = defaults.getArg(name)!;
if (arg.hasValue) {
builder.append("=<...>");
}
builder.append(
"\t\t\t${arg.description} ${(arg.getValue().toString() == "%" || arg.getType() == ArgumentType.BOOL) ? "" : "[Default: ${arg.getValue()}]"}\n");
for (var arg in arguments) {
final description = _getArgumentDescription(arg);
final valueType = arg.getType().toString();
helpMessage.writeln(
' --${arg.name} [${valueType.split('.').last}] $description');
}
return builder.toString();
return helpMessage.toString();
}
/// Parses and returns the arguments list with keeping defaults in mind
static Future<Arguments> parseArgs(
List<String> args, Arguments defaults) async {
Arguments arguments = defaults.clone();
for (int i = 0; i < args.length; i++) {
Argument arg = await _parseArgument(args[i]);
Argument? defArg;
if (defaults.hasArg(arg.name)) defArg = defaults.getArg(arg.name)!;
/// Gets a description for an argument. This can be extended to provide more info.
///
/// [argument] The argument for which to generate a description.
/// Returns a description of the argument.
static String _getArgumentDescription(Argument<dynamic> argument) {
// You can extend this to add more detailed descriptions for specific arguments
return argument.hasValue()
? 'Default value: ${argument.getValue()}'
: 'No default value assigned';
}
if (!arg.hasValue) {
if (defArg != null) {
arg = defArg;
}
}
arguments.setArg(arg);
/// Retrieves the type of an argument in a human-readable format.
///
/// [argument] The argument to get the type for.
/// Returns a string describing the argument type.
static String _getArgumentType(Argument<dynamic> argument) {
switch (argument.getType()) {
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;
}
}