diff --git a/src/lib/args/ArgType.hx b/src/lib/args/ArgType.hx new file mode 100644 index 0000000..312b3d7 --- /dev/null +++ b/src/lib/args/ArgType.hx @@ -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); +} diff --git a/src/lib/args/CLIArgs.hx b/src/lib/args/CLIArgs.hx new file mode 100644 index 0000000..9446ea8 --- /dev/null +++ b/src/lib/args/CLIArgs.hx @@ -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; + private final parsedArgs:StringMap = new StringMap(); + private var rest:Array; + + private var error:Null; + private var errorPos:Int = 0; + + public function new(args:Array) { + this.argTypes = args; + } + + public function parse(args:ReadOnlyArray):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):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 { + 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 { + 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 { + 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 { + return this.rest; + } +}