put new cli parser to use in CLIAppBase

This commit is contained in:
Niklas Kapelle 2024-05-08 16:08:35 +02:00
parent 5f42941d76
commit f95c262c57
Signed by: niklas
GPG Key ID: 4EB651B36D841D16
11 changed files with 114 additions and 219 deletions

View File

@ -1,5 +1,6 @@
package bin; package bin;
import kernel.peripherals.Drive;
import lib.CLIAppBase; import lib.CLIAppBase;
import kernel.peripherals.Peripherals.Peripheral; import kernel.peripherals.Peripherals.Peripheral;
@ -30,37 +31,17 @@ class Disk extends CLIAppBase {
}); });
registerSyncSubcommand("play", (args) -> { registerSyncSubcommand("play", (args) -> {
if (args.length < 1) { return audioDiskPlayPause(args.getString("addr"), true);
handle.writeLine("Missing drive address"); }, [Peripheral("addr", Drive.TYPE_NAME)]);
return false;
}
return audioDiskPlayPause(args[0], true);
}, "<drive>");
registerSyncSubcommand("stop", (args) -> { registerSyncSubcommand("stop", (args) -> {
if (args.length < 1) { return audioDiskPlayPause(args.getString("addr"), false);
handle.writeLine("Missing drive address"); }, [Peripheral("addr", Drive.TYPE_NAME)]);
return false;
}
return audioDiskPlayPause(args[0], false);
}, "<drive>");
registerSyncSubcommand("eject", (args) -> { registerSyncSubcommand("eject", (args) -> {
if (args.length < 1) { var driveAddr = args.getString("addr");
handle.writeLine("Missing drive address");
return false;
}
var driveAddr = args[0];
var drive = Peripheral.getDrive(driveAddr); var drive = Peripheral.getDrive(driveAddr);
if (drive == null) {
handle.writeLine("Drive not found: " + driveAddr);
return false;
}
if (!drive.isDiskPresent()) { if (!drive.isDiskPresent()) {
handle.writeLine("No disk in drive: " + driveAddr); handle.writeLine("No disk in drive: " + driveAddr);
return false; return false;
@ -68,21 +49,12 @@ class Disk extends CLIAppBase {
drive.ejectDisk(); drive.ejectDisk();
return true; return true;
}, "<drive>"); }, [Peripheral("addr", Drive.TYPE_NAME)]);
registerSyncSubcommand("lable", (args) -> { registerSyncSubcommand("label", (args) -> {
if (args.length < 1) { var driveAddr = args.getString("addr");
handle.writeLine("Missing drive address");
return false;
}
var driveAddr = args[0];
var drive = Peripheral.getDrive(driveAddr); var drive = Peripheral.getDrive(driveAddr);
var label:String = args[1]; var label:Null<String> = args.getString("label");
if (drive == null) {
handle.writeLine("Drive not found: " + driveAddr);
}
if (!drive.isDiskPresent()) { if (!drive.isDiskPresent()) {
handle.writeLine("No disk in drive: " + driveAddr); handle.writeLine("No disk in drive: " + driveAddr);
@ -99,7 +71,7 @@ class Disk extends CLIAppBase {
} }
return true; return true;
}, "<drive> [label]"); }, [Peripheral("addr", Drive.TYPE_NAME), Optional(String("label"))]);
} }
private function audioDiskPlayPause(driveAddr:String, play:Bool):Bool { private function audioDiskPlayPause(driveAddr:String, play:Bool):Bool {

View File

@ -11,16 +11,16 @@ using tink.CoreApi;
class GPS extends CLIAppBase { class GPS extends CLIAppBase {
public function new() { public function new() {
registerSyncSubcommand("set", (args) -> { registerSyncSubcommand("set", (args) -> {
var x:Float = Std.parseFloat(args[0]); var x:Float = args.getFloat("x");
var y:Float = Std.parseFloat(args[1]); var y:Float = args.getFloat("y");
var z:Float = Std.parseFloat(args[2]); var z:Float = args.getFloat("z");
var pos:Pos3 = new Vec3<Float>(x, y, z); var pos:Pos3 = new Vec3<Float>(x, y, z);
kernel.gps.GPS.setManualPosition(pos); kernel.gps.GPS.setManualPosition(pos);
return true; return true;
}, "<x> <y> <z>"); }, [Float("x"), Float("y"), Float("z")]);
registerSyncSubcommand("status", (args) -> { registerSyncSubcommand("status", (args) -> {
var pos = kernel.gps.GPS.getPosition(); var pos = kernel.gps.GPS.getPosition();

View File

@ -7,12 +7,7 @@ import lib.CLIAppBase;
class KSettings extends CLIAppBase { class KSettings extends CLIAppBase {
public function new() { public function new() {
registerSyncSubcommand("get", (args) -> { registerSyncSubcommand("get", (args) -> {
var key = args[0]; var key = args.getString("key");
if (key == null) {
handle.writeLine("Key not specified");
return false;
}
var value = switch (key) { var value = switch (key) {
case "hostname": case "hostname":
@ -30,17 +25,17 @@ class KSettings extends CLIAppBase {
handle.writeLine(value); handle.writeLine(value);
return true; return true;
}, " <key>"); }, [String("key")]);
registerSyncSubcommand("set", (args) -> { registerSyncSubcommand("set", (args) -> {
var key = args[0]; var key = args.getString("key");
if (key == null) { if (key == null) {
handle.writeLine("Key not specified"); handle.writeLine("Key not specified");
return false; return false;
} }
var value = args[1]; var value = args.getString("value");
if (value == null) { if (value == null) {
handle.writeLine("Value not specified"); handle.writeLine("Value not specified");
@ -57,7 +52,7 @@ class KSettings extends CLIAppBase {
return false; return false;
} }
return true; return true;
}, " <key> <value>"); }, [String("key"), String("value")]);
registerSyncSubcommand("list", (args) -> { registerSyncSubcommand("list", (args) -> {
handle.writeLine("hostname"); handle.writeLine("hostname");

View File

@ -40,16 +40,7 @@ class Net extends CLIAppBase {
}); });
registerAsyncSubcommand("ping", (args) -> { registerAsyncSubcommand("ping", (args) -> {
if (args.length < 1) { var toID:Int = args.getInt("id");
return Future.sync(false);
}
var toID:Null<Int> = Std.parseInt(args[0]);
if (toID == null) {
handle.write("Invalid ID");
return Future.sync(false);
}
return kernel.net.Net.ping(toID).map(result -> { return kernel.net.Net.ping(toID).map(result -> {
switch (result) { switch (result) {
@ -61,6 +52,6 @@ class Net extends CLIAppBase {
return true; return true;
}); });
}, "<id>"); }, [Int("id")]);
} }
} }

View File

@ -7,15 +7,8 @@ import lib.CLIAppBase;
class Perf extends CLIAppBase { class Perf extends CLIAppBase {
public function new() { public function new() {
registerSyncSubcommand("inspect", (args) -> { registerSyncSubcommand("inspect", (args) -> {
if (args.length < 1) var addr = args.getString("addr");
return false; var result = Peripheral.inspect(addr);
var result = Peripheral.inspect(args[0]);
if (result == null) {
handle.writeLine("No peripheral found on side " + args[0]);
return true;
}
handle.writeLine("Types:"); handle.writeLine("Types:");
for (type in result.types) { for (type in result.types) {
@ -28,7 +21,7 @@ class Perf extends CLIAppBase {
} }
return true; return true;
}, "<side>"); }, [Addr("addr")]);
registerSyncSubcommand("list", (args) -> { registerSyncSubcommand("list", (args) -> {
for (addr in Peripheral.getAllAddresses()) { for (addr in Peripheral.getAllAddresses()) {

View File

@ -9,19 +9,19 @@ using tink.CoreApi;
class Redstone extends CLIAppBase { class Redstone extends CLIAppBase {
public function new() { public function new() {
registerSyncSubcommand("on", (args) -> { registerSyncSubcommand("on", (args) -> {
Peripheral.getRedstone(args[0]).setOutput(true); Peripheral.getRedstone(args.getString("side")).setOutput(true);
return true; return true;
}, "<side>"); }, [Side("side")]);
registerSyncSubcommand("off", (args) -> { registerSyncSubcommand("off", (args) -> {
Peripheral.getRedstone(args[0]).setOutput(false); Peripheral.getRedstone(args.getString("side")).setOutput(false);
return true; return true;
}, "<side>"); }, [Side("side")]);
registerSyncSubcommand("get", (args) -> { registerSyncSubcommand("get", (args) -> {
var value = Peripheral.getRedstone(args[0]).getAnalogInput(); var value = Peripheral.getRedstone(args.getString("side")).getAnalogInput();
handle.write("Analog input: " + value); handle.write("Analog input: " + value);
return true; return true;
}, "<side>"); }, [Side("side")]);
} }
} }

View File

@ -9,50 +9,28 @@ using tink.CoreApi;
class Service extends CLIAppBase { class Service extends CLIAppBase {
public function new() { public function new() {
registerSyncSubcommand("start", (args) -> { registerSyncSubcommand("start", (args) -> {
if (args.length < 1) { var result = ServiceManager.start(args.getString("name"));
return false;
}
var name = args[0];
var result = ServiceManager.start(name);
return handleResult(result); return handleResult(result);
}, "<name>"); }, [String("name")]);
registerSyncSubcommand("stop", (args) -> { registerSyncSubcommand("stop", (args) -> {
if (args.length < 1) { var result = ServiceManager.stop(args.getString("name"));
return false;
}
var name = args[0];
var result = ServiceManager.stop(name);
return handleResult(result); return handleResult(result);
}, "<name>"); }, [String("name")]);
registerSyncSubcommand("register", (args) -> { registerSyncSubcommand("register", (args) -> {
if (args.length < 2) { var name = args.getString("name");
return false; var binName = args.getString("binary");
} var rest = args.getRest();
var name = args[0];
var binName = args[1];
var rest = args.slice(2);
var result = ServiceManager.register(name, binName, rest); var result = ServiceManager.register(name, binName, rest);
return handleResult(result); return handleResult(result);
}, "<name> <binary> [args...]"); }, [String("name"), String("binary"), Rest("args")]);
registerSyncSubcommand("unregister", (args) -> { registerSyncSubcommand("unregister", (args) -> {
if (args.length < 2) { var result = ServiceManager.unregister(args.getString("name"));
return false;
}
var name = args[0];
var result = ServiceManager.unregister(name);
return handleResult(result); return handleResult(result);
}, "<name>"); }, [String("name")]);
registerSyncSubcommand("list", (args) -> { registerSyncSubcommand("list", (args) -> {
var list = ServiceManager.listRunning(); var list = ServiceManager.listRunning();
@ -65,13 +43,9 @@ class Service extends CLIAppBase {
}); });
registerSyncSubcommand("enable", (args) -> { registerSyncSubcommand("enable", (args) -> {
if (args.length < 1) { ServiceManager.enable(args.getString("name"));
return false;
}
ServiceManager.enable(args[0]);
return true; return true;
}, "<name>"); }, [String("name")]);
} }
private function handleResult(res:Outcome<Noise, String>):Bool { private function handleResult(res:Outcome<Noise, String>):Bool {

View File

@ -9,33 +9,16 @@ using tink.CoreApi;
class Speaker extends CLIAppBase { class Speaker extends CLIAppBase {
public function new() { public function new() {
registerSyncSubcommand("note", (args) -> { registerSyncSubcommand("note", (args) -> {
if (args.length < 2) { var sp = Peripheral.getSpeaker(args.getString("addr"));
handle.writeLine("Not enough args");
return false;
}
var sp = Peripheral.getSpeaker(args[0]); var note = args.getString("note");
var note = args[1];
var r; var r;
if (args.length > 3) { if (args.hasArg("pitch")) {
var volume = Std.parseFloat(args[2]); if (args.hasArg("volume")) {
if (volume == null) { r = sp.playNote(note, args.getFloat("volume"), args.getInt("pitch"));
handle.writeLine("Failed to parse volume");
return false;
}
if (args.length > 4) {
var pitch = Std.parseInt(args[3]);
if (pitch == null) {
handle.writeLine("Failed to parse pitch");
return false;
}
r = sp.playNote(note, volume, pitch);
} else { } else {
r = sp.playNote(note, volume); r = sp.playNote(note, args.getFloat("volume"));
} }
} else { } else {
r = sp.playNote(note); r = sp.playNote(note);
@ -48,36 +31,24 @@ class Speaker extends CLIAppBase {
case Success(_): case Success(_):
return true; return true;
} }
}, "<addr> <note> [pitch] [volume]"); }, [
Peripheral("addr", kernel.peripherals.Speaker.TYPE_NAME),
String("note"),
Optional(Int("pitch")),
Optional(Float("volume"))
]);
registerSyncSubcommand("sound", (args) -> { registerSyncSubcommand("sound", (args) -> {
if (args.length < 2) { var sp = Peripheral.getSpeaker(args.getString("addr"));
handle.writeLine("Not enough args");
return false;
}
var sp = Peripheral.getSpeaker(args[0]); var sound = args.getString("sound");
var sound = args[1];
var r; var r;
if (args.length > 3) { if (args.hasArg("volume")) {
var volume = Std.parseFloat(args[2]); if (args.hasArg("pitch")) {
if (volume == null) { r = sp.playSound(sound, args.getFloat("volume"), args.getFloat("pitch"));
handle.writeLine("Failed to parse volume");
return false;
}
if (args.length > 4) {
var pitch = Std.parseFloat(args[3]);
if (pitch == null) {
handle.writeLine("Failed to parse pitch");
return false;
}
r = sp.playSound(sound, volume, pitch);
} else { } else {
r = sp.playSound(sound, volume); r = sp.playSound(sound, args.getFloat("volume"));
} }
} else { } else {
r = sp.playSound(sound); r = sp.playSound(sound);
@ -90,6 +61,11 @@ class Speaker extends CLIAppBase {
case Success(_): case Success(_):
return true; return true;
} }
}, "<addr> <sound> [volume] [pitch]"); }, [
Peripheral("addr", kernel.peripherals.Speaker.TYPE_NAME),
String("sound"),
Optional(Float("pitch")),
Optional(Float("volume"))
]);
} }
} }

View File

@ -11,28 +11,28 @@ using tink.CoreApi;
class TurtleCtl extends CLIAppBase { class TurtleCtl extends CLIAppBase {
public function new() { public function new() {
registerAsyncSubcommand("f", (args) -> { registerAsyncSubcommand("f", (args) -> {
return asynPerform(Turtle.forward, parseTimes(args)); return asynPerform(Turtle.forward, args.getInt("times"));
}, "<times>"); }, [Int("times")]);
registerAsyncSubcommand("b", (args) -> { registerAsyncSubcommand("b", (args) -> {
return asynPerform(Turtle.back, parseTimes(args)); return asynPerform(Turtle.back, args.getInt("times"));
}, "<times>"); }, [Int("times")]);
registerAsyncSubcommand("l", (args) -> { registerAsyncSubcommand("l", (args) -> {
return asynPerform(Turtle.turnLeft, parseTimes(args)); return asynPerform(Turtle.turnLeft, args.getInt("times"));
}, "<times>"); }, [Int("times")]);
registerAsyncSubcommand("r", (args) -> { registerAsyncSubcommand("r", (args) -> {
return asynPerform(Turtle.turnRight, parseTimes(args)); return asynPerform(Turtle.turnRight, args.getInt("times"));
}, "<times>"); }, [Int("times")]);
registerAsyncSubcommand("u", (args) -> { registerAsyncSubcommand("u", (args) -> {
return asynPerform(Turtle.up, parseTimes(args)); return asynPerform(Turtle.up, args.getInt("times"));
}, "<times>"); }, [Int("times")]);
registerAsyncSubcommand("d", (args) -> { registerAsyncSubcommand("d", (args) -> {
return asynPerform(Turtle.down, parseTimes(args)); return asynPerform(Turtle.down, args.getInt("times"));
}, "<times>"); }, [Int("times")]);
registerAsyncSubcommand("defrag", (args) -> { registerAsyncSubcommand("defrag", (args) -> {
return asynPerform(() -> { return asynPerform(() -> {
@ -63,8 +63,9 @@ class TurtleCtl extends CLIAppBase {
registerAsyncSubcommand("refuel", (args) -> { registerAsyncSubcommand("refuel", (args) -> {
var refuelTo = Turtle.getFuelLimit(); var refuelTo = Turtle.getFuelLimit();
if (args.length > 0) { var arg = args.getString("to");
var split = args[0].split("%"); if (arg != null) {
var split = arg.split("%");
if (split.length > 1) { if (split.length > 1) {
// Is percentage // Is percentage
@ -78,7 +79,7 @@ class TurtleCtl extends CLIAppBase {
refuelTo = Math.round(refuelTo * (parsed / 100)); refuelTo = Math.round(refuelTo * (parsed / 100));
} else { } else {
// Is absolute // Is absolute
var parsed = Std.parseInt(args[0]); var parsed = Std.parseInt(arg);
if (parsed == null) { if (parsed == null) {
handle.writeLine("Failed to parse ammount"); handle.writeLine("Failed to parse ammount");
@ -94,7 +95,7 @@ class TurtleCtl extends CLIAppBase {
return Success(null); return Success(null);
}, 1); }, 1);
}, "<to|to%>"); }, [Optional(String("to"))]);
} }
private function asynPerform(op:Void->Outcome<Noise, String>, times:Int):Future<Bool> { private function asynPerform(op:Void->Outcome<Noise, String>, times:Int):Future<Bool> {

View File

@ -8,13 +8,9 @@ using tink.CoreApi;
class NameSystemCLI extends CLIAppBase { class NameSystemCLI extends CLIAppBase {
public function new() { public function new() {
registerAsyncSubcommand("get", (args) -> { registerAsyncSubcommand("get", (args) -> {
if (args.length < 1) {
return Future.sync(false);
}
var ns = NameSystemRPC.getDefault(); var ns = NameSystemRPC.getDefault();
return ns.getIDRecord(args[0]).map((r) -> { return ns.getIDRecord(args.getString("name")).map((r) -> {
switch r { switch r {
case Success(data): case Success(data):
handle.writeLine('Resolved: $data'); handle.writeLine('Resolved: $data');
@ -24,23 +20,13 @@ class NameSystemCLI extends CLIAppBase {
return true; return true;
}); });
}, "<name>"); }, [String("name")]);
registerAsyncSubcommand("register", (args) -> { registerAsyncSubcommand("register", (args) -> {
if (args.length < 2) { var id = args.getInt("id");
return Future.sync(false);
}
var id = Std.parseInt(args[1]);
if (id == null) {
handle.writeLine("Not a valid id");
return Future.sync(false);
}
var ns = NameSystemRPC.getDefault(); var ns = NameSystemRPC.getDefault();
return ns.setIDRecord(args[0], id).map((r) -> { return ns.setIDRecord(args.getString("name"), id).map((r) -> {
switch r { switch r {
case Success(_): case Success(_):
handle.writeLine('Set'); handle.writeLine('Set');
@ -50,7 +36,7 @@ class NameSystemCLI extends CLIAppBase {
return true; return true;
}); });
}, "<name> <id>"); }, [String("name"), Int("id")]);
registerAsyncSubcommand("list", (args) -> { registerAsyncSubcommand("list", (args) -> {
var ns = NameSystemRPC.getDefault(); var ns = NameSystemRPC.getDefault();
@ -68,13 +54,9 @@ class NameSystemCLI extends CLIAppBase {
}); });
registerAsyncSubcommand("unregister", (args) -> { registerAsyncSubcommand("unregister", (args) -> {
if (args.length < 1) {
return Future.sync(false);
}
var ns = NameSystemRPC.getDefault(); var ns = NameSystemRPC.getDefault();
return ns.removeIDRecord(args[0]).map((r) -> { return ns.removeIDRecord(args.getString("name")).map((r) -> {
switch r { switch r {
case Success(_): case Success(_):
handle.writeLine('Unregisterd'); handle.writeLine('Unregisterd');
@ -84,6 +66,6 @@ class NameSystemCLI extends CLIAppBase {
return true; return true;
}); });
}, "<name>"); }, [String("name")]);
} }
} }

View File

@ -1,5 +1,7 @@
package lib; package lib;
import lib.args.CLIArgs;
import lib.args.ArgType;
import kernel.ps.IProcess; import kernel.ps.IProcess;
import kernel.ps.ProcessHandle; import kernel.ps.ProcessHandle;
@ -8,9 +10,9 @@ using tink.CoreApi;
abstract class CLIAppBase implements IProcess { abstract class CLIAppBase implements IProcess {
private var handle:ProcessHandle; private var handle:ProcessHandle;
private final _subcommandsSync:Map<String, (Array<String>) -> Bool> = []; private final _subcommandsSync:Map<String, (CLIArgs) -> Bool> = [];
private final _subcommandsAsync:Map<String, (Array<String>) -> Future<Bool>> = []; private final _subcommandsAsync:Map<String, (CLIArgs) -> Future<Bool>> = [];
private final _subcommandsSynopsis:Array<String> = []; private final _subcommandsArgs:Map<String, Array<ArgType>> = [];
public final function run(handle:ProcessHandle) { public final function run(handle:ProcessHandle) {
this.handle = handle; this.handle = handle;
@ -26,10 +28,19 @@ abstract class CLIAppBase implements IProcess {
var args = handle.args.slice(1); var args = handle.args.slice(1);
if (_subcommandsSync.exists(subcommand)) { if (_subcommandsSync.exists(subcommand)) {
var result = _subcommandsSync[subcommand](args); var argParser = new CLIArgs(_subcommandsArgs.get(subcommand));
return handle.close(result); if (!argParser.parse(args)) {
handle.writeLine(argParser.getError());
return handle.close(false);
}
return handle.close(_subcommandsSync[subcommand](argParser));
} else if (_subcommandsAsync.exists(subcommand)) { } else if (_subcommandsAsync.exists(subcommand)) {
_subcommandsAsync[subcommand](args).handle(handle.close); var argParser = new CLIArgs(_subcommandsArgs.get(subcommand));
if (!argParser.parse(args)) {
handle.writeLine(argParser.getError());
return handle.close(false);
}
_subcommandsAsync[subcommand](argParser).handle(handle.close);
} else { } else {
handle.writeLine("Unknown subcommand: " + subcommand); handle.writeLine("Unknown subcommand: " + subcommand);
printHelp(); printHelp();
@ -37,21 +48,21 @@ abstract class CLIAppBase implements IProcess {
} }
} }
private function registerSyncSubcommand(command:String, callback:(Array<String>) -> Bool, synopsis:String = null) { private function registerSyncSubcommand(command:String, callback:(CLIArgs) -> Bool, args:Array<ArgType> = null) {
_subcommandsSync.set(command, callback); _subcommandsSync.set(command, callback);
_subcommandsSynopsis.push(command + " " + (synopsis ?? "")); _subcommandsArgs.set(command, args ?? []);
} }
private function registerAsyncSubcommand(command:String, callback:(Array<String>) -> Future<Bool>, synopsis:String = null) { private function registerAsyncSubcommand(command:String, callback:(CLIArgs) -> Future<Bool>, args:Array<ArgType> = null) {
_subcommandsAsync.set(command, callback); _subcommandsAsync.set(command, callback);
_subcommandsSynopsis.push(command + " " + (synopsis ?? "")); _subcommandsArgs.set(command, args ?? []);
} }
private function printHelp() { private function printHelp() {
handle.writeLine("Usage: <subcommand> [args]"); handle.writeLine("Usage: <subcommand> [args]");
handle.writeLine("Subcommands:"); handle.writeLine("Subcommands:");
for (subcommand in _subcommandsSynopsis) { for (k => v in this._subcommandsArgs) {
handle.writeLine(" " + subcommand); handle.writeLine(' $k ${CLIArgs.getSynopsis(v)}');
} }
} }
} }