/*** 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 std::optional gCommandDispatcher; struct Command { AuString tag; Parse::ParseObject commandStructure; const CommandCallback_cb &callback; Command(AuString tag, Parse::ParseObject commandStructure, const CommandCallback_cb &callback) : tag(tag), commandStructure(commandStructure), callback(callback) {} }; struct CommandDispatch { Parse::ParsedObject arguments; const CommandCallback_cb &callback; CommandDispatch(const Parse::ParsedObject &arguments, const CommandCallback_cb &callback) : arguments(arguments), callback(callback) {} }; static bool Dispatch(const AuString &string) { Threading::WaitableLockGuard guard(gPendingCommandsMutex.get()); 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; auto consumable = Parse::StringToConsumable(cmdParse, offset); auto status = Parse::Parse(res, cmdEntry.commandStructure, consumable); if (!status) { LogWarn("Couldn't parse command {}", string); return false; } gPendingCommands.push_back(CommandDispatch(res.result, cmdEntry.callback)); return true; } void AddCommand(const AuString &tag, const Parse::ParseObject &commandStructure, const CommandCallback_cb &callback) { Threading::WaitableLockGuard guard(gPendingCommandsMutex.get()); gCommands.insert(std::make_pair(tag, Command(tag, commandStructure, callback))); } bool DispatchCommand(const AuString &string) { return Dispatch(string); } void UpdateDispatcher(std::optional target) { gMutex->Lock(); gPendingCommandsMutex->Lock(); if ((!target.has_value()) && (gCommandDispatcher == target)) { auto commands = std::exchange(gPendingCommands, {}); for (const auto &command : commands) { command.callback(command.arguments); } } gCommandDispatcher = target; gPendingCommandsMutex->Unlock(); gMutex->Unlock(); } 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::DispatchTarget_t{0, 0}) == Async::DispatchTarget_t{0, 0}) { DispatchCommandsFromThis(commands); } else { Async::NewWorkItem(gCommandDispatcher.value(), std::make_shared([&commands]() { DispatchCommandsFromThis(commands); }), true)->Dispatch()->BlockUntilComplete(); } gMutex->Unlock(); } }