added cli arg parser

This commit is contained in:
Niklas Kapelle 2024-05-08 16:07:11 +02:00
parent 1108eab403
commit 5f42941d76
Signed by: niklas
GPG Key ID: 4EB651B36D841D16
2 changed files with 208 additions and 0 deletions

27
src/lib/args/ArgType.hx Normal file
View File

@ -0,0 +1,27 @@
package lib.args;
enum ArgType {
/** Any Int **/
Int(name:String);
/** Any Float **/
Float(name:String);
/** Any String **/
String(name:String);
/** A side like front, back, top, ... **/
Side(name:String);
/** Any peripheral address that exists **/
Addr(name:String);
/** Address of peripheral with given type **/
Peripheral(name:String, type:String);
/** An optional argument **/
Optional(type:ArgType);
/** The rest of the arguments as a string array **/
Rest(name:String);
}

181
src/lib/args/CLIArgs.hx Normal file
View File

@ -0,0 +1,181 @@
package lib.args;
import kernel.peripherals.Peripherals.Peripheral;
import haxe.ds.ReadOnlyArray;
import haxe.ds.StringMap;
class CLIArgs {
private final argTypes:Array<ArgType>;
private final parsedArgs:StringMap<Dynamic> = new StringMap();
private var rest:Array<String>;
private var error:Null<String>;
private var errorPos:Int = 0;
public function new(args:Array<ArgType>) {
this.argTypes = args;
}
public function parse(args:ReadOnlyArray<String>):Bool {
for (i => type in this.argTypes) {
if (args.length < (i + 1)) {
if (type.match(Optional(_))) {
return true;
} else {
this.errorPos = i;
return false;
}
}
switch type {
case Rest(_):
this.rest = args.slice(i);
default:
if (!parseArg(args[i], type)) {
this.errorPos = i;
return false;
}
}
}
return true;
}
public inline function getError():String {
return 'Error at pos ${this.errorPos}: ${this.error}';
}
/**
Returns synopsis.
**/
public static function getSynopsis(argTypes:Array<ArgType>):String {
var synopsis = "";
for (arg in argTypes) {
var name = getName(arg);
synopsis += switch (arg) {
case Optional(_): '[$name]';
case Rest(_): '[$name...]'; // TODO: is rest always optional?
default: '<$name>';
}
}
return synopsis;
}
private static function getName(arg:ArgType):String {
return switch (arg) {
case Int(name): name;
case Float(name): name;
case String(name): name;
case Side(name): name;
case Addr(name): name;
case Peripheral(name, type): name;
case Rest(name): name;
case Optional(type): getName(type);
};
}
private function parseArg(arg:String, type:ArgType):Bool {
switch type {
case Int(name):
var parsed = Std.parseInt(arg);
if (parsed == null) {
this.error = 'Need to be an integer';
return false;
}
this.parsedArgs.set(name, parsed);
case Float(name):
var parsed = Std.parseFloat(arg);
if (parsed == null) {
return false;
}
this.parsedArgs.set(name, parsed);
case String(name):
this.parsedArgs.set(name, arg);
case Side(name):
if (!["front", "back", "top", "right", "left", "bottom"].contains(arg)) {
this.error = "must be a side";
return false;
}
this.parsedArgs.set(name, arg);
case Addr(name):
if (!Peripheral.isPresent(arg)) {
this.error = "address not present";
return false;
}
this.parsedArgs.set(name, arg);
case Peripheral(name, type):
if (!Peripheral.getTypes(arg).contains(type)) {
this.error = "address has invalid type";
return false;
}
this.parsedArgs.set(name, arg);
case Optional(innerType):
return parseArg(arg, innerType);
case Rest(name):
return true; // Should never happen
}
return true;
}
/**
Get the arg with `name`.
When in debug mode will throw execption on wrong type.
Returns null when not existing.
**/
public function getInt(name:String):Null<Int> {
var v = this.parsedArgs.get(name);
if (v == null)
return null;
#if debug
return cast(v, Int);
#else
return cast v;
#end
}
/**
Get the arg with `name`.
When in debug mode will throw execption on wrong type.
Returns null when not existing.
**/
public function getFloat(name:String):Null<Float> {
var v = this.parsedArgs.get(name);
if (v == null)
return null;
#if debug
return cast(v, Float);
#else
return cast v;
#end
}
/**
Get the arg with `name`.
When in debug mode will throw execption on wrong type.
Returns null when not existing.
**/
public function getString(name:String):Null<String> {
var v = this.parsedArgs.get(name);
if (v == null)
return null;
#if debug
return cast(v, String);
#else
return cast v;
#end
}
/**
Returns true if arg is present. Only makes sense on Optional args.
**/
public function hasArg(name:String):Bool {
return this.parsedArgs.exists(name);
}
public function getRest():Array<String> {
return this.rest;
}
}