/*** Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. File: Commands.cpp Date: 2021-6-12 Author: Reece ***/ #include #include "Commands.hpp" namespace Aurora::Console::Commands { struct Command; struct CommandDispatch; static AuHashMap gCommands; static AuList gLineCallbacks; static AuList gPendingCommands; static auto gMutex = Threading::Primitives::MutexUnique(); static auto gPendingCommandsMutex = Threading::Primitives::MutexUnique(); static AuOptional gCommandDispatcher; struct Command { AuString tag; Parse::ParseObject commandStructure; CommandCallback_cb callback; Command(AuString tag, Parse::ParseObject commandStructure, const CommandCallback_cb &callback) : tag(tag), commandStructure(commandStructure), callback(callback) {} Command(AuString tag, Parse::ParseObject commandStructure, CommandCallback_cb &&callback) : tag(tag), commandStructure(commandStructure), callback(std::move(callback)) {} }; struct CommandDispatch { Parse::ParsedObject arguments; CommandCallback_cb callback; CommandDispatch(const Parse::ParsedObject &arguments, const CommandCallback_cb &callback) : arguments(arguments), callback(callback) {} }; static bool Dispatch(const AuString &string) { AU_LOCK_GUARD(gPendingCommandsMutex); AuString tag; AuString cmdParse; AuMach offset; Parse::ParseResult res; auto commandSplit = string.find(" "); if (commandSplit != AuString::npos) { tag = string.substr(0, commandSplit); cmdParse = string.substr(commandSplit + 1); } else { tag = string; } auto cmdItr = gCommands.find(tag); if (cmdItr == gCommands.end()) { LogWarn("Command {} does not exist", tag); return false; } auto const &cmdEntry = cmdItr->second; offset = 0; Parse::ParseState consumable(Parse::StringToConsumable(cmdParse, offset)); auto status = Parse::Parse(consumable, cmdEntry.commandStructure, res); if (!status) { LogWarn("Couldn't parse command {}", string); return false; } gPendingCommands.push_back(CommandDispatch(res.result, cmdEntry.callback)); return true; } AUKN_SYM void AddCommand(const AuString &tag, const Parse::ParseObject &commandStructure, const CommandCallback_cb &callback) { AU_LOCK_GUARD(gPendingCommandsMutex); gCommands.insert(AuMakePair(tag, Command(tag, commandStructure, callback))); } AUKN_SYM bool DispatchCommand(const AuString &string) { return Dispatch(string); } void UpdateDispatcher(AuOptional target) { AU_LOCK_GUARD(gMutex); AU_LOCK_GUARD(gPendingCommandsMutex); // process commands before async app termination if ((!target.has_value())) { auto commands = std::exchange(gPendingCommands, {}); for (const auto &command : commands) { command.callback(command.arguments); } } gCommandDispatcher = target; } static void DispatchCommandsFromThis(const AuList &commands) { for (const auto &command : commands) { command.callback(command.arguments); } } void PumpCommands() { gMutex->Lock(); gPendingCommandsMutex->Lock(); auto commands = std::exchange(gPendingCommands, {}); gPendingCommandsMutex->Unlock(); if (gCommandDispatcher.value_or(Async::WorkerId_t{}) == Async::WorkerId_t{}) { DispatchCommandsFromThis(commands); } else { Async::NewWorkItem(gCommandDispatcher.value(), AuMakeShared([&commands]() { DispatchCommandsFromThis(commands); }), true)->Dispatch()->BlockUntilComplete(); } gMutex->Unlock(); } }