diff --git a/builder.cpp b/builder.cpp new file mode 100644 index 0000000..d039360 --- /dev/null +++ b/builder.cpp @@ -0,0 +1,130 @@ +#include "builder.hpp" +#include "dbc.hpp" +#include "watcher.hpp" +#include "game_engine.hpp" +#include // for milliseconds +#include +#include +#include +#include +#include +#include +#include +#include +#include // for allocator, shared_ptr +#include +#include +#include // for EXIT_SUCCESS +#include // for operator+, to_string +#include // for sleep_for +#include +#include + +using namespace std; +using namespace fmt; + +#define BUF_MAX 1024 + +void Builder::run_build(GameEngine &game, const char* command) { + regex err_re("(.*?):([0-9]+):([0-9]+):\\s*(.*?):\\s*(.*)\n*"); + + char buffer[BUF_MAX]; // BUF_MAX is a define already? + ofstream stats_out; + stats_out.open("stats.csv", ios::out | ios::app); + std::time_t tstamp = std::time(nullptr); + + dbc::check(stats_out.good(), "Error opening stats.csv file."); + dbc::pre("simple test", [&]() { return stats_out.good(); }); + + // need to catch the error message when the command is bad + FILE *build_out = popen(command, "r"); + dbc::check(build_out != nullptr, "Failed to run command."); + + int hit_count = 0; + + while(fgets(buffer, BUF_MAX, build_out) != nullptr) { + string line(buffer); // yeah, that's probably a problem + + smatch err; + bool match = regex_match(line, err, err_re); + + if(match) { + string file_name = err[1].str(); + string lnumber = err[2].str(); + string col = err[3].str(); + string type = err[4].str(); + string message = err[5].str(); + + string result = format("{:%FT%T},{},{},{},{},{}\n", + fmt::localtime(tstamp), file_name, + lnumber, col, type, message); + + stats_out << result; + gui.output(format("\nHIT WITH {} @ {}:{}:{} {}", type, file_name, lnumber, col, message)); + + game.hit(type); + ++hit_count; + + // refactor this + if(game.is_dead()) { + gui.output(format("YOU DIED!\n")); + } + } + } + + if(hit_count == 0) { + game.heal(10); + } + + int rc = pclose(build_out); + if(rc == 0) { + gui.output("BUILD FINISHED!"); + } else { + gui.output(format("!!! BUILD FAILED. Your command correct? '{}'", command)); + } + + stats_out.close(); + dbc::post("a post test", [&]() { return !stats_out.is_open(); }); +} + +void Builder::run(const char *git_path, const char *build_cmd) { + git_repository* repo = nullptr; + + try { + gui.output(format("Using build command: {}", build_cmd)); + efsw::FileWatcher* fileWatcher = new efsw::FileWatcher(); + dbc::check(fileWatcher != nullptr, "Failed to create filewatcher."); + + git_libgit2_init(); + + int err = git_repository_open(&repo, git_path); + dbc::check(err == 0, git_error_last()->message); + + UpdateListener* listener = new UpdateListener(repo); + dbc::check(listener != nullptr, "Failed to create listener."); + + gui.output(format("Watching directory {} for changes...\n", git_path)); + efsw::WatchID wid = fileWatcher->addWatch(git_path, listener, true); + + int rc = gui.main_loop(game, [&] { + fileWatcher->watch(); + + if(listener->changes) { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + gui.output(format("CHANGES! Running build {}", build_cmd)); + run_build(game, build_cmd); + listener->reset_state(); + } + return 0; + }); + + dbc::check(rc == 0, "Invalid return from main_loop."); + + fileWatcher->removeWatch(wid); + git_libgit2_shutdown(); + } catch(dbc::Error &err) { + if(repo != nullptr) git_repository_free(repo); + git_libgit2_shutdown(); + throw err; + } +} diff --git a/builder.hpp b/builder.hpp new file mode 100644 index 0000000..4c170ff --- /dev/null +++ b/builder.hpp @@ -0,0 +1,17 @@ +#pragma once +#include "gui.hpp" +#include "game_engine.hpp" + +class Builder { + GUI gui; + GameEngine game; + + public: + + Builder(GUI &g, GameEngine &engine) : gui(g), game(engine) {}; + + void run_build(GameEngine &game, const char* command); + + void run(const char *git_path, const char *build_cmd); + +}; diff --git a/escape_turings_tarpit.cpp b/escape_turings_tarpit.cpp index f064669..f79b79b 100644 --- a/escape_turings_tarpit.cpp +++ b/escape_turings_tarpit.cpp @@ -1,176 +1,21 @@ -#include "dbc.hpp" -#include "gui.hpp" -#include "game_engine.hpp" -#include // for milliseconds -#include -#include -#include -#include +#include "builder.hpp" #include -#include -#include -#include // for Event -#include // for ftxui -#include // for text, separator, Element, operator|, vbox, border -#include -#include -#include // for allocator, shared_ptr -#include -#include -#include // for EXIT_SUCCESS -#include // for operator+, to_string -#include // for sleep_for -#include -#include - -#include "ftxui/component/component.hpp" // for CatchEvent, Renderer, operator|= -#include "ftxui/component/loop.hpp" // for Loop -#include "ftxui/component/screen_interactive.hpp" // for ScreenInteractive - -using namespace ftxui; -using namespace std; - -using namespace std; -using namespace fmt; -namespace fs = std::filesystem; - -const auto ERROR = fmt::emphasis::bold | fg(fmt::color::red); - -#define BUF_MAX 1024 - -class UpdateListener : public efsw::FileWatchListener { - public: - bool changes = false; - git_repository* repo = nullptr; - - UpdateListener(git_repository *r) : repo(r) {}; - - void handleFileAction(efsw::WatchID watchid, - const std::string& dir, - const std::string& filename, - efsw::Action action, - std::string oldFilename) override - { - - // this is some gnarly BS here, probably tons - // of memory leaks for now but it's working - int ignored = 1; - auto the_path = fs::path(dir) / fs::path(filename); - string full_path = the_path.lexically_normal().string(); - - std::replace(full_path.begin(), - full_path.end(), '\\', '/'); - - int rc = git_ignore_path_is_ignored(&ignored, repo, full_path.c_str()); - - dbc::check(rc == 0, "git ignored failed."); - - if(!ignored) { - output(format("\nCHANGE: filename={} .gitignored?={}", full_path.c_str(), ignored)); - - changes = changes || !ignored; - } - } - - void reset_state() { - changes = false; - } -}; - -void run_build(GameEngine &game, const char* command) { - regex err_re("(.*?):([0-9]+):([0-9]+):\\s*(.*?):\\s*(.*)\n*"); - - char buffer[BUF_MAX]; // BUF_MAX is a define already? - ofstream stats_out; - stats_out.open("stats.csv", ios::out | ios::app); - std::time_t tstamp = std::time(nullptr); - - dbc::check(stats_out.good(), "Error opening stats.csv file."); - dbc::pre("simple test", [&]() { return stats_out.good(); }); - - // need to catch the error message when the command is bad - FILE *build_out = popen(command, "r"); - dbc::check(build_out != nullptr, "Failed to run command."); - - while(fgets(buffer, BUF_MAX, build_out) != nullptr) { - string line(buffer); // yeah, that's probably a problem - - smatch err; - bool match = regex_match(line, err, err_re); - - if(match) { - string file_name = err[1].str(); - string lnumber = err[2].str(); - string col = err[3].str(); - string type = err[4].str(); - string message = err[5].str(); - - string result = format("{:%FT%T},{},{},{},{},{}\n", - fmt::localtime(tstamp), file_name, - lnumber, col, type, message); - - stats_out << result; - output(format("\nHIT WITH {} @ {}:{}:{} {}", type, file_name, lnumber, col, message)); - - game.hit(type); - - // refactor this - if(game.is_dead()) { - output(format("YOU DIED!\n")); - } - } - } - - stats_out.close(); - dbc::post("a post test", [&]() { return !stats_out.is_open(); }); -} - int main(int argc, char *argv[]) { - git_repository* repo = nullptr; - - try { - dbc::check(argc == 3, "USAGE: watchgit PATH BUILD_CMD"); - const char *git_path = argv[1]; - const char *build_cmd = argv[2]; - - output(format("Using build command: {}", build_cmd)); - efsw::FileWatcher* fileWatcher = new efsw::FileWatcher(); - dbc::check(fileWatcher != nullptr, "Failed to create filewatcher."); - - git_libgit2_init(); - - int err = git_repository_open(&repo, git_path); - dbc::check(err == 0, git_error_last()->message); + if(argc != 3) { + fmt::println("USAGE: watchgit PATH BUILD_CMD"); + return 1; + } else { + const char *git_path = argv[1]; + const char *build_cmd = argv[2]; - UpdateListener* listener = new UpdateListener(repo); - dbc::check(listener != nullptr, "Failed to create listener."); + GUI gui; + GameEngine game{100}; - output(format("Watching directory {} for changes...\n", git_path)); - efsw::WatchID wid = fileWatcher->addWatch(git_path, listener, true); + auto builder = Builder(gui, game); + builder.run(git_path, build_cmd); - GameEngine game{100}; - - int rc = main_loop(game, [&] { - fileWatcher->watch(); - - if(listener->changes) { - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - output(format("CHANGES! Running build {}", build_cmd)); - run_build(game, build_cmd); - listener->reset_state(); - } - return 0; - }); - - dbc::check(rc == 0, "Invalid return from main_loop."); - - git_libgit2_shutdown(); - } catch(dbc::Error &err) { - output(format("ERROR: {}\n", err.message)); - if(repo != nullptr) git_repository_free(repo); - git_libgit2_shutdown(); - return 1; - } + return 1; + } } diff --git a/game_engine.cpp b/game_engine.cpp index 607e488..6e2fe5a 100644 --- a/game_engine.cpp +++ b/game_engine.cpp @@ -144,6 +144,10 @@ bool GameEngine::hit(string &type) { return is_dead(); } +void GameEngine::heal(int amount) { + hit_points += amount; +} + bool GameEngine::is_dead() { return hit_points <= 0; } diff --git a/game_engine.hpp b/game_engine.hpp index 1311c0f..a8d5dd2 100644 --- a/game_engine.hpp +++ b/game_engine.hpp @@ -44,4 +44,6 @@ class GameEngine { bool hit(string &type); bool is_dead(); + + void heal(int amount); }; diff --git a/gui.cpp b/gui.cpp index 7faa860..8f0aa2f 100644 --- a/gui.cpp +++ b/gui.cpp @@ -19,25 +19,11 @@ using namespace std; using namespace fmt; namespace fs = std::filesystem; -vector lines; - -void output(const string &msg) { +void GUI::output(const string &msg) { lines.push_back(msg); } -ButtonOption Style() { - auto option = ButtonOption::Animated(); - option.transform = [](const EntryState& s) { - auto element = paragraph(s.label); - if (s.focused) { - element |= bold; - } - return element | center | borderEmpty | flex; - }; - return option; -} - -int main_loop(GameEngine &game, std::function runner) { +int GUI::main_loop(GameEngine &game, std::function runner) { auto screen = ScreenInteractive::Fullscreen(); screen.TrackMouse(true); diff --git a/gui.hpp b/gui.hpp index e29d1b0..9f68db2 100644 --- a/gui.hpp +++ b/gui.hpp @@ -5,6 +5,12 @@ #include #include -void output(const string &msg); +class GUI { + vector lines; -int main_loop(GameEngine &game, std::function runner); + public: + + void output(const string &msg); + + int main_loop(GameEngine &game, std::function runner); +}; diff --git a/meson.build b/meson.build index da24eeb..c184eeb 100644 --- a/meson.build +++ b/meson.build @@ -30,6 +30,8 @@ dependencies = [ executable('escape_turings_tarpit', ['game_engine.cpp', 'gui.cpp', + 'watcher.cpp', + 'builder.cpp', 'escape_turings_tarpit.cpp'], dependencies: dependencies) diff --git a/watcher.cpp b/watcher.cpp new file mode 100644 index 0000000..9a86055 --- /dev/null +++ b/watcher.cpp @@ -0,0 +1,31 @@ +#include "watcher.hpp" +#include +using namespace std; +namespace fs = std::filesystem; + +void UpdateListener::handleFileAction(efsw::WatchID watchid, + const std::string& dir, + const std::string& filename, + efsw::Action action, + std::string oldFilename) +{ + + // this is some gnarly BS here, probably tons + // of memory leaks for now but it's working + int ignored = 1; + auto the_path = fs::path(dir) / fs::path(filename); + string full_path = the_path.lexically_normal().string(); + + std::replace(full_path.begin(), + full_path.end(), '\\', '/'); + + int rc = git_ignore_path_is_ignored(&ignored, repo, full_path.c_str()); + + if(!ignored) { + changes = changes || !ignored; + } +} + +void UpdateListener::reset_state() { + changes = false; +} diff --git a/watcher.hpp b/watcher.hpp new file mode 100644 index 0000000..91b8c6e --- /dev/null +++ b/watcher.hpp @@ -0,0 +1,20 @@ +#pragma once +#include +#include +#include // for operator+, to_string + +class UpdateListener : public efsw::FileWatchListener { + public: + bool changes = false; + git_repository* repo = nullptr; + + UpdateListener(git_repository *r) : repo(r) {}; + + void handleFileAction(efsw::WatchID watchid, + const std::string& dir, + const std::string& filename, + efsw::Action action, + std::string oldFilename) override; + + void reset_state(); +};