Skip to content

Commit

Permalink
Refactor hooks to use a dedicated handler in client
Browse files Browse the repository at this point in the history
This will also enable calling a hook without available configuration, closing #39
  • Loading branch information
MartinHelmut committed Aug 7, 2021
1 parent 0817a3c commit 6fefda5
Show file tree
Hide file tree
Showing 13 changed files with 112 additions and 106 deletions.
1 change: 1 addition & 0 deletions src/client/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ include(${PROJECT_SOURCE_DIR}/cmake/StaticAnalyzers.cmake)

add_executable(${NAME}
Client/Client.cpp Client/Application.cpp Client/Application.hpp
Client/Hooks/Handler.cpp Client/Hooks/Handler.hpp
Client/Hooks/Help.cpp Client/Hooks/Help.hpp
Client/Hooks/Version.cpp Client/Hooks/Version.hpp)

Expand Down
63 changes: 28 additions & 35 deletions src/client/Client/Application.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,65 +3,58 @@
#include <fmt/color.h>
#include <fmt/format.h>

#include "Hooks/Handler.hpp"
#include "Hooks/Help.hpp"
#include "Hooks/Version.hpp"

namespace Litr {

Application::Application() {
// @todo: Error handling here is horrible. In need of refactoring!
ExitStatus Application::Run(int argc, char* argv[]) {
LITR_PROFILE_FUNCTION();

LITR_TRACE("Hello, Litr!\n");
const std::string source{SourceFromArguments(argc, argv)};
const auto instruction{CreateRef<CLI::Instruction>()};
const CLI::Parser parser{instruction, source};

Path configPath{GetConfigPath()};
if (m_ExitStatus == ExitStatus::FAILURE) {
return;
// Litr called without any arguments:
if (instruction->Count() == 0) {
fmt::print("You can run `litr --help` to see what you can do here.\n");
return ExitStatus::FAILURE;
}

m_Config = CreateRef<Config::Loader>(configPath);
}
Hook::Handler hooks{instruction};

ExitStatus Application::Run(int argc, char* argv[]) {
LITR_PROFILE_FUNCTION();
// Hooks before config
hooks.Add(CLI::Instruction::Code::DEFINE, {"version", "v"}, Hook::Version::Print);
if (hooks.Execute()) {
return ExitStatus::SUCCESS;
}

const Path configPath{GetConfigPath()};
if (m_ExitStatus == ExitStatus::FAILURE) {
return m_ExitStatus;
}

m_Source = SourceFromArguments(argc, argv);

const auto instruction{CreateRef<CLI::Instruction>()};
const CLI::Parser parser{instruction, m_Source};
Error::Reporter errorReporter{m_Config};

// Print parser errors if any:
Error::Reporter errorReporter{configPath};
if (Error::Handler::HasErrors()) {
errorReporter.PrintErrors(Error::Handler::GetErrors());
return ExitStatus::FAILURE;
}

// Litr called without any arguments:
if (instruction->Count() == 0) {
fmt::print("You can run `litr --help` to see what you can do here.\n");
return ExitStatus::FAILURE;
}

const auto interpreter{CreateRef<CLI::Interpreter>(instruction, m_Config)};

const Help help{m_Config};
const std::vector<std::string> versionParams{"version", "v"};
const std::vector<std::string> helpParams{"help", "h"};
const auto config{CreateRef<Config::Loader>(configPath)};
const auto interpreter{CreateRef<CLI::Interpreter>(instruction, config)};

interpreter->AddHook(CLI::Instruction::Code::DEFINE, versionParams, Version::Print);
interpreter->AddHook(
CLI::Instruction::Code::DEFINE, helpParams,
[&help](const Ref<CLI::Instruction>& instruction) {
help.Print(instruction);
});
hooks.Add(CLI::Instruction::Code::DEFINE, {"help", "h"}, [&config](const Ref<CLI::Instruction>& instruction) {
const Hook::Help help{config};
help.Print(instruction);
});
if (hooks.Execute()) {
return ExitStatus::SUCCESS;
}

// Run
interpreter->Execute();

// Print interpreter errors if any:
if (Error::Handler::HasErrors()) {
errorReporter.PrintErrors(Error::Handler::GetErrors());
return ExitStatus::FAILURE;
Expand Down
5 changes: 1 addition & 4 deletions src/client/Client/Application.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,14 @@ namespace Litr {

class Application {
public:
Application();
Application() = default;
ExitStatus Run(int argc, char* argv[]);

private:
[[nodiscard]] Path GetConfigPath();
[[nodiscard]] static std::string SourceFromArguments(int argc, char* argv[]);

private:
Ref<Config::Loader> m_Config;

std::string m_Source{};
ExitStatus m_ExitStatus{ExitStatus::SUCCESS};
};

Expand Down
41 changes: 41 additions & 0 deletions src/client/Client/Hooks/Handler.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#include "Handler.hpp"

namespace Litr::Hook {

Handler::Handler(const Ref<CLI::Instruction>& instruction) : m_Instruction(instruction) {}

void Handler::Add(Code code, const std::vector<Value>& values,
const Handler::HookCallback& callback) {
LITR_PROFILE_FUNCTION();

for (auto&& value : values) {
m_Hooks.emplace_back(code, value, callback);
}
}

bool Handler::Execute() const {
LITR_PROFILE_FUNCTION();

size_t offset{0};

while (offset < m_Instruction->Count()) {
const auto code{static_cast<Code>(m_Instruction->Read(offset++))};

for (auto&& hook : m_Hooks) {
if (code != hook.Code) continue;
auto value{m_Instruction->ReadConstant(m_Instruction->Read(offset))};
if (value == hook.Value) {
hook.Callback(m_Instruction);
return true;
}
}

if (code != Code::CLEAR) {
offset++;
}
}

return false;
}

} // namespace Litr::Hook
31 changes: 31 additions & 0 deletions src/client/Client/Hooks/Handler.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#pragma once

#include "Core.hpp"

namespace Litr::Hook {

class Handler {
using HookCallback = std::function<void(const Ref<CLI::Instruction>& instruction)>;
using Code = CLI::Instruction::Code;
using Value = CLI::Instruction::Value;

struct Hook {
CLI::Instruction::Code Code;
CLI::Instruction::Value Value;
HookCallback Callback;

Hook(CLI::Instruction::Code code, CLI::Instruction::Value value, HookCallback callback)
: Code(code), Value(std::move(value)), Callback(std::move(callback)) {}
};

public:
explicit Handler(const Ref<CLI::Instruction>& instruction);
void Add(Code code, const std::vector<Value>& values, const HookCallback& callback);
[[nodiscard]] bool Execute() const;

private:
const Ref<CLI::Instruction>& m_Instruction;
std::vector<Hook> m_Hooks{};
};

} // namespace Litr::Hook
4 changes: 2 additions & 2 deletions src/client/Client/Hooks/Help.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
#include <fmt/printf.h>
#include <fmt/format.h>

namespace Litr {
namespace Litr::Hook {

// Attention dear reader: I know how this file looks like. There is a lot going on,
// numbers, formatting, some of it all over the place. It is not pretty, but it works
Expand Down Expand Up @@ -333,4 +333,4 @@ bool Help::SortParameterByRequired(const Ref<Config::Parameter>& p1, const Ref<C
return RankParameterRequired(p1) < RankParameterRequired(p2);
}

} // namespace Litr
} // namespace Litr::Hook
4 changes: 2 additions & 2 deletions src/client/Client/Hooks/Help.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

#include "Core.hpp"

namespace Litr {
namespace Litr::Hook {

class Help {
public:
Expand Down Expand Up @@ -38,4 +38,4 @@ class Help {
const std::string m_ArgumentPlaceholder{"=<option>"};
};

} // namespace Litr
} // namespace Litr::Hook
4 changes: 2 additions & 2 deletions src/client/Client/Hooks/Version.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@

#include "Core.hpp"

namespace Litr {
namespace Litr::Hook {

void Version::Print([[maybe_unused]] const Ref<CLI::Instruction>& instruction) {
fmt::print("{}.{}.{}\n", LITR_VERSION_MAJOR, LITR_VERSION_MINOR, LITR_VERSION_PATCH);
}

} // namespace Litr
} // namespace Litr::Hook
4 changes: 2 additions & 2 deletions src/client/Client/Hooks/Version.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@

#include "Core.hpp"

namespace Litr {
namespace Litr::Hook {

class Version {
public:
static void Print(const Ref<CLI::Instruction>& instruction);
};

} // namespace Litr
} // namespace Litr::Hook
41 changes: 0 additions & 41 deletions src/core/Core/CLI/Interpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,28 +26,12 @@ void Interpreter::Execute() {

m_Offset = 0;

if (HookExecuted()) return;

while (m_Offset < m_Instruction->Count()) {
if (m_StopExecution) return;
ExecuteInstruction();
}
}

void Interpreter::AddHook(Instruction::Code code, const Instruction::Value& value, const Interpreter::HookCallback& callback) {
LITR_PROFILE_FUNCTION();

m_Hooks.emplace_back(code, value, callback);
}

void Interpreter::AddHook(Instruction::Code code, const std::vector<Instruction::Value>& values, const Interpreter::HookCallback& callback) {
LITR_PROFILE_FUNCTION();

for (auto&& value : values) {
AddHook(code, value, callback);
}
}

Instruction::Value Interpreter::ReadCurrentValue() const {
LITR_PROFILE_FUNCTION();

Expand Down Expand Up @@ -342,31 +326,6 @@ enum Variable::Type Interpreter::GetVariableType(const Ref<Config::Parameter>& p
return {};
}

bool Interpreter::HookExecuted() const {
LITR_PROFILE_FUNCTION();

size_t offset{0};

while (offset < m_Instruction->Count()) {
const auto code{static_cast<Instruction::Code>(m_Instruction->Read(offset++))};

for (auto&& hook : m_Hooks) {
if (code != hook.Code) continue;
auto value{m_Instruction->ReadConstant(m_Instruction->Read(offset))};
if (value == hook.Value) {
hook.Callback(m_Instruction);
return true;
}
}

if (code != Instruction::Code::CLEAR) {
offset++;
}
}

return false;
}

void Interpreter::ValidateRequiredParameters(const Ref<Config::Command>& command) {
LITR_PROFILE_FUNCTION();

Expand Down
15 changes: 0 additions & 15 deletions src/core/Core/CLI/Interpreter.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,23 +17,11 @@ namespace Litr::CLI {
class Interpreter {
using Variables = std::unordered_map<std::string, CLI::Variable>;
using Scripts = std::vector<std::string>;
using HookCallback = std::function<void(const Ref<Instruction>& instruction)>;

struct Hook {
Instruction::Code Code;
Instruction::Value Value;
HookCallback Callback;

Hook(Instruction::Code code, Instruction::Value value, HookCallback callback)
: Code(code), Value(std::move(value)), Callback(std::move(callback)) {}
};

public:
Interpreter(const Ref<Instruction>& instruction, const Ref<Config::Loader>& config);

void Execute();
void AddHook(Instruction::Code code, const Instruction::Value& value, const HookCallback& callback);
void AddHook(Instruction::Code code, const std::vector<Instruction::Value>& values, const HookCallback& callback);

private:
[[nodiscard]] Instruction::Value ReadCurrentValue() const;
Expand All @@ -56,7 +44,6 @@ class Interpreter {
[[nodiscard]] std::string ParseScript(const std::string& script, const Config::Location& location);

[[nodiscard]] static enum Variable::Type GetVariableType(const Ref<Config::Parameter>& param);
[[nodiscard]] bool HookExecuted() const;

void ValidateRequiredParameters(const Ref<Config::Command>& command);
[[nodiscard]] bool IsVariableDefined(const std::string& name) const;
Expand All @@ -72,8 +59,6 @@ class Interpreter {
std::string m_CurrentVariableName{};
bool m_StopExecution{false};

std::vector<Hook> m_Hooks{};

// Initialize with empty scope
std::vector<Variables> m_Scope{Variables()};
};
Expand Down
2 changes: 1 addition & 1 deletion src/core/Core/Error/Reporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

namespace Litr::Error {

Reporter::Reporter(const Ref<Config::Loader>& config) : m_FilePath(config->GetFilePath()) {}
Reporter::Reporter(const Path& path) : m_FilePath(path) {}

void Reporter::PrintErrors(const std::vector<BaseError>& errors) {
LITR_PROFILE_FUNCTION();
Expand Down
3 changes: 1 addition & 2 deletions src/core/Core/Error/Reporter.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,13 @@
#include <vector>

#include "Core/Error/BaseError.hpp"
#include "Core/Config/Loader.hpp"
#include "Core/FileSystem.hpp"

namespace Litr::Error {

class Reporter {
public:
explicit Reporter(const Ref<Config::Loader>& config);
explicit Reporter(const Path& path);

void PrintErrors(const std::vector<BaseError>& errors);
void PrintError(const BaseError& error);
Expand Down

0 comments on commit 6fefda5

Please sign in to comment.