diff --git a/.gitignore b/.gitignore
index d163863..c6ca6b4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,2 @@
-build/
\ No newline at end of file
+build/
+cli*
\ No newline at end of file
diff --git a/gymboard-cli/build.d b/gymboard-cli/build.d
new file mode 100755
index 0000000..a669d4e
--- /dev/null
+++ b/gymboard-cli/build.d
@@ -0,0 +1,39 @@
+#!/usr/bin/env rdmd
+/**
+ * Run this script with `./build.d` to prepare the latest version of the CLI
+ * for use. It compiles the CLI application, and copies a "cli" executable to
+ * the project's root directory for use.
+ */
+module build;
+
+import std.stdio;
+import std.process;
+import std.file;
+import std.path;
+
+int main() {
+ writeln("Building...");
+ auto result = executeShell("dub build --build=release");
+ if (result.status != 0) {
+ stderr.writefln!"Build failed: %d"(result.status);
+ stderr.writeln(result.output);
+ return result.status;
+ }
+ string finalPath = buildPath("..", "cli");
+ if (exists(finalPath)) std.file.remove(finalPath);
+ version (Posix) {
+ string sourceExecutable = "gymboard-cli";
+ }
+ version (Windows) {
+ string sourceExecutable = "gymboard-cli.exe";
+ }
+ std.file.copy(sourceExecutable, finalPath);
+ version (Posix) {
+ result = executeShell("chmod +x " ~ finalPath);
+ if (result.status != 0) {
+ stderr.writefln!"Failed to enable executable permission: %d"(result.status);
+ return result.status;
+ }
+ }
+ return 0;
+}
diff --git a/gymboard-cli/source/app.d b/gymboard-cli/source/app.d
index 7396248..3c9da4f 100644
--- a/gymboard-cli/source/app.d
+++ b/gymboard-cli/source/app.d
@@ -1,6 +1,7 @@
import std.stdio;
import cli;
+import command;
import services;
import consolecolors;
@@ -9,11 +10,12 @@ void main() {
ServiceManager serviceManager = new ServiceManager();
CliHandler cliHandler = new CliHandler();
cliHandler.register("service", new ServiceCommand(serviceManager));
- cwriteln("Gymboard CLI: Type help for more information. Type exit to exit the CLI.");
- while (!cliHandler.shouldExit) {
+ cwriteln("\nGymboard CLI: Command-line interface for managing Gymboard services.");
+ cwriteln(" Type help for more information.\n Type exit to exit the CLI.\n");
+ while (!cliHandler.isExitRequested) {
cwrite("> ".blue);
cliHandler.readAndHandleCommand();
}
serviceManager.stopAll();
- cwriteln("Goodbye!".green);
+ cwriteln("Goodbye!".blue);
}
diff --git a/gymboard-cli/source/cli.d b/gymboard-cli/source/cli.d
index 4da07fc..6903ebc 100644
--- a/gymboard-cli/source/cli.d
+++ b/gymboard-cli/source/cli.d
@@ -7,45 +7,7 @@ import std.typecons;
import consolecolors;
-interface CliCommand {
- void handle(string[] args);
-}
-
-class CliHandler {
- private CliCommand[string] commands;
- public bool shouldExit = false;
-
- public void register(string name, CliCommand command) {
- this.commands[name] = command;
- }
-
- public void readAndHandleCommand() {
- string[] commandAndArgs = readln().strip().split!isWhite();
- if (commandAndArgs.length == 0) return;
- string command = commandAndArgs[0].toLower();
- if (command == "help") {
- showHelp();
- } else if (command == "exit") {
- shouldExit = true;
- } else if (command in commands) {
- commands[command].handle(commandAndArgs.length > 1 ? commandAndArgs[1 .. $] : []);
- } else {
- cwritefln("Unknown command: %s".red, command.orange);
- }
- }
-}
-
-void showHelp() {
- writeln(q"HELP
-Gymboard CLI: A tool for streamlining development.
-
-Commands:
-
-help Shows this message.
-exit Exits the CLI, stopping any running services.
-HELP");
-}
-
+import command.base;
import services;
class ServiceCommand : CliCommand {
@@ -86,6 +48,14 @@ class ServiceCommand : CliCommand {
}
}
+ string name() const {
+ return "Service";
+ }
+
+ string description() const {
+ return "bleh";
+ }
+
/**
* Validates that a service command contains as its second argument a valid
* service name.
diff --git a/gymboard-cli/source/command/base.d b/gymboard-cli/source/command/base.d
new file mode 100644
index 0000000..fa38304
--- /dev/null
+++ b/gymboard-cli/source/command/base.d
@@ -0,0 +1,102 @@
+module command.base;
+
+import consolecolors;
+import std.stdio;
+import std.string;
+import std.uni;
+
+interface CliCommand {
+ void handle(string[] args);
+ string name() const;
+ string description() const;
+}
+
+class CliHandler {
+ private CliCommand[string] commands;
+ private bool exitRequested = false;
+
+ this() {
+ commands["help"] = new HelpCommand(this);
+ commands["exit"] = new ExitCommand(this);
+ }
+
+ void register(string name, CliCommand command) {
+ this.commands[name.strip().toLower()] = command;
+ }
+
+ void readAndHandleCommand() {
+ string[] commandAndArgs = readln().strip().split!isWhite();
+ if (commandAndArgs.length == 0) return;
+ string command = commandAndArgs[0].toLower();
+ if (command in commands) {
+ commands[command].handle(commandAndArgs.length > 1 ? commandAndArgs[1 .. $] : []);
+ } else {
+ cwritefln("Unknown command: %s".red, command.orange);
+ }
+ }
+
+ void setExitRequested() {
+ this.exitRequested = true;
+ }
+
+ bool isExitRequested() const {
+ return this.exitRequested;
+ }
+
+ CliCommand[string] getCommands() {
+ return this.commands;
+ }
+}
+
+class HelpCommand : CliCommand {
+ private CliHandler handler;
+
+ this(CliHandler handler) {
+ this.handler = handler;
+ }
+
+ void handle(string[] args) {
+ import std.algorithm;
+
+ cwriteln("Gymboard CLI Help: Information about how to use this program.");
+
+ string[] commandNames = this.handler.getCommands().keys;
+ sort(commandNames);
+ uint longestCommandNameLength = commandNames.map!(n => cast(uint) n.length).maxElement;
+
+ foreach (name; commandNames) {
+ CliCommand command = this.handler.getCommands()[name];
+
+ string formattedName = cyan(leftJustify(name, longestCommandNameLength + 1, ' '));
+ cwriteln(formattedName, command.description().grey);
+ }
+ }
+
+ string name() const {
+ return "help";
+ }
+
+ string description() const {
+ return "Shows help information.";
+ }
+}
+
+class ExitCommand : CliCommand {
+ private CliHandler handler;
+
+ this(CliHandler handler) {
+ this.handler = handler;
+ }
+
+ void handle(string[] args) {
+ this.handler.setExitRequested();
+ }
+
+ string name() const {
+ return "exit";
+ }
+
+ string description() const {
+ return "Exits the CLI, gracefully stopping any services or running jobs.";
+ }
+}
diff --git a/gymboard-cli/source/command/package.d b/gymboard-cli/source/command/package.d
new file mode 100644
index 0000000..ef20fe8
--- /dev/null
+++ b/gymboard-cli/source/command/package.d
@@ -0,0 +1,3 @@
+module command;
+
+public import command.base;